[tarantool-patches] Re: [PATCH 2/7] sql: remove SQLite original struct Index

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Wed Aug 29 03:58:30 MSK 2018


Thanks for the patch! See 16 comments below and a
commit on the branch.

On 23/08/2018 19:55, Nikita Pettik wrote:
> As a part of SQL DD integration it is required to substitute SQLite
> structure representing index with one from Tarantool internals.
> To make this happen, lets add surrogate space to Table, which will
> hold array of indexes. Those indexes are not real copies from Tarantool
> core, but contain only index_def, since only def is useful during query
> compilation.
> 
> Note that in new implementation indexes are held as array and added to
> that array in straight order. In SQLite indexes are arranged in list and
> added to the head. Hence, the order of indexes is reversed. It results
> in different query plans: if planner must make choice of two equal
> indexes, it chooses simply first one. Due to this change, some tests are
> fixed.
> 
> Part of #3561
> ---
>   src/box/sql.c                            |  56 +--
>   src/box/sql/analyze.c                    |  69 ++--
>   src/box/sql/build.c                      | 689 ++++++++++---------------------
>   src/box/sql/delete.c                     |  14 +-
>   src/box/sql/expr.c                       | 120 +-----
>   src/box/sql/insert.c                     |  92 ++---
>   src/box/sql/parse.y                      |   8 -
>   src/box/sql/pragma.c                     | 188 ++++-----
>   src/box/sql/prepare.c                    |   7 +-
>   src/box/sql/select.c                     |  17 +-
>   src/box/sql/sqliteInt.h                  |  79 ++--
>   src/box/sql/tarantoolInt.h               |  34 +-
>   src/box/sql/update.c                     |   3 +-
>   src/box/sql/vdbe.c                       |  12 +-
>   src/box/sql/vdbe.h                       |   6 +-
>   src/box/sql/vdbeaux.c                    |   6 +-
>   src/box/sql/vdbemem.c                    |  28 +-
>   src/box/sql/where.c                      | 381 ++++++++---------
>   src/box/sql/whereInt.h                   |   8 +-
>   src/box/sql/wherecode.c                  | 167 +++-----
>   test/sql-tap/analyze3.test.lua           |   2 +-
>   test/sql-tap/analyze7.test.lua           |   8 +-
>   test/sql-tap/analyze9.test.lua           |  10 +-
>   test/sql-tap/analyzeF.test.lua           |   2 +-
>   test/sql-tap/eqp.test.lua                |  14 +-
>   test/sql-tap/gh-2996-indexed-by.test.lua |  10 +-
>   test/sql-tap/lua-tables.test.lua         |   8 +-
>   27 files changed, 807 insertions(+), 1231 deletions(-)
> 
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 74f5ae827..abed9d31b 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -819,23 +817,27 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>   	pParse->nTab = MAX(pParse->nTab, iTab);
>   	sqlite3OpenTable(pParse, iTabCur, pTab, OP_OpenRead);
>   	sqlite3VdbeLoadString(v, regTabname, pTab->def->name);
> -
> -	for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
> +	/*
> +	 * Here we need real space from Tarantool DD since
> +	 * further it is passed to cursor opening routine.
> +	 */
> +	struct space *space = space_by_id(pTab->def->id);
> +	assert(space != NULL);
> +	for (uint32_t j = 0; j < space->index_count; ++j) {
> +		struct index *idx = pTab->space->index[j];
>   		int addrRewind;	/* Address of "OP_Rewind iIdxCur" */
>   		int addrNextRow;	/* Address of "next_row:" */
>   		const char *idx_name;	/* Name of the index */
>   
> -		if (pOnlyIdx && pOnlyIdx != pIdx)
> -			continue;
>   		/* Primary indexes feature automatically generated
>   		 * names. Thus, for the sake of clarity, use
>   		 * instead more familiar table name.
>   		 */
> -		if (sql_index_is_primary(pIdx))
> +		if (idx->def->iid == 0)
>   			idx_name = pTab->def->name;
>   		else
> -			idx_name = pIdx->def->name;
> -		int part_count = pIdx->def->key_def->part_count;
> +			idx_name = idx->def->name;
> +		int part_count = idx->def->key_def->part_count;

1. Below you have check 'if (part_count > 0)', but it is
always true, it is not?

>   
>   		/* Populate the register containing the index name. */
>   		sqlite3VdbeLoadString(v, regIdxname, idx_name);
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 47fa7c305..79dc46592 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -151,55 +151,46 @@ sqlite3LocateIndex(sqlite3 * db, const char *zName, const char *zTable)
>   
>   	if (pTab == NULL)
>   		return NULL;
> -	for (struct Index *idx = pTab->pIndex; idx != NULL; idx = idx->pNext) {
> +	for (uint32_t i = 0; i < pTab->space->index_count; ++i) {
> +		struct index *idx = pTab->space->index[i];
>   		if (strcmp(zName, idx->def->name) == 0)
>   			return idx;
>   	}
>   	return NULL;
>   }
>   
> -/*
> - * Reclaim the memory used by an index
> - */
> -static void
> -freeIndex(sqlite3 * db, Index * p)
> -{
> -	if (p->def != NULL)
> -		index_def_delete(p->def);
> -	sqlite3DbFree(db, p);
> -}
> -
> -/*
> - * For the index called zIdxName which is found in the database,
> - * unlike that index from its Table then remove the index from
> - * the index hash table and free all memory structures associated
> - * with the index.
> - */
>   void
> -sqlite3UnlinkAndDeleteIndex(sqlite3 * db, Index * pIndex)
> +sql_space_index_delete(struct space *space, uint32_t iid)
>   {
> -	assert(pIndex != 0);
> -
> -	struct session *user_session = current_session();
> -	if (ALWAYS(pIndex)) {
> -		if (pIndex->pTable->pIndex == pIndex) {
> -			pIndex->pTable->pIndex = pIndex->pNext;
> -		} else {
> -			Index *p;
> -			/* Justification of ALWAYS();  The index must be on the list of
> -			 * indices.
> -			 */
> -			p = pIndex->pTable->pIndex;
> -			while (ALWAYS(p) && p->pNext != pIndex) {
> -				p = p->pNext;
> -			}
> -			if (ALWAYS(p && p->pNext == pIndex)) {
> -				p->pNext = pIndex->pNext;
> +	assert(space != NULL);
> +	for (uint32_t i = 0; i < space->index_count; ++i) {
> +		struct index *idx = space->index[i];

2. You have mentioned that space indexes are stored as an
array, but it is not the only true. The indexes are stored in
two ways: as an array and a map. A map allows to find an index
by id in O(1) time. Look at space_index function.

Here you do not clear space->index_map and do not
reallocate it. Looks like you do not touch index_map
at all in this patch. But maybe you should. See my
other comments here and in next patches.

> +		/*
> +		 * Allocate new chunk with size reduced by 1 slot.
> +		 * Copy all indexes to that chunk except for one
> +		 * to be deleted.
> +		 */
> +		if (idx->def->iid == iid) {
> +			free(idx->def);

3. idx->def->key_def leaks. cmp_def as well. Please,
use index_def_delete. Same in other places where you
delete index_def (whereLoopClearUnion, etc).

> +			free(idx);
> +			size_t idx_sz = sizeof(struct index *);
> +			uint32_t idx_count = --space->index_count;

4. If malloc below fails, index_count will remain decremented.

> +			struct index **new_idexes =
> +				(struct index **) malloc(idx_sz * idx_count);

5. Typo: 'idexes'.

> +			if (new_idexes == NULL) {
> +				diag_set(OutOfMemory, idx_sz * idx_count,
> +					 "malloc", "new_indexes");
> +				return;
>   			}
> +			memcpy(new_idexes, space->index, i * idx_sz);
> +			memcpy(new_idexes + i, space->index + i + 1,
> +			       idx_sz * (idx_count - i));
> +			free(space->index);
> +			space->index = new_idexes;
> +			break;
>   		}
> -		freeIndex(db, pIndex);
>   	}
> -
> +	struct session *user_session = current_session();
>   	user_session->sql_flags |= SQLITE_InternChanges;
>   }
>   
> @@ -259,38 +250,39 @@ table_column_is_in_pk(Table *table, uint32_t column)
>   	return false;
>   }
>   
> -/*
> +/**
>    * Remove the memory data structures associated with the given
> - * Table.  No changes are made to disk by this routine.
> + * Table.
>    *
> - * This routine just deletes the data structure.  It does not unlink
> - * the table data structure from the hash table.  But it does destroy
> - * memory structures of the indices and foreign keys associated with
> - * the table.
> - *
> - * The db parameter is optional.  It is needed if the Table object
> - * contains lookaside memory.  (Table objects in the schema do not use
> - * lookaside memory, but some ephemeral Table objects do.)  Or the
> - * db parameter can be used with db->pnBytesFreed to measure the memory
> - * used by the Table object.
> + * @param db Database handler.
> + * @param tab Table to be deleted.
>    */
> -static void SQLITE_NOINLINE
> -deleteTable(sqlite3 * db, Table * pTable)
> +static void
> +table_delete(struct sqlite3 *db, struct Table *tab)
>   {
> -	Index *pIndex, *pNext;
> -
> +	if (tab->space->def != NULL)
> +		goto skip_index_delete;
>   	/* Delete all indices associated with this table. */
> -	for (pIndex = pTable->pIndex; pIndex; pIndex = pNext) {
> -		pNext = pIndex->pNext;
> -		freeIndex(db, pIndex);
> -	}
> -	assert(pTable->def != NULL);
> -	/* Do not delete pTable->def allocated on region. */
> -	if (!pTable->def->opts.is_temporary)
> -		space_def_delete(pTable->def);
> +	for (uint32_t i = 0; i < tab->space->index_count; ++i) {
> +		/*
> +		 * These indexes are just wrapper for
> +		 * index_def's, so it makes no sense to call
> +		 * index_delete().
> +		 */
> +		struct index *idx = tab->space->index[i];
> +		free(idx->def);
> +		free(idx);
> +	}
> +	free(tab->space);
> +	free(tab->space->index);

6. Use after free (tab->space is freed here).

> +skip_index_delete:
> +	assert(tab->def != NULL);
> +	/* Do not delete table->def allocated on region. */
> +	if (!tab->def->opts.is_temporary)
> +		space_def_delete(tab->def);
>   	else
> -		sql_expr_list_delete(db, pTable->def->opts.checks);
> -	sqlite3DbFree(db, pTable);
> +		sql_expr_list_delete(db, tab->def->opts.checks);
> +	sqlite3DbFree(db, tab);
>   }
>   
>   void
> @@ -368,16 +360,12 @@ sqlite3CheckIdentifierName(Parse *pParse, char *zName)
>   	return SQLITE_OK;
>   }
>   
> -/*
> - * Return the PRIMARY KEY index of a table
> - */
> -Index *
> -sqlite3PrimaryKeyIndex(Table * pTab)
> +struct index *
> +sql_table_primary_key(const struct Table *tab)
>   {
> -	Index *p;
> -	for (p = pTab->pIndex; p != NULL && !sql_index_is_primary(p);
> -	     p = p->pNext);
> -	return p;
> +	if (tab->space->index_count == 0 || tab->space->index[0]->def->iid != 0)
> +		return NULL;
> +	return tab->space->index[0];

7. If you had index_map, you could do it like this:

     if (tab->space-index_count == 0)
         return NULL;
     return tab->space->index_map[0];

No checking for index[0]->iid == 0, it looks very
strange. Likewise a primary index could have id != 0.

You can allocate index_map in the same memory block
as index array.

>   }
>   
>   /**
> @@ -2431,84 +2408,6 @@ sql_drop_foreign_key(struct Parse *parse_context, struct SrcList *table,
>   		vdbe_emit_fkey_drop(parse_context, constraint_name, child_id);
>   }
>   
> -/*
> - * Generate code that will erase and refill index *pIdx.  This is
> - * used to initialize a newly created index or to recompute the
> - * content of an index in response to a REINDEX command.
> - */
> -static void
> -sqlite3RefillIndex(Parse * pParse, Index * pIndex)

8. Reindex still has a plenty of mentions over the code,
including the parser and commented tests. What are you going
to do with that? I propose to remove it from all the places.

> @@ -2558,16 +2457,24 @@ getNewIid(Parse * pParse, int iSpaceId, int iCursor)
> +table_add_index(struct Table *tab, struct index *index)
> +{
> +	uint32_t idx_count = tab->space->index_count;
> +	size_t indexes_sz = sizeof(struct index *) * (idx_count + 1);
> +	struct index **idx = (struct index **) realloc(tab->space->index,
> +						       indexes_sz);
> +	if (idx == NULL) {
> +		diag_set(OutOfMemory, indexes_sz, "malloc", "idx");
> +		return;
> +	}
> +	tab->space->index = idx;
> +	/* Make sure that PK always comes as first member. */
> +	if (index->def->iid == 0 && idx_count != 0) {
> +		struct index *tmp = tab->space->index[0];
> +		tab->space->index[0] = index;
> +		index = tmp;

9. Do not care about index array. You should only
care about index_map - here the pk should be on the
first place. So this whole 'if' can be replaced with
a single tab->space->index_map[iid] = index.

>   	}
> +	tab->space->index[tab->space->index_count++] = index;
>   }
>   
>   /**
> @@ -2751,10 +2652,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
>   	 *    auto-index name will be generated.
>   	 */
>   	if (token != NULL) {
> +		assert(token->z != NULL);
>   		name = sqlite3NameFromToken(db, token);
>   		if (name == NULL)
>   			goto exit_create_index;
> -		assert(token->z != NULL);

10. Noise diff hunk.

>   		if (sqlite3LocateIndex(db, name, table->def->name) != NULL) {
>   			if (!if_not_exist) {
>   				sqlite3ErrorMsg(parse,
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 7780bf749..9220d34e1 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c> @@ -1353,12 +1352,6 @@ xferOptimization(Parse * pParse,	/* Parser context */
>   		sqlite3VdbeJumpHere(v, addr1);
>   	}
>   
> -	for (pDestIdx = pDest->pIndex; pDestIdx; pDestIdx = pDestIdx->pNext) {
> -		for (pSrcIdx = pSrc->pIndex; ALWAYS(pSrcIdx);
> -		     pSrcIdx = pSrcIdx->pNext) {
> -			if (xferCompatibleIndex(pDestIdx, pSrcIdx))
> -				break;
> -		}

11. Why are you sure that pDestIdx and pSrcIdx are xfercompatible
now? And the indentation is broken.

As I see, pDestIdx and pSrcIdx above are checked for xfer in a
cycle, but it is not stopped once they are found.

>   		assert(pSrcIdx);
>   		struct space *src_space =
>   			space_by_id(pSrc->def->id);
> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> index a4a1c456f..d8a2c1a79 100644
> --- a/src/box/sql/where.c
> +++ b/src/box/sql/where.c
> @@ -1741,12 +1740,11 @@ whereLoopInit(WhereLoop * p)
>    * Clear the WhereLoop.u union.  Leave WhereLoop.pLTerm intact.
>    */
>   static void
> -whereLoopClearUnion(sqlite3 * db, WhereLoop * p)
> +whereLoopClearUnion(WhereLoop * p)
>   {
> -	if ((p->wsFlags & WHERE_AUTO_INDEX) != 0 &&
> -	    (p->wsFlags & WHERE_AUTO_INDEX) != 0 && p->pIndex != 0) {
> -		sqlite3DbFree(db, p->pIndex);
> -		p->pIndex = 0;
> +	if ((p->wsFlags & WHERE_AUTO_INDEX) != 0) {

12. I put here assert(p->index_def->key_def == NULL)
and it did not fire. Then I tried assert(false) and the
result was the same. So this code is not tested at all.
Can you fix it?

> +		free(p->index_def);
> +		p->index_def = NULL;
>   	}
> @@ -2999,13 +2975,15 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
>   		/* If there was an INDEXED BY clause, then only that one index is
>   		 * considered.
>   		 */
> -		if (pSrc->pIBIndex)
> +		if (pSrc->pIBIndex != NULL)
> +			break;
> +		if (fake_index != NULL)
>   			break;

13. This 'break' makes no sense. You already
limited loop iterations with 1 for fake_index != NULL.
Either break, or limit 'for'.

> +
>   	}
> -	if (fake_index.def != NULL)
> -	{
> -		free(fake_index.def->opts.stat->tuple_log_est);
> -		index_def_delete(fake_index.def);
> +	if (fake_index != NULL) {
> +		free(fake_index->opts.stat->tuple_log_est);

14. Just allocate fake_index->opts.stat like it
works in ordinary index_defs and use index_def_delete
only.

> +		index_def_delete(fake_index);
>   	}
>   	return rc;
>   }

15. Why index_is_unordered still exists? Why can you
get key_def->opts.stat->is_unordered right from SQL index_def?

> @@ -4651,10 +4628,10 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
>   			 *    It is something w/ defined space_def
>   			 *    and nothing else. Skip such loops.
>   			 */
> -			if (idx_def == NULL && pIx == NULL)
> +			if (idx_def == NULL)
>   				continue;
> -			bool is_primary = (pIx != NULL && sql_index_is_primary(pIx)) ||
> -					  (idx_def != NULL && (idx_def->iid == 0));
> +			bool is_primary = (idx_def != NULL &&
> +					   idx_def->iid == 0);

16. idx_def == NULL is filtered out one line above. The same about other
'idx_def !=/== NULL' below.

>   			if (is_primary
>   			    && (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
>   				/* This is one term of an OR-optimization using

Please, consider my fixes on the branch and here:

commit cccbcaa444453aba3373ef53febb83389d2a113d
Author: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
Date:   Mon Aug 27 17:07:53 2018 -0300

     Review fixes

diff --git a/src/box/key_def.c b/src/box/key_def.c
index 75eba79ec..e171e88fa 100644
--- a/src/box/key_def.c
+++ b/src/box/key_def.c
@@ -250,6 +250,8 @@ key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
  		if (part1->coll != part2->coll)
  			return (uintptr_t) part1->coll <
  			       (uintptr_t) part2->coll ? -1 : 1;
+		if (part1->sort_order != part2->sort_order)
+			return part1->sort_order < part2->sort_order ? -1 : 1;
  		if (key_part_is_nullable(part1) != key_part_is_nullable(part2))
  			return key_part_is_nullable(part1) <
  			       key_part_is_nullable(part2) ? -1 : 1;
diff --git a/src/box/sql.c b/src/box/sql.c
index 61c766540..f85daf9bd 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -31,10 +31,6 @@
  #include <assert.h>
  #include "field_def.h"
  #include "sql.h"
-/*
- * Both Tarantool and SQLite codebases declare Index, hence the
- * workaround below.
- */
  #include "sql/sqliteInt.h"
  #include "sql/tarantoolInt.h"
  #include "sql/vdbeInt.h"
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index abed9d31b..33a3e1da8 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -918,68 +918,67 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
  		sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
  		addrNextRow = sqlite3VdbeCurrentAddr(v);
  
-		if (part_count > 0) {
-			int endDistinctTest = sqlite3VdbeMakeLabel(v);
-			int *aGotoChng;	/* Array of jump instruction addresses */
-			aGotoChng =
-			    sqlite3DbMallocRawNN(db, sizeof(int) * part_count);
-			if (aGotoChng == 0)
-				continue;
-
+		int endDistinctTest = sqlite3VdbeMakeLabel(v);
+		/* Array of jump instruction addresses. */
+		int *aGotoChng =
+			sqlite3DbMallocRawNN(db, sizeof(int) * part_count);
+		if (aGotoChng == NULL)
+			continue;
+		/*
+		 *  next_row:
+		 *   regChng = 0
+		 *   if( idx(0) != regPrev(0) ) goto chng_addr_0
+		 *   regChng = 1
+		 *   if( idx(1) != regPrev(1) ) goto chng_addr_1
+		 *   ...
+		 *   regChng = N
+		 *   goto endDistinctTest
+		 */
+		sqlite3VdbeAddOp0(v, OP_Goto);
+		addrNextRow = sqlite3VdbeCurrentAddr(v);
+		if (part_count == 1 && idx->def->opts.is_unique) {
  			/*
-			 *  next_row:
-			 *   regChng = 0
-			 *   if( idx(0) != regPrev(0) ) goto chng_addr_0
-			 *   regChng = 1
-			 *   if( idx(1) != regPrev(1) ) goto chng_addr_1
-			 *   ...
-			 *   regChng = N
-			 *   goto endDistinctTest
+			 * For a single-column UNIQUE index, once
+			 * we have found a non-NULL row, we know
+			 * that all the rest will be distinct, so
+			 * skip subsequent distinctness tests.
  			 */
-			sqlite3VdbeAddOp0(v, OP_Goto);
-			addrNextRow = sqlite3VdbeCurrentAddr(v);
-			if (part_count == 1 && idx->def->opts.is_unique) {
-				/* For a single-column UNIQUE index, once we have found a non-NULL
-				 * row, we know that all the rest will be distinct, so skip
-				 * subsequent distinctness tests.
-				 */
-				sqlite3VdbeAddOp2(v, OP_NotNull, regPrev,
-						  endDistinctTest);
-				VdbeCoverage(v);
-			}
-			struct key_part *part = idx->def->key_def->parts;
-			for (i = 0; i < part_count; ++i, ++part) {
-				struct coll *coll = part->coll;
-				sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
-				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
-						  part->fieldno, regTemp);
-				aGotoChng[i] =
-				    sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0,
-						      regPrev + i, (char *)coll,
-						      P4_COLLSEQ);
-				sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
-				VdbeCoverage(v);
-			}
-			sqlite3VdbeAddOp2(v, OP_Integer, part_count, regChng);
-			sqlite3VdbeGoto(v, endDistinctTest);
+			sqlite3VdbeAddOp2(v, OP_NotNull, regPrev,
+					  endDistinctTest);
+			VdbeCoverage(v);
+		}
+		struct key_part *part = idx->def->key_def->parts;
+		for (i = 0; i < part_count; ++i, ++part) {
+			struct coll *coll = part->coll;
+			sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
+			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, part->fieldno,
+					  regTemp);
+			aGotoChng[i] = sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0,
+							 regPrev + i,
+							 (char *)coll,
+							 P4_COLLSEQ);
+			sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+			VdbeCoverage(v);
+		}
+		sqlite3VdbeAddOp2(v, OP_Integer, part_count, regChng);
+		sqlite3VdbeGoto(v, endDistinctTest);
  
-			/*
-			 *  chng_addr_0:
-			 *   regPrev(0) = idx(0)
-			 *  chng_addr_1:
-			 *   regPrev(1) = idx(1)
-			 *  ...
-			 */
-			sqlite3VdbeJumpHere(v, addrNextRow - 1);
-			part = idx->def->key_def->parts;
-			for (i = 0; i < part_count; ++i, ++part) {
-				sqlite3VdbeJumpHere(v, aGotoChng[i]);
-				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
-						  part->fieldno, regPrev + i);
-			}
-			sqlite3VdbeResolveLabel(v, endDistinctTest);
-			sqlite3DbFree(db, aGotoChng);
+		/*
+		 *  chng_addr_0:
+		 *   regPrev(0) = idx(0)
+		 *  chng_addr_1:
+		 *   regPrev(1) = idx(1)
+		 *  ...
+		 */
+		sqlite3VdbeJumpHere(v, addrNextRow - 1);
+		part = idx->def->key_def->parts;
+		for (i = 0; i < part_count; ++i, ++part) {
+			sqlite3VdbeJumpHere(v, aGotoChng[i]);
+			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, part->fieldno,
+					  regPrev + i);
  		}
+		sqlite3VdbeResolveLabel(v, endDistinctTest);
+		sqlite3DbFree(db, aGotoChng);
  
  		/*
  		 *  chng_addr_N:
@@ -989,14 +988,14 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
  		 *   if !eof(csr) goto next_row;
  		 */
  		assert(regKey == (regStat4 + 2));
-		struct index *pPk = sql_table_primary_key(pTab);
-		int pk_part_count = pPk->def->key_def->part_count;
+		struct index *pk = sql_table_primary_key(pTab);
+		int pk_part_count = pk->def->key_def->part_count;
  		/* Allocate memory for array. */
  		pParse->nMem = MAX(pParse->nMem,
  				   regPrev + part_count + pk_part_count);
  		int regKeyStat = regPrev + part_count;
  		for (i = 0; i < pk_part_count; i++) {
-			uint32_t k = pPk->def->key_def->parts[i].fieldno;
+			uint32_t k = pk->def->key_def->parts[i].fieldno;
  			assert(k < pTab->def->field_count);
  			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k,
  					  regKeyStat + i);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 79dc46592..534afc154 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -273,8 +273,8 @@ table_delete(struct sqlite3 *db, struct Table *tab)
  		free(idx->def);
  		free(idx);
  	}
-	free(tab->space);
  	free(tab->space->index);
+	free(tab->space);
  skip_index_delete:
  	assert(tab->def != NULL);
  	/* Do not delete table->def allocated on region. */
@@ -1214,8 +1214,8 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
   * @param idx_name Name of index to be created.
   * @param space_id Space id (or register containing it)
   *                 which index belongs to.
- * @param iIndexId Id of index (or register containing it)
- *                 to be created.
+ * @param iid Id of index (or register containing it) to be
+ *        created.
   * @param sql_stmt String containing 'CREATE INDEX ...' statement.
   *                 NULL for UNIQUE and PK constraints.
   */
@@ -1229,7 +1229,7 @@ vdbe_emit_index_schema_record(struct Parse *parse, const char *idx_name,
  
  	sqlite3VdbeAddOp4(v, OP_String8, 0, entry_reg, 0,
  			  sqlite3DbStrDup(parse->db, idx_name), P4_DYNAMIC);
-	if (parse->pNewTable) {
+	if (parse->pNewTable != NULL) {
  		/*
  		 * A new table is being created, hence space_id
  		 * is a register, but index id is literal.
@@ -1327,11 +1327,12 @@ parseTableSchemaRecord(Parse * pParse, int iSpaceId, char *zStmt)
  	sqlite3VdbeAddOp2(v, OP_Integer, 0, iTop + 2);
  	sqlite3VdbeAddOp4(v, OP_String8, 0, iTop + 3, 0, zStmt, P4_DYNAMIC);
  
-	if (!p->def->opts.is_view)
+	if (!p->def->opts.is_view) {
  		for (uint32_t i = 1; i < p->space->index_count; ++i) {
  			const char *idx_name = p->space->index[i]->def->name;
  			vdbe_emit_index_schema_record(pParse, idx_name,
  						      iSpaceId, i, NULL);
+		}
  	}
  
  	sqlite3VdbeAddParseSchema2Op(v, iTop, pParse->nMem - iTop + 1);
@@ -2464,7 +2465,7 @@ table_add_index(struct Table *tab, struct index *index)
  	struct index **idx = (struct index **) realloc(tab->space->index,
  						       indexes_sz);
  	if (idx == NULL) {
-		diag_set(OutOfMemory, indexes_sz, "malloc", "idx");
+		diag_set(OutOfMemory, indexes_sz, "realloc", "idx");
  		return;
  	}
  	tab->space->index = idx;
@@ -3010,7 +3011,7 @@ sql_drop_index(struct Parse *parse_context, struct SrcList *index_name_list,
  	 * data-dictionaries will be completely merged.
  	 */
  	struct Table *tab = sqlite3HashFind(&db->pSchema->tblHash, table_name);
-	sqlite3VdbeAddOp3(v, OP_DropIndex, index->def->iid, 0, 0);
+	sqlite3VdbeAddOp1(v, OP_DropIndex, index->def->iid);
  	sqlite3VdbeAppendP4(v, tab->space, P4_SPACEPTR);
  
   exit_drop_index:
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 9220d34e1..ce704c0a2 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -1113,22 +1113,9 @@ sql_index_is_xfer_compatible(const struct index_def *dest,
  {
  	assert(dest != NULL && src != NULL);
  	assert(dest->space_id != src->space_id);
-	uint32_t dest_idx_part_count = dest->key_def->part_count;
-	uint32_t src_idx_part_count = src->key_def->part_count;
-	if (dest_idx_part_count != src_idx_part_count)
-		return false;
-	struct key_part *src_part = src->key_def->parts;
-	struct key_part *dest_part = dest->key_def->parts;
-	for (uint32_t i = 0; i < src_idx_part_count;
-	     ++i, ++src_part, ++dest_part) {
-		if (src_part->fieldno != dest_part->fieldno)
-			return false;
-		if (src_part->sort_order != dest_part->sort_order)
-			return false;
-		if (src_part->coll != dest_part->coll)
-			return false;
-	}
-	return true;
+	return key_part_cmp(src->key_def->parts, src->key_def->part_count,
+			    dest->key_def->parts,
+			    dest->key_def->part_count) == 0;
  }
  
  /*
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 19a5e7dd9..2bc6a98f6 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3083,7 +3083,6 @@ struct Walker {
  		SrcList *pSrcList;	/* FROM clause */
  		struct SrcCount *pSrcCount;	/* Counting column references */
  		int *aiCol;	/* array of column indexes */
-		struct IdxCover *pIdxCover;	/* Check for index coverage */
  		/** Space definition. */
  		struct space_def *space_def;
  	} u;
@@ -3682,7 +3681,6 @@ int sqlite3ExprCodeExprList(Parse *, ExprList *, int, int, u8);
  #define SQLITE_ECEL_OMITREF  0x08	/* Omit if ExprList.u.x.iOrderByCol */
  void sqlite3ExprIfTrue(Parse *, Expr *, int, int);
  void sqlite3ExprIfFalse(Parse *, Expr *, int, int);
-void sqlite3ExprIfFalseDup(Parse *, Expr *, int, int);
  #define LOCATE_VIEW    0x01
  #define LOCATE_NOERR   0x02
  Table *sqlite3LocateTable(Parse *, u32 flags, const char *);
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 6ee83a5de..3ba01198d 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -229,10 +229,10 @@ void sqlite3VdbeAppendP4(Vdbe *, void *pP4, int p4type);
   * Set the P4 on the most recently added opcode to the key_def for the
   * index given.
   * @param Parse context, for error reporting.
- * @param idx_def Definition of index to get key_def from.
+ * @param key_def Definition of a key to set.
   */
  void
-sql_vdbe_set_p4_key_def(struct Parse *parse, struct index_def *idx_def);
+sql_vdbe_set_p4_key_def(struct Parse *parse, struct key_def *key_def);
  
  VdbeOp *sqlite3VdbeGetOp(Vdbe *, int);
  int sqlite3VdbeMakeLabel(Vdbe *);
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 12c2edffe..dda030234 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -1055,16 +1055,16 @@ sqlite3VdbeAppendP4(Vdbe * p, void *pP4, int n)
  }
  
  void
-sql_vdbe_set_p4_key_def(struct Parse *parse, struct index_def *idx_def)
+sql_vdbe_set_p4_key_def(struct Parse *parse, struct key_def *key_def)
  {
  	struct Vdbe *v = parse->pVdbe;
  	assert(v != NULL);
-	assert(idx_def != NULL);
-	struct key_def *def = key_def_dup(idx_def->key_def);
-	if (def == NULL)
+	assert(key_def != NULL);
+	key_def = key_def_dup(key_def);
+	if (key_def == NULL)
  		sqlite3OomFault(parse->db);
  	else
-		sqlite3VdbeAppendP4(v, def, P4_KEYDEF);
+		sqlite3VdbeAppendP4(v, key_def, P4_KEYDEF);
  }
  
  #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index d8a2c1a79..1b9ba436c 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -617,19 +617,18 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
  	 *      contain a "col=X" term are subject to a NOT NULL constraint.
  	 */
  	for (uint32_t j = 0; j < pTab->space->index_count; ++j) {
-		struct index *idx = pTab->space->index[j];
-		if (!idx->def->opts.is_unique)
+		struct index_def *def = pTab->space->index[j]->def;
+		if (!def->opts.is_unique)
  			continue;
-		uint32_t col_count = idx->def->key_def->part_count;
+		uint32_t col_count = def->key_def->part_count;
  		uint32_t i;
  		for (i = 0; i < col_count; i++) {
-			if (0 ==
-			    sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask) 0,
-						 WO_EQ, idx->def)) {
-				if (findIndexCol
-				    (pParse, pDistinct, iBase, idx->def, i) < 0)
+			if (sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask) 0,
+						 WO_EQ, def) == 0) {
+				if (findIndexCol(pParse, pDistinct, iBase, def,
+						 i) < 0)
  					break;
-				uint32_t x = idx->def->key_def->parts[i].fieldno;
+				uint32_t x = def->key_def->parts[i].fieldno;
  				if (pTab->def->fields[x].is_nullable)
  					break;
  			}
@@ -638,7 +637,7 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
  		 * This index implies that the DISTINCT
  		 * qualifier is redundant.
  		 */
-		if (i == idx->def->key_def->part_count)
+		if (i == col_count)
  			return 1;
  	}
  
@@ -858,7 +857,7 @@ constructAutomaticIndex(Parse * pParse,			/* The parsing context */
  	assert(pLevel->iIdxCur >= 0);
  	pLevel->iIdxCur = pParse->nTab++;
  	sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol + 1);
-	sql_vdbe_set_p4_key_def(pParse, pIdx);
+	sql_vdbe_set_p4_key_def(pParse, pIdx->key_def);
  	VdbeComment((v, "for %s", pTable->def->name));
  
  	/* Fill the automatic index with content */
@@ -2827,6 +2826,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
  		memset(&fake_index, 0, sizeof(fake_index));
  		struct key_def *key_def = key_def_new(1);
  		if (key_def == NULL) {
+tnt_error:
  			pWInfo->pParse->nErr++;
  			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
  			return SQL_TARANTOOL_ERROR;
@@ -2843,18 +2843,18 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
  					   sizeof("fake_autoindex") - 1,
  					   TREE, &opts, key_def, NULL);
  		key_def_delete(key_def);
-		if (fake_index == NULL) {
-			pWInfo->pParse->nErr++;
-			pWInfo->pParse->rc = SQL_TARANTOOL_ERROR;
-			return SQL_TARANTOOL_ERROR;
-		}
+		if (fake_index == NULL)
+			goto tnt_error;
  		/* Special marker for  non-existent index. */
  		fake_index->iid = UINT32_MAX;
+		int size = sizeof(struct index_stat) + sizeof(log_est_t) * 2;
  
-		struct index_stat *stat =
-			(struct index_stat *) malloc(sizeof(struct index_stat));
-		stat->tuple_log_est =
-			(log_est_t *) malloc(sizeof(log_est_t) * 2);
+		struct index_stat *stat = (struct index_stat *) malloc(size);
+		if (stat == NULL) {
+			diag_set(OutOfMemory, size, "malloc", "stat");
+			goto tnt_error;
+		}
+		stat->tuple_log_est = (log_est_t *) ((char *) (stat + 1));
  		stat->tuple_log_est[0] = sql_space_tuple_log_count(pTab);
  		stat->tuple_log_est[1] = 0;
  		fake_index->opts.stat = stat;
@@ -2917,7 +2917,12 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
  		}
  	}
  #endif				/* SQLITE_OMIT_AUTOMATIC_INDEX */
-	uint32_t idx_count = fake_index == NULL ? pTab->space->index_count : 1;
+	/*
+	 * If there was an INDEXED BY clause, then only that one
+	 * index is considered.
+	 */
+	uint32_t idx_count = fake_index == NULL || pSrc->pIBIndex != NULL ?
+			     pTab->space->index_count : 1;
  	for (uint32_t i = 0; i < idx_count; iSortIdx++, i++) {
  		if (i > 0)
  			probe = pTab->space->index[i]->def;
@@ -2971,20 +2976,9 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
  		sqlite3Stat4ProbeFree(pBuilder->pRec);
  		pBuilder->nRecValid = 0;
  		pBuilder->pRec = 0;
-
-		/* If there was an INDEXED BY clause, then only that one index is
-		 * considered.
-		 */
-		if (pSrc->pIBIndex != NULL)
-			break;
-		if (fake_index != NULL)
-			break;
-
  	}
-	if (fake_index != NULL) {
-		free(fake_index->opts.stat->tuple_log_est);
+	if (fake_index != NULL)
  		index_def_delete(fake_index);
-	}
  	return rc;
  }
  
@@ -3209,7 +3203,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
  	WhereLoop *pLoop = 0;	/* Current WhereLoop being processed. */
  	WhereTerm *pTerm;	/* A single term of the WHERE clause */
  	Expr *pOBExpr;		/* An expression from the ORDER BY clause */
-	struct index_def *pIndex;
+	struct index_def *idx_def;
  	sqlite3 *db = pWInfo->pParse->db;	/* Database connection */
  	Bitmask obSat = 0;	/* Mask of ORDER BY terms satisfied so far */
  	Bitmask obDone;		/* Mask of all ORDER BY terms */
@@ -3313,14 +3307,14 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
  
  		if ((pLoop->wsFlags & WHERE_ONEROW) == 0) {
  			if (pLoop->wsFlags & WHERE_IPK) {
-				pIndex = NULL;
+				idx_def = NULL;
  				nColumn = 1;
-			} else if ((pIndex = pLoop->index_def) == NULL ||
-				   index_is_unordered(pIndex)) {
+			} else if ((idx_def = pLoop->index_def) == NULL ||
+				   index_is_unordered(idx_def)) {
  				return 0;
  			} else {
-				nColumn = pIndex->key_def->part_count;
-				isOrderDistinct = pIndex->opts.is_unique;
+				nColumn = idx_def->key_def->part_count;
+				isOrderDistinct = idx_def->opts.is_unique;
  			}
  
  			/* Loop through all columns of the index and deal with the ones
@@ -3380,8 +3374,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
  				/* Get the column number in the table (iColumn) and sort order
  				 * (revIdx) for the j-th column of the index.
  				 */
-				if (pIndex != NULL) {
-					struct key_def *def = pIndex->key_def;
+				if (idx_def != NULL) {
+					struct key_def *def = idx_def->key_def;
  					iColumn = def->parts[j].fieldno;
  					revIdx = def->parts[j].sort_order;
  				} else {
@@ -3393,9 +3387,9 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
  				 * WhereLoop is not well-ordered
  				 */
  				if (isOrderDistinct && iColumn >= 0 &&
-				    j >= pLoop->nEq && pIndex != NULL) {
+				    j >= pLoop->nEq && idx_def != NULL) {
  					struct space *space =
-						space_by_id(pIndex->space_id);
+						space_by_id(idx_def->space_id);
  					assert(space != NULL);
  					if (space->def->fields[iColumn].is_nullable)
  						isOrderDistinct = 0;
@@ -3432,7 +3426,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
  								      pOrderBy->a[i].pExpr,
  								      &is_found, &id);
  						struct coll *idx_coll =
-							pIndex->key_def->parts[j].coll;
+							idx_def->key_def->parts[j].coll;
  						if (is_found &&
  						    coll != idx_coll)
  							continue;
@@ -4630,9 +4624,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  			 */
  			if (idx_def == NULL)
  				continue;
-			bool is_primary = (idx_def != NULL &&
-					   idx_def->iid == 0);
-			if (is_primary
+			if (idx_def->iid == 0
  			    && (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
  				/* This is one term of an OR-optimization using
  				 * the PRIMARY KEY.  No need for a separate index
@@ -4640,8 +4632,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  				iIndexCur = pLevel->iTabCur;
  				op = 0;
  			} else if (pWInfo->eOnePass != ONEPASS_OFF) {
-				if (idx_def != NULL &&
-				    pTabItem->pTab->space->index_count != 0) {
+				if (pTabItem->pTab->space->index_count != 0) {
  					uint32_t iid = 0;
  					struct index *pJ = pTabItem->pTab->space->index[iid];
  					iIndexCur = iAuxArg;
@@ -4675,19 +4666,10 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  			pLevel->iIdxCur = iIndexCur;
  			assert(iIndexCur >= 0);
  			if (op) {
-				if (idx_def != NULL) {
-					uint32_t space_id =
-						idx_def->space_id;
-					struct space *space =
-						space_by_id(space_id);
-					vdbe_emit_open_cursor(pParse, iIndexCur,
-							      idx_def->iid,
-							      space);
-				} else {
-					vdbe_emit_open_cursor(pParse, iIndexCur,
-							      idx_def->iid,
-							      space);
-				}
+				uint32_t space_id = idx_def->space_id;
+				struct space *space = space_by_id(space_id);
+				vdbe_emit_open_cursor(pParse, iIndexCur,
+						      idx_def->iid, space);
  				if ((pLoop->wsFlags & WHERE_CONSTRAINT) != 0
  				    && (pLoop->
  					wsFlags & (WHERE_COLUMN_RANGE |
@@ -4696,8 +4678,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  					wctrlFlags & WHERE_ORDERBY_MIN) == 0) {
  					sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ);	/* Hint to COMDB2 */
  				}
-				if (idx_def != NULL)
-					VdbeComment((v, "%s", idx_def->name));
+				VdbeComment((v, "%s", idx_def->name));
  #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
  				{
  					u64 colUsed = 0;
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 628c6f9ad..5d8663a92 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -219,7 +219,7 @@ sqlite3WhereExplainOneScan(Parse * pParse,	/* Parse context */
  
  			assert(!(flags & WHERE_AUTO_INDEX)
  			       || (flags & WHERE_IDX_ONLY));
-			if (idx_def != NULL && idx_def->iid == 0) {
+			if (idx_def->iid == 0) {
  				if (isSearch) {
  					zFmt = "PRIMARY KEY";
  				}
@@ -234,10 +234,7 @@ sqlite3WhereExplainOneScan(Parse * pParse,	/* Parse context */
  			}
  			if (zFmt) {
  				sqlite3StrAccumAppend(&str, " USING ", 7);
-				if (idx_def != NULL)
-					sqlite3XPrintf(&str, zFmt, idx_def->name);
-				else
-					sqlite3XPrintf(&str, zFmt, "EPHEMERAL INDEX");
+				sqlite3XPrintf(&str, zFmt, idx_def->name);
  				explainIndexRange(&str, pLoop);
  			}
  		} else if ((flags & WHERE_IPK) != 0
@@ -477,7 +474,7 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
  		int nEq = 0;
  		int *aiMap = 0;
  
-		if (pLoop->index_def != 0 &&
+		if (pLoop->index_def != NULL &&
  		    pLoop->index_def->key_def->parts[iEq].sort_order) {
  			testcase(iEq == 0);
  			testcase(bRev);
@@ -1187,7 +1184,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  			int limit = pRangeStart == NULL ? nEq : nEq + 1;
  			for (int i = 0; i < limit; i++) {
  				if (idx_def->key_def->parts[i].fieldno ==
-				     idx_pk->key_def->parts[0].fieldno) {
+				    idx_pk->key_def->parts[0].fieldno) {
  					/* Here: we know for sure that table has INTEGER
  					   PRIMARY KEY, single column, and Index we're
  					   trying to use for scan contains this column. */
@@ -1295,11 +1292,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  		if (omitTable) {
  			/* pIdx is a covering index.  No need to access the main table. */
  		}  else if (iCur != iIdxCur) {
-			struct index *pPk = space->index_map[0];
-			int pk_part_count = pPk->def->key_def->part_count;
  			int iKeyReg = sqlite3GetTempRange(pParse, pk_part_count);
-			for (j = 0; j < pk_part_count; j++) {
-				k = pPk->def->key_def->parts[j].fieldno;
+			for (j = 0; j < (int) pk_part_count; j++) {
+				k = idx_pk->key_def->parts[j].fieldno;
  				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k,
  						  iKeyReg + j);
  			}
@@ -1344,7 +1339,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  		 */
  		WhereClause *pOrWc;	/* The OR-clause broken out into subterms */
  		SrcList *pOrTab;	/* Shortened table list or OR-clause generation */
-		struct index_def *pCov = 0;	/* Potential covering index (or NULL) */
+		struct index_def *cov = NULL;	/* Potential covering index (or NULL) */
  		int iCovCur = pParse->nTab++;	/* Cursor used for index scans (if any) */
  
  		int regReturn = ++pParse->nMem;	/* Register used with OP_Gosub */
@@ -1357,6 +1352,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  		u16 wctrlFlags;	/* Flags for sub-WHERE clause */
  		Expr *pAndExpr = 0;	/* An ".. AND (...)" expression */
  		Table *pTab = pTabItem->pTab;
+		struct key_def *pk_key_def =
+			sql_table_primary_key(pTab)->def->key_def;
+		uint32_t pk_part_count = pk_key_def->part_count;
  
  		pTerm = pLoop->aLTerm[0];
  		assert(pTerm != 0);
@@ -1403,12 +1401,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  		 * called on an uninitialized cursor.
  		 */
  		if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
-			struct index *pPk = sql_table_primary_key(pTab);
-			int pk_part_count = pPk->def->key_def->part_count;
  			regRowset = pParse->nTab++;
  			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
  					  regRowset, pk_part_count);
-			sql_vdbe_set_p4_key_def(pParse, pPk->def);
+			sql_vdbe_set_p4_key_def(pParse, pk_key_def);
  			regPk = ++pParse->nMem;
  		}
  		iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
@@ -1507,19 +1503,15 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  						int r;
  						int iSet =
  						    ((ii == pOrWc->nTerm - 1) ? -1 : ii);
-						struct index *pPk =
-							sql_table_primary_key(pTab);
-						struct key_def *def =
-							pPk->def->key_def;
  
  						/* Read the PK into an array of temp registers. */
  						r = sqlite3GetTempRange(pParse,
-									def->part_count);
+									pk_part_count);
  						for (uint32_t iPk = 0;
-						     iPk < def->part_count;
+						     iPk < pk_part_count;
  						     iPk++) {
  							uint32_t fieldno =
-								def->parts[iPk].
+								pk_key_def->parts[iPk].
  								fieldno;
  							sqlite3ExprCodeGetColumnToReg
  								(pParse,
@@ -1546,20 +1538,20 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  								(v, OP_Found,
  								 regRowset, 0,
  								 r,
-								 def->part_count);
+								 pk_part_count);
  							VdbeCoverage(v);
  						}
  						if (iSet >= 0) {
  							sqlite3VdbeAddOp3
  								(v, OP_MakeRecord,
-								 r, def->part_count, regPk);
+								 r, pk_part_count, regPk);
  							sqlite3VdbeAddOp2
  								(v, OP_IdxInsert,
  								 regRowset, regPk);
  						}
  
  						/* Release the array of temp registers */
-						sqlite3ReleaseTempRange(pParse, r, def->part_count);
+						sqlite3ReleaseTempRange(pParse, r, pk_part_count);
  					}
  
  					/* Invoke the main loop body as a subroutine */
@@ -1588,21 +1580,21 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  					 * If the call to sqlite3WhereBegin() above resulted in a scan that
  					 * uses an index, and this is either the first OR-connected term
  					 * processed or the index is the same as that used by all previous
-					 * terms, set pCov to the candidate covering index. Otherwise, set
-					 * pCov to NULL to indicate that no candidate covering index will
+					 * terms, set cov to the candidate covering index. Otherwise, set
+					 * cov to NULL to indicate that no candidate covering index will
  					 * be available.
  					 */
  					pSubLoop = pSubWInfo->a[0].pWLoop;
  					assert((pSubLoop->wsFlags & WHERE_AUTO_INDEX) == 0);
  					if ((pSubLoop->wsFlags & WHERE_INDEXED) != 0
-					    && (ii == 0 || (pCov != NULL &&
-						pSubLoop->index_def->iid == pCov->iid))
+					    && (ii == 0 || (cov != NULL &&
+						pSubLoop->index_def->iid == cov->iid))
  					    && (pSubLoop->index_def->iid != 0)) {
  						assert(pSubWInfo->a[0].
  						       iIdxCur == iCovCur);
-						pCov = pSubLoop->index_def;
+						cov = pSubLoop->index_def;
  					} else {
-						pCov = 0;
+						cov = 0;
  					}
  
  					/* Finish the loop through table entries that match term pOrTerm. */
@@ -1610,8 +1602,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
  				}
  			}
  		}
-		pLevel->u.pCovidx = pCov;
-		if (pCov)
+		pLevel->u.pCovidx = cov;
+		if (cov)
  			pLevel->iIdxCur = iCovCur;
  		if (pAndExpr) {
  			pAndExpr->pLeft = 0;







More information about the Tarantool-patches mailing list