[tarantool-patches] Re: [PATCH 5/7] sql: remove lookups in Table hash

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


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

On 23/08/2018 19:55, Nikita Pettik wrote:
> Instead of looking at Table hash lets extract space from Tarantool
> internals and create Table wrapper around it.

The major question - why do we still need struct Table? Just because
of Table.nTabRef? This looks like an insignificant stopper since
anyway we need refs for struct space and multiversion transactional
DDL. We could create surrogate struct spaces for some temporary things
instead of surrogate Tables. And do not create anything for ordinal
spaces.

> 
> Part of #3561
> ---
>   src/box/sql/build.c            |  16 +---
>   src/box/sql/delete.c           |  84 ++++++++------------
>   src/box/sql/expr.c             |   2 +-
>   src/box/sql/fkey.c             |  15 +++-
>   src/box/sql/insert.c           |  48 ++++++-----
>   src/box/sql/pragma.c           | 176 ++++++++++++++++++++++-------------------
>   src/box/sql/select.c           |  41 +---------
>   src/box/sql/sqliteInt.h        |  35 +++++---
>   src/box/sql/trigger.c          |  10 +--
>   src/box/sql/update.c           |  10 +--
>   test/sql-tap/analyze9.test.lua |   2 +-
>   test/sql-tap/join.test.lua     |   6 +-
>   test/sql/delete.result         |   4 +-
>   13 files changed, 204 insertions(+), 245 deletions(-)
> 
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 79dc46592..840604aa3 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -217,22 +217,10 @@ sqlite3CommitInternalChanges()
>   	current_session()->sql_flags &= ~SQLITE_InternChanges;
>   }
>   
> -/*
> - * Return true if given column is part of primary key.
> - * If field number is less than 63, corresponding bit
> - * in column mask is tested. Otherwise, check whether 64-th bit
> - * in mask is set or not. If it is set, then iterate through
> - * key parts of primary index and check field number.
> - * In case it isn't set, there are no key columns among
> - * the rest of fields.
> - */
>   bool
> -table_column_is_in_pk(Table *table, uint32_t column)
> +sql_space_column_is_in_pk(struct space *space, uint32_t column)
>   {
> -	struct space *space = space_by_id(table->def->id);
> -	assert(space != NULL);
> -
> -	struct index *primary_idx = index_find(space, 0 /* PK */);
> +	struct index *primary_idx = space->index[0];

1. space_index(space, 0). Also I see below check for idx == NULL.
But if a space has no indexes, its space->index is NULL as well
AFAIK. How does it work? The previous version was checking if a
space has an index.

>   	/* Views don't have any indexes. */
>   	if (primary_idx == NULL)
>   		return false;
> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 0f285cc8b..a457a71d1 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -36,16 +36,31 @@
>   #include "tarantoolInt.h"
>   
>   struct Table *
> -sql_list_lookup_table(struct Parse *parse, SrcList *src_list)
> +sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
>   {
> -	struct SrcList_item *item = src_list->a;
> -	assert(item != NULL && src_list->nSrc == 1);
> -	struct Table *table = sqlite3LocateTable(parse, 0, item->zName);
> -	sqlite3DeleteTable(parse->db, item->pTab);
> -	item->pTab = table;
> -	if (table != NULL)
> -		item->pTab->nTabRef++;
> -	if (sqlite3IndexedByLookup(parse, item) != 0)
> +	assert(tbl_name != NULL);
> +	uint32_t space_id = box_space_id_by_name(tbl_name->zName,
> +						 strlen(tbl_name->zName));
> +	sqlite3DeleteTable(parse->db, tbl_name->pTab);

2. What is happening here? If tbl_name->pTab != NULL, why do you
recreate it?

> +	struct space *space = space_by_id(space_id);
> +	if (space_id == BOX_ID_NIL || space == NULL) {

3. If 'space_id' == BOX_ID_NIL then space == NULL as well,
always. You should not even try to lookup a space when
box_space_id_by_name returned BOX_ID_NIL.

> +		sqlite3ErrorMsg(parse, "no such table: %s", tbl_name->zName);

4. ER_NO_SUCH_SPACE.

> +		return NULL;
> +	}
> +	assert(space != NULL);
> +	if (space->def->field_count == 0) {
> +		sqlite3ErrorMsg(parse, "no format for space: %s",
> +				tbl_name->zName);

5. ER_UNSUPPORTED or a new error code.

> +		return NULL;
> +	}
> +	struct Table *table = sqlite3DbMallocZero(parse->db, sizeof(*table));
> +	if (table == NULL)
> +		return NULL;
> +	table->def = space_def_dup(space->def);
> +	table->space = space;
> +	table->nTabRef = 1;
> +	tbl_name->pTab = table;
> +	if (sqlite3IndexedByLookup(parse, tbl_name) != 0)
>   		table = NULL;

6. And here table leaks.

>   	return table;
>   }
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index cb4f89e35..7b6300c75 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -278,7 +278,7 @@ comparisonAffinity(Expr * pExpr)
>   		aff =
>   		    sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr,
>   					   aff);
> -	} else if (NEVER(aff == 0)) {
> +	} else {
>   		aff = AFFINITY_BLOB;
>   	}
>   	return aff;

7. Why? How is affinity linked with the patch?

> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 9220d34e1..d3950254e 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -1234,40 +1233,40 @@ xferOptimization(Parse * pParse,	/* Parser context */
>   	 * we have to check the semantics.
>   	 */
>   	pItem = pSelect->pSrc->a;
> -	pSrc = sqlite3LocateTable(pParse, 0, pItem->zName);
> +	uint32_t src_id = box_space_id_by_name(pItem->zName,
> +						strlen(pItem->zName));
>   	/* FROM clause does not contain a real table. */
> -	if (pSrc == NULL)
> +	if (src_id == BOX_ID_NIL)
>   		return 0;
> +	struct space *src = space_by_id(src_id);
> +	assert(src != NULL);

8. In this function you do not need pDest as Table. It is
enough to pass a space here. Only sqlite3OpenTable still
uses struct Table in this function, but anyway in the first
lines it does space lookup. I fixed this remark partially and
dirty. Please, finish my fixes (refactor or remove
sqlite3OpenTable etc).

> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 6173462ff..063836989 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -245,6 +245,95 @@ sql_default_engine_set(const char *engine_name)
>   	return 0;
>   }
>   
> +/**
> + * This function handles PRAGMA TABLE_INFO(<table>).
> + *
> + * Return a single row for each column of the named table.
> + * The columns of the returned data set are:
> + *
> + * - cid: Column id (numbered from left to right, starting at 0);
> + * - name: Column name;
> + * - type: Column declaration type;
> + * - notnull: True if 'NOT NULL' is part of column declaration;
> + * - dflt_value: The default value for the column, if any.
> + *
> + * @param parse Current parsing context.
> + * @param tbl_name Name of table to be examined.
> + */
> +static void
> +sql_pragma_table_info(struct Parse *parse, const char *tbl_name)
> +{
> +	if (tbl_name == NULL)
> +		return;
> +	uint32_t space_id = box_space_id_by_name(tbl_name, strlen(tbl_name));
> +	if (space_id == BOX_ID_NIL)
> +		return;
> +	struct space *space = space_by_id(space_id);
> +	assert(space != NULL);
> +	struct space_def *def = space->def;
> +	struct index *pk = space->index[0];

9. space_index(). What is strange, you use space_index sometimes.

> +	parse->nMem = 6;
> +	if (def->opts.is_view) {
> +		const char *sql = def->opts.sql;
> +		sql_view_assign_cursors(parse, sql);
> +	}
> +	struct Vdbe *v = sqlite3GetVdbe(parse);
> +	for (uint32_t i = 0, k; i < def->field_count; ++i) {
> +		if (!sql_space_column_is_in_pk(space, i)) {
> +			k = 0;
> +		} else if (pk == NULL) {
> +			k = 1;
> +		} else {
> +			struct key_def *kdef = pk->def->key_def;
> +			k = key_def_find(kdef, i) - kdef->parts + 1;
> +		}
> +		bool is_nullable = def->fields[i].is_nullable;
> +		char *expr_str = def->fields[i].default_value;
> +		const char *name = def->fields[i].name;
> +		enum field_type type = def->fields[i].type;
> +		sqlite3VdbeMultiLoad(v, 1, "issisi", i, name,
> +				     field_type_strs[type], !is_nullable,
> +				     expr_str, k);
> +		sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
> +	}
> +}
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 3ae8db1bc..08893c473 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -3333,6 +3333,11 @@ void sqlite3ExprListSetSpan(Parse *, ExprList *, ExprSpan *);
>   u32 sqlite3ExprListFlags(const ExprList *);
>   int sqlite3Init(sqlite3 *);
>   
> +void sqlite3Pragma(Parse *, Token *, Token *, Token *, int);
> +void sqlite3ResetAllSchemasOfConnection(sqlite3 *);
> +void sqlite3CommitInternalChanges();
> +void sqlite3DeleteColumnNames(sqlite3 *, Table *);

10. Why do you need these movements?

> +
>   /**
>    * This is the callback routine for the code that initializes the
>    * database.  See sqlite3Init() below for additional information.
> diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
> index 2b37e3ad5..05ed50192 100755
> --- a/test/sql-tap/analyze9.test.lua
> +++ b/test/sql-tap/analyze9.test.lua
> @@ -1021,7 +1021,7 @@ test:do_execsql_test(
>           INSERT INTO x1 VALUES(3, 4);
>           INSERT INTO x1 VALUES(5, 6);
>           ANALYZE;
> -        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', 0, 0, 0, '');
> +        INSERT INTO "_sql_stat4" VALUES('x1', 'abc', '', '', '', '');

11. Why are tests changed? As I understand, you just did refactoring.

>       ]])
>   
>   test:do_execsql_test(
> diff --git a/test/sql-tap/join.test.lua b/test/sql-tap/join.test.lua
> index 04b7674ed..ac05a98b2 100755
> --- a/test/sql-tap/join.test.lua
> +++ b/test/sql-tap/join.test.lua
> @@ -1080,9 +1080,9 @@ jointest("join-12.9", 1000, {1, 'at most 64 tables in a join'})
>   --    if X(703, "X!cmd", [=[["expr","[lsearch [db eval {PRAGMA compile_options}] MEMDEBUG]<0"]]=])
>   -- then
>   jointest("join-12.10", 65534, {1, 'at most 64 tables in a join'})
> -jointest("join-12.11", 65535, {1, 'too many references to "T14": max 65535'})
> -jointest("join-12.12", 65536, {1, 'too many references to "T14": max 65535'})
> -jointest("join-12.13", 65537, {1, 'too many references to "T14": max 65535'})
> +jointest("join-12.11", 65535, {1, 'at most 64 tables in a join'})
> +jointest("join-12.12", 65536, {1, 'at most 64 tables in a join'})
> +jointest("join-12.13", 65537, {1, 'at most 64 tables in a join'})
>   --    end
>   --end
>   
> diff --git a/test/sql/delete.result b/test/sql/delete.result
> index 52f9969c1..993e9e04d 100644
> --- a/test/sql/delete.result
> +++ b/test/sql/delete.result
> @@ -44,7 +44,7 @@ box.sql.execute("DROP TABLE t1;");
>   --
>   box.sql.execute("DELETE FROM t1;")
>   ---
> -- error: Space 'T1' does not exist
> +- error: 'no such table: T1'
>   ...
>   box.sql.execute("CREATE TABLE t2 (s1 INT PRIMARY KEY);")
>   ---
> @@ -54,7 +54,7 @@ box.sql.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END
>   ...
>   box.sql.execute("INSERT INTO t2 VALUES (0);")
>   ---
> -- error: Space 'T1' does not exist
> +- error: 'no such table: T1'
>   ...
>   box.sql.execute("DROP TABLE t2;")
>   ---
> 

My diff is on the branch and below:

commit df9555fabbed80c080858f5662e0bbd351056cc2
Author: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
Date:   Tue Aug 28 21:21:35 2018 -0300

     Review fixes

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index ff8594fba..6ac3463f5 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -224,17 +224,12 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column)
  	/* Views don't have any indexes. */
  	if (primary_idx == NULL)
  		return false;
-	struct index_def *idx_def = primary_idx->def;
-	uint64_t pk_mask = idx_def->key_def->column_mask;
-	if (column < 63) {
-		return pk_mask & (((uint64_t) 1) << column);
-	} else if ((pk_mask & (((uint64_t) 1) << 63)) != 0) {
-		for (uint32_t i = 0; i < idx_def->key_def->part_count; ++i) {
-			struct key_part *part = &idx_def->key_def->parts[i];
-			if (part->fieldno == column)
-				return true;
-		}
-	}
+	struct key_def *key_def = primary_idx->def->key_def;
+	uint64_t pk_mask = key_def->column_mask;
+	if (column < 63)
+		return (pk_mask & (((uint64_t) 1) << column)) != 0;
+	else if ((pk_mask & (((uint64_t) 1) << 63)) != 0)
+		return key_def_find(key_def, column) != NULL;
  	return false;
  }
  
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 215ad2867..b2d2a19e7 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -620,10 +620,10 @@ fkey_emit_check(struct Parse *parser, struct Table *tab, int reg_old,
  		memset(&fake_tab, 0, sizeof(fake_tab));
  		fake_tab.def = child->def;
  		fake_tab.space = child;
+		/* Prevent from deallocationg fake_tab. */
+		fake_tab.nTabRef = 2;
  		item->pTab = &fake_tab;
  		item->zName = sqlite3DbStrDup(db, child->def->name);
-		/* Prevent from deallocationg fake_tab. */
-		item->pTab->nTabRef = 2;
  		item->iCursor = parser->nTab++;
  
  		if (reg_new != 0) {
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index a2f323f04..49bac58f0 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -46,17 +46,15 @@
  void
  sqlite3OpenTable(Parse * pParse,	/* Generate code into this VDBE */
  		 int iCur,	/* The cursor number of the table */
-		 Table * pTab,	/* The table to be opened */
+		 struct space *space,	/* The table to be opened */
  		 int opcode)	/* OP_OpenRead or OP_OpenWrite */
  {
  	Vdbe *v;
  	v = sqlite3GetVdbe(pParse);
  	assert(opcode == OP_OpenWrite || opcode == OP_OpenRead);
-
-	struct space *space = space_by_id(pTab->def->id);
  	assert(space->index_count > 0);
  	vdbe_emit_open_cursor(pParse, iCur, 0, space);
-	VdbeComment((v, "%s", pTab->def->name));
+	VdbeComment((v, "%s", space->def->name));
  }
  
  char *
@@ -1159,6 +1157,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
  	int destHasUniqueIdx = 0;	/* True if pDest has a UNIQUE index */
  	int regData, regTupleid;	/* Registers holding data and tupleid */
  	struct session *user_session = current_session();
+	struct space *dest = space_by_id(pDest->def->id);
+	assert(dest != NULL);
  
  	if (pSelect == NULL)
  		return 0;	/* Must be of the form  INSERT INTO ... SELECT ... */
@@ -1170,7 +1170,7 @@ xferOptimization(Parse * pParse,	/* Parser context */
  		return 0;
  	}
  	/* The pDest must not have triggers. */
-	if (space_trigger_list(pDest->def->id) != NULL)
+	if (dest->sql_triggers != NULL)
  		return 0;
  	if (onError == ON_CONFLICT_ACTION_DEFAULT) {
  		if (onError == ON_CONFLICT_ACTION_DEFAULT)
@@ -1221,41 +1221,41 @@ xferOptimization(Parse * pParse,	/* Parser context */
  	 */
  	pItem = pSelect->pSrc->a;
  	uint32_t src_id = box_space_id_by_name(pItem->zName,
-						strlen(pItem->zName));
+					       strlen(pItem->zName));
  	/* FROM clause does not contain a real table. */
  	if (src_id == BOX_ID_NIL)
  		return 0;
  	struct space *src = space_by_id(src_id);
  	assert(src != NULL);
  	/* Src and dest may not be the same table. */
-	if (src->def->id == pDest->space->def->id)
+	if (src->def->id == dest->def->id)
  		return 0;
  	/* Src may not be a view. */
  	if (src->def->opts.is_view)
  		return 0;
  	/* Number of columns must be the same in src and dst. */
-	if (pDest->def->field_count != src->def->field_count)
+	if (dest->def->field_count != src->def->field_count)
  		return 0;
-	for (i = 0; i < (int)pDest->def->field_count; i++) {
+	for (i = 0; i < (int)dest->def->field_count; i++) {
  		enum affinity_type dest_affinity =
-			pDest->def->fields[i].affinity;
+			dest->def->fields[i].affinity;
  		enum affinity_type src_affinity =
  			src->def->fields[i].affinity;
  		/* Affinity must be the same on all columns. */
  		if (dest_affinity != src_affinity)
  			return 0;
  		uint32_t id;
-		if (sql_column_collation(pDest->def, i, &id) !=
+		if (sql_column_collation(dest->def, i, &id) !=
  		    sql_column_collation(src->def, i, &id))
  			return 0;
-		if (!pDest->def->fields[i].is_nullable &&
+		if (!dest->def->fields[i].is_nullable &&
  		    src->def->fields[i].is_nullable)
  			return 0;
  		/* Default values for second and subsequent columns need to match. */
  		if (i > 0) {
  			char *src_expr_str = src->def->fields[i].default_value;
  			char *dest_expr_str =
-				pDest->def->fields[i].default_value;
+				dest->def->fields[i].default_value;
  			if ((dest_expr_str == NULL) != (src_expr_str == NULL) ||
  			    (dest_expr_str &&
  			     strcmp(src_expr_str, dest_expr_str) != 0)
@@ -1264,8 +1264,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
  			}
  		}
  	}
-	for (uint32_t i = 0; i < pDest->space->index_count; ++i) {
-		pDestIdx = pDest->space->index[i];
+	for (uint32_t i = 0; i < dest->index_count; ++i) {
+		pDestIdx = dest->index[i];
  		if (pDestIdx->def->opts.is_unique)
  			destHasUniqueIdx = 1;
  		for (uint32_t j = 0; j < src->index_count; ++j) {
@@ -1280,7 +1280,7 @@ xferOptimization(Parse * pParse,	/* Parser context */
  	}
  	/* Get server checks. */
  	ExprList *pCheck_src = space_checks_expr_list(src->def->id);
-	ExprList *pCheck_dest = space_checks_expr_list(pDest->def->id);
+	ExprList *pCheck_dest = space_checks_expr_list(dest->def->id);
  	if (pCheck_dest != NULL &&
  	    sqlite3ExprListCompare(pCheck_src, pCheck_dest, -1) != 0) {
  		/* Tables have different CHECK constraints.  Ticket #2252 */
@@ -1291,8 +1291,6 @@ xferOptimization(Parse * pParse,	/* Parser context */
  	 * So the extra complication to make this rule less restrictive is probably
  	 * not worth the effort.  Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
  	 */
-	struct space *dest = space_by_id(pDest->def->id);
-	assert(dest != NULL);
  	if ((user_session->sql_flags & SQLITE_ForeignKeys) != 0 &&
  	    !rlist_empty(&dest->child_fkey))
  		return 0;
@@ -1312,7 +1310,7 @@ xferOptimization(Parse * pParse,	/* Parser context */
  	iDest = pParse->nTab++;
  	regData = sqlite3GetTempReg(pParse);
  	regTupleid = sqlite3GetTempReg(pParse);
-	sqlite3OpenTable(pParse, iDest, pDest, OP_OpenWrite);
+	sqlite3OpenTable(pParse, iDest, dest, OP_OpenWrite);
  	assert(destHasUniqueIdx);
  	if (destHasUniqueIdx	/* (2) */
  	    || (onError != ON_CONFLICT_ACTION_ABORT
@@ -1342,10 +1340,7 @@ xferOptimization(Parse * pParse,	/* Parser context */
  
  		vdbe_emit_open_cursor(pParse, iSrc, pSrcIdx->def->iid, src);
  		VdbeComment((v, "%s", pSrcIdx->def->name));
-		struct space *dest_space = space_by_id(pDest->def->id);
-		vdbe_emit_open_cursor(pParse, iDest,
-				      pDestIdx->def->iid,
-				      dest_space);
+		vdbe_emit_open_cursor(pParse, iDest, pDestIdx->def->iid, dest);
  		VdbeComment((v, "%s", pDestIdx->def->name));
  		addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
  		VdbeCoverage(v);
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 063836989..3451d7c79 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -270,15 +270,13 @@ sql_pragma_table_info(struct Parse *parse, const char *tbl_name)
  		return;
  	struct space *space = space_by_id(space_id);
  	assert(space != NULL);
-	struct space_def *def = space->def;
  	struct index *pk = space->index[0];
  	parse->nMem = 6;
-	if (def->opts.is_view) {
-		const char *sql = def->opts.sql;
-		sql_view_assign_cursors(parse, sql);
-	}
+	if (space->def->opts.is_view)
+		sql_view_assign_cursors(parse, space->def->opts.sql);
  	struct Vdbe *v = sqlite3GetVdbe(parse);
-	for (uint32_t i = 0, k; i < def->field_count; ++i) {
+	struct field_def *field = space->def->fields;
+	for (uint32_t i = 0, k; i < space->def->field_count; ++i, ++field) {
  		if (!sql_space_column_is_in_pk(space, i)) {
  			k = 0;
  		} else if (pk == NULL) {
@@ -287,13 +285,10 @@ sql_pragma_table_info(struct Parse *parse, const char *tbl_name)
  			struct key_def *kdef = pk->def->key_def;
  			k = key_def_find(kdef, i) - kdef->parts + 1;
  		}
-		bool is_nullable = def->fields[i].is_nullable;
-		char *expr_str = def->fields[i].default_value;
-		const char *name = def->fields[i].name;
-		enum field_type type = def->fields[i].type;
-		sqlite3VdbeMultiLoad(v, 1, "issisi", i, name,
-				     field_type_strs[type], !is_nullable,
-				     expr_str, k);
+		sqlite3VdbeMultiLoad(v, 1, "issisi", i, field->name,
+				     field_type_strs[field->type],
+				     !field->is_nullable, field->default_value,
+				     k);
  		sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
  	}
  }
@@ -316,7 +311,7 @@ sql_pragma_table_stats(struct space *space, void *data)
  	if (pk == NULL)
  		return 0;
  	struct Vdbe *v = sqlite3GetVdbe(parse);
-	LogEst tuple_count_est = sqlite3LogEst(pk->vtab->size(pk));
+	LogEst tuple_count_est = sqlite3LogEst(index_size(pk));
  	size_t avg_tuple_size_pk = sql_index_tuple_size(space, pk);
  	parse->nMem = 4;
  	sqlite3VdbeMultiLoad(v, 1, "ssii", space->def->name, 0,
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 596e38fdc..0c9801e1c 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3585,7 +3585,7 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
  struct Table *
  sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
  
-void sqlite3OpenTable(Parse *, int iCur, Table *, int);
+void sqlite3OpenTable(Parse *, int iCur, struct space *, int);
  /**
   * Generate code for a DELETE FROM statement.
   *
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 1b9ba436c..ce93b0940 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4582,6 +4582,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  		struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
  		Table *pTab = pTabItem->pTab;
  		pLoop = pLevel->pWLoop;
+		struct space *space = space_cache_find(pTabItem->pTab->def->id);
  		if (pTab->def->id == 0 || pTab->def->opts.is_view) {
  			/* Do nothing */
  		} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
@@ -4591,7 +4592,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  				op = OP_OpenWrite;
  				pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
  			};
-			sqlite3OpenTable(pParse, pTabItem->iCursor, pTab, op);
+			sqlite3OpenTable(pParse, pTabItem->iCursor, space, op);
  			assert(pTabItem->iCursor == pLevel->iTabCur);
  			testcase(pWInfo->eOnePass == ONEPASS_OFF
  				 && pTab->nCol == BMS - 1);
@@ -4607,7 +4608,6 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
  		}
  		if (pLoop->wsFlags & WHERE_INDEXED) {
  			struct index_def *idx_def = pLoop->index_def;
-			struct space *space = space_cache_find(pTabItem->pTab->def->id);
  			int iIndexCur;
  			int op = OP_OpenRead;
  			/* Check if index is primary. Either of





More information about the Tarantool-patches mailing list