Completely removes struct Table. Also the
patch simplifies memory management as in
many cases struct space (which replaces
struct Table) is allocated on region
and shouldn't be explicitly freed.

Closes #3235
---
Branch: https://github.com/tarantool/tarantool/tree/sudobobo/gh-3235-repl-Table-w-space
Issue: https://github.com/tarantool/tarantool/issues/3235

 src/box/sql.c              |  64 +++-----
 src/box/sql.h              |  25 +--
 src/box/sql/analyze.c      |   8 +-
 src/box/sql/build.c        | 390 +++++++++++++++++++++------------------------
 src/box/sql/delete.c       |  75 ++++-----
 src/box/sql/expr.c         |  28 ++--
 src/box/sql/fkey.c         |  67 ++++----
 src/box/sql/insert.c       |  97 ++++++-----
 src/box/sql/resolve.c      |  47 +++---
 src/box/sql/select.c       | 297 ++++++++++++++++------------------
 src/box/sql/sqliteInt.h    | 129 +++++++--------
 src/box/sql/tarantoolInt.h |   8 +-
 src/box/sql/tokenize.c     |  10 +-
 src/box/sql/treeview.c     |   4 +-
 src/box/sql/trigger.c      |  36 ++---
 src/box/sql/update.c       |  52 +++---
 src/box/sql/where.c        |  59 ++++---
 src/box/sql/wherecode.c    |   8 +-
 src/box/sql/whereexpr.c    |  13 +-
 19 files changed, 635 insertions(+), 782 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 387da7b3d..978ad8310 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -972,7 +972,7 @@ cursor_advance(BtCursor *pCur, int *pRes)
  */
 
 char *
-sql_encode_table(struct region *region, struct Table *table, uint32_t *size)
+sql_encode_table(struct region *region, struct space *space, uint32_t *size)
 {
 	size_t used = region_used(region);
 	struct mpstream stream;
@@ -980,7 +980,7 @@ sql_encode_table(struct region *region, struct Table *table, uint32_t *size)
 	mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
 		      set_encode_error, &is_error);
 
-	const struct space_def *def = table->def;
+	const struct space_def *def = space->def;
 	assert(def != NULL);
 	uint32_t field_count = def->field_count;
 	mpstream_encode_array(&stream, field_count);
@@ -1033,7 +1033,7 @@ sql_encode_table(struct region *region, struct Table *table, uint32_t *size)
 }
 
 char *
-sql_encode_table_opts(struct region *region, struct Table *table,
+sql_encode_table_opts(struct region *region, struct space *space,
 		      const char *sql, uint32_t *size)
 {
 	size_t used = region_used(region);
@@ -1043,8 +1043,8 @@ sql_encode_table_opts(struct region *region, struct Table *table,
 		      set_encode_error, &is_error);
 	int checks_cnt = 0;
 	struct ExprList_item *a;
-	bool is_view = table->def->opts.is_view;
-	struct ExprList *checks = table->def->opts.checks;
+	bool is_view = space->def->opts.is_view;
+	struct ExprList *checks = space->def->opts.checks;
 	if (checks != NULL) {
 		checks_cnt = checks->nExpr;
 		a = checks->a;
@@ -1258,49 +1258,26 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
 	return def;
 }
 
-Table *
-sql_ephemeral_table_new(Parse *parser, const char *name)
+struct space *
+sql_ephemeral_space_new(Parse *parser, const char *name)
 {
-	sqlite3 *db = parser->db;
-	struct space_def *def = NULL;
-	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
-	if (table != NULL)
-		def = sql_ephemeral_space_def_new(parser, name);
-	if (def == NULL) {
-		sqlite3DbFree(db, table);
-		return NULL;
-	}
-	table->space = (struct space *) region_alloc(&parser->region,
-						     sizeof(struct space));
-	if (table->space == NULL) {
+	struct space *space =
+		(struct space *) region_alloc(&parser->region,
+		                              sizeof(struct space));
+	if (space == NULL) {
 		diag_set(OutOfMemory, sizeof(struct space), "region", "space");
 		parser->rc = SQL_TARANTOOL_ERROR;
 		parser->nErr++;
-		sqlite3DbFree(db, table);
 		return NULL;
 	}
-	memset(table->space, 0, sizeof(struct space));
-	table->def = def;
-	return table;
-}
 
-int
-sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
-{
-	struct space_def *old_def = pTable->def;
-	struct space_def *new_def = NULL;
-	new_def = space_def_new(old_def->id, old_def->uid,
-				old_def->field_count, old_def->name,
-				strlen(old_def->name), old_def->engine_name,
-				strlen(old_def->engine_name), &old_def->opts,
-				old_def->fields, old_def->field_count);
-	if (new_def == NULL) {
-		sqlite3OomFault(db);
-		return -1;
+	memset(space, 0, sizeof(struct space));
+	space->def = sql_ephemeral_space_def_new(parser, name);
+	if (space->def == NULL) {
+		return NULL;
 	}
-	pTable->def = new_def;
-	pTable->def->opts.is_temporary = false;
-	return 0;
+
+	return space;
 }
 
 int
@@ -1357,11 +1334,10 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
 	sql_parser_create(&parser, sql_get());
 	parser.parse_only = true;
 
-	Table dummy_table;
-	memset(&dummy_table, 0, sizeof(dummy_table));
-	dummy_table.def = def;
+	struct space dummy_space;
+	dummy_space.def = def;
 
-	sql_resolve_self_reference(&parser, &dummy_table, NC_IsCheck, NULL,
+	sql_resolve_self_reference(&parser, &dummy_space, NC_IsCheck, NULL,
 				   expr_list);
 	int rc = 0;
 	if (parser.rc != SQLITE_OK) {
diff --git a/src/box/sql.h b/src/box/sql.h
index 028a15245..0d9ecd0ce 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -225,36 +225,25 @@ void
 sql_expr_delete(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
 
 /**
- * Create and initialize a new ephemeral SQL Table object.
+ * Create and initialize a new ephemeral space object.
  * @param parser SQL Parser object.
- * @param name Table to create name.
+ * @param name Space to create name.
  * @retval NULL on memory allocation error, Parser state changed.
  * @retval not NULL on success.
  */
-struct Table *
-sql_ephemeral_table_new(struct Parse *parser, const char *name);
+struct space *
+sql_ephemeral_space_new(struct Parse *parser, const char *name);
 
 /**
  * Create and initialize a new ephemeral space_def object.
  * @param parser SQL Parser object.
- * @param name Table to create name.
+ * @param name Space to create name.
  * @retval NULL on memory allocation error, Parser state changed.
  * @retval not NULL on success.
  */
 struct space_def *
 sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
 
-/**
- * Rebuild struct def in Table with memory allocated on a single
- * malloc.
- * @param db The database connection.
- * @param table The Table with fragmented def to rebuild.
- * @retval 1 on memory allocation error.
- * @retval 0 on success.
- */
-int
-sql_table_def_rebuild(struct sqlite3 *db, struct Table *table);
-
 /**
  * Duplicate Expr list.
  * The flags parameter contains a combination of the EXPRDUP_XXX
@@ -301,13 +290,13 @@ sql_expr_list_append(struct sqlite3 *db, struct ExprList *expr_list,
  * the column number. Any errors cause an error message to be set
  * in parser.
  * @param parser Parsing context.
- * @param table The table being referenced.
+ * @param space The space being referenced.
  * @param type NC_IsCheck or NC_IdxExpr.
  * @param expr Expression to resolve.  May be NULL.
  * @param expr_list Expression list to resolve.  May be NUL.
  */
 void
-sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
+sql_resolve_self_reference(struct Parse *parser, struct space *space, int type,
 			   struct Expr *expr, struct ExprList *expr_list);
 
 /**
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 51c63fa7a..1f800bda7 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -1588,11 +1588,11 @@ const log_est_t default_tuple_est[] = {DEFAULT_TUPLE_LOG_COUNT,
 				       33, 32, 30, 28, 26, 23};
 
 LogEst
-sql_space_tuple_log_count(struct Table *tab)
+sql_space_tuple_log_count(struct space *space)
 {
-	struct space *space = space_by_id(tab->def->id);
-	if (space == NULL)
-		return tab->tuple_log_count;
+	if (space == NULL || space->index_map == NULL)
+		return 0;
+
 	struct index *pk = space_index(space, 0);
 	assert(sqlite3LogEst(DEFAULT_TUPLE_COUNT) == DEFAULT_TUPLE_LOG_COUNT);
 	/* If space represents VIEW, return default number. */
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 49b90b5d0..cae0b3f6e 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -234,64 +234,31 @@ sql_space_column_is_in_pk(struct space *space, uint32_t column)
 
 /**
  * Remove the memory data structures associated with the given
- * Table.
+ * space. The only case when some parts of space must be
+ * deleted explicitly is when it comes from building
+ * routine (i.e. it was born during CREATE TABLE
+ * processing). In this case only index defs and check
+ * expressions are allocated using malloc; the rest - on region.
+ * This case is identified by 'is_temporary' flag == TRUE.
  *
  * @param db Database handler.
- * @param tab Table to be deleted.
+ * @param space Space to be deleted.
  */
-static void
-table_delete(struct sqlite3 *db, struct Table *tab)
-{
-	/*
-	 * There are three possible cases:
-	 * 1. Table comes from building routine (i.e. it
-	 *    was born during CREATE TABLE processing).
-	 *    In this case only index defs and check expressions
-	 *    are allocated using malloc; the rest - on region.
-	 *    'is_temporary' flag is set to TRUE.
-	 * 2. Table comes from query processing (e.g.
-	 *    SELECT, INSERT etc). Hence, table is only
-	 *    wrapper around space and its def from real
-	 *    space cache. As a result we don't need to free
-	 *    anything except for table itself. For such tables
-	 *    flag 'is_temporary' set to FALSE and id != 0.
-	 * 3. Table is 'ephemeral' and represents metadata for
-	 *    flattened subquery or materialized view. It is quite
-	 *    similar to tables from p.1, but their space_defs
-	 *    are rebuilt (see selectExpander() function) using
-	 *    malloc. Such rebuild is required since subquery
-	 *    flattening may occur in trigger's body, which in
-	 *    turn handled in a separate parsing context.
-	 *    At the end of trigger's parsing, those tables may
-	 *    not be deleted, but added to the zombie list of
-	 *    top-level parsing context. Each parsing context
-	 *    features individual region allocator. Hence, when
-	 *    top-level parsing context starts to release zombie
-	 *    tables, they have already corrupted memory layout.
-	 *    Reproducer for this case can be found in
-	 *    tkt-7bbfb7d442 test. For such tables flag
-	 *    'is_temporary' set to false and id == 0.
-	 */
-	if (tab->def->opts.is_temporary) {
-		for (uint32_t i = 0; i < tab->space->index_count; ++i)
-			index_def_delete(tab->space->index[i]->def);
-		/* Do not delete table->def allocated on region. */
-		sql_expr_list_delete(db, tab->def->opts.checks);
-	} else if (tab->def->id == 0) {
-		space_def_delete(tab->def);
-	}
-	sqlite3DbFree(db, tab);
-}
-
 void
-sqlite3DeleteTable(sqlite3 * db, Table * pTable)
+sql_space_delete(struct sqlite3 *db, struct space *space)
 {
-	/* Do not delete the table until the reference count reaches zero. */
-	if (!pTable)
-		return;
-	if (((!db || db->pnBytesFreed == 0) && (--pTable->nTabRef) > 0))
+	if (!space || !db || db->pnBytesFreed == 0)
 		return;
-	table_delete(db, pTable);
+
+	if (space->def->opts.is_temporary) {
+		for (uint32_t i = 0; i < space->index_count; ++i)
+			index_def_delete(space->index[i]->def);
+		/**
+		 * Do not delete space and space->def allocated
+		 * on region.
+		 */
+		sql_expr_list_delete(db, space->def->opts.checks);
+	}
 }
 
 /*
@@ -343,15 +310,15 @@ sqlite3CheckIdentifierName(Parse *pParse, char *zName)
 }
 
 struct index *
-sql_table_primary_key(const struct Table *tab)
+sql_table_primary_key(const struct space *space)
 {
-	if (tab->space->index_count == 0 || tab->space->index[0]->def->iid != 0)
+	if (space->index_count == 0 || space->index[0]->def->iid != 0)
 		return NULL;
-	return tab->space->index[0];
+	return space->index[0];
 }
 
 /**
- * Create and initialize a new SQL Table object.
+ * Create and initialize a new SQL space object.
  * All memory except table object itself is allocated on region.
  * @param parser SQL Parser object.
  * @param name Table to create name.
@@ -359,18 +326,17 @@ sql_table_primary_key(const struct Table *tab)
  *         changed.
  * @retval not NULL on success.
  */
-static Table *
+static struct space *
 sql_table_new(Parse *parser, char *name)
 {
-	struct Table *table = sql_ephemeral_table_new(parser, name);
-	if (table == NULL)
+	struct space *space = sql_ephemeral_space_new(parser, name);
+	if (space == NULL)
 		return NULL;
 
-	strcpy(table->def->engine_name,
+	strcpy(space->def->engine_name,
 	       sql_storage_engine_strs[current_session()->sql_default_engine]);
 
-	table->nTabRef = 1;
-	return table;
+	return space;
 }
 
 /*
@@ -396,7 +362,6 @@ sql_table_new(Parse *parser, char *name)
 void
 sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 {
-	Table *pTable;
 	char *zName = 0;	/* The name of the new table */
 	sqlite3 *db = pParse->db;
 	struct Vdbe *v = sqlite3GetVdbe(pParse);
@@ -423,12 +388,12 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 		goto cleanup;
 	}
 
-	pTable = sql_table_new(pParse, zName);
-	if (pTable == NULL)
+	struct space *new_space = sql_table_new(pParse, zName);
+	if (new_space == NULL)
 		goto cleanup;
 
-	assert(pParse->pNewTable == 0);
-	pParse->pNewTable = pTable;
+	assert(pParse->new_space == 0);
+	pParse->new_space = new_space;
 
 	if (!db->init.busy && (v = sqlite3GetVdbe(pParse)) != 0)
 		sql_set_multi_write(pParse, true);
@@ -443,45 +408,45 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
  * Useful in cases when initial field_count is unknown.
  * Allocated memory should by manually released.
  * @param parser SQL Parser object.
- * @param table SQL Table object.
+ * @param space_def Space definition.
  * @param id column identifier.
  * @retval not NULL on success.
  * @retval NULL on out of memory.
  */
 static struct field_def *
-sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
+sql_field_retrieve(Parse *parser, struct space_def *space_def, uint32_t id)
 {
 	struct field_def *field;
-	assert(table->def != NULL);
+	assert(space_def != NULL);
 	assert(id < SQLITE_MAX_COLUMN);
 
-	if (id >= table->def->exact_field_count) {
-		uint32_t columns_new = table->def->exact_field_count;
+	if (id >= space_def->exact_field_count) {
+		uint32_t columns_new = space_def->exact_field_count;
 		columns_new = (columns_new > 0) ? 2 * columns_new : 1;
 		struct region *region = &parser->region;
 		field = region_alloc(region, columns_new *
-				     sizeof(table->def->fields[0]));
+				     sizeof(space_def->fields[0]));
 		if (field == NULL) {
 			diag_set(OutOfMemory, columns_new *
-				sizeof(table->def->fields[0]),
-				 "region_alloc", "sql_field_retrieve");
+				sizeof(space_def->fields[0]),
+				"region_alloc", "sql_field_retrieve");
 			parser->rc = SQL_TARANTOOL_ERROR;
 			parser->nErr++;
 			return NULL;
 		}
 
-		memcpy(field, table->def->fields,
-		       sizeof(*field) * table->def->exact_field_count);
+		memcpy(field, space_def->fields,
+		       sizeof(*field) * space_def->exact_field_count);
 		for (uint32_t i = columns_new / 2; i < columns_new; i++) {
 			memcpy(&field[i], &field_def_default,
 			       sizeof(struct field_def));
 		}
 
-		table->def->fields = field;
-		table->def->exact_field_count = columns_new;
+		space_def->fields = field;
+		space_def->exact_field_count = columns_new;
 	}
 
-	field = &table->def->fields[id];
+	field = &space_def->fields[id];
 	return field;
 }
 
@@ -513,27 +478,28 @@ void
 sqlite3AddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 {
 	assert(type_def != NULL);
-	Table *p;
 	int i;
 	char *z;
 	sqlite3 *db = pParse->db;
-	if ((p = pParse->pNewTable) == 0)
+	if ((pParse->new_space) == 0)
 		return;
+	struct space *space = pParse->new_space;
+
 #if SQLITE_MAX_COLUMN
-	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
+	if ((int)space->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
 		sqlite3ErrorMsg(pParse, "too many columns on %s",
-				p->def->name);
+				space->def->name);
 		return;
 	}
 #endif
-	/*
+	/**
 	 * As sql_field_retrieve will allocate memory on region
-	 * ensure that p->def is also temporal and would be rebuilded or
+	 * ensure that p->space->def is also temporal and would be rebuilded or
 	 * dropped.
 	 */
-	assert(p->def->opts.is_temporary);
-	if (sql_field_retrieve(pParse, p,
-			       (uint32_t) p->def->field_count) == NULL)
+	assert(space->def->opts.is_temporary);
+	if (sql_field_retrieve(pParse, space->def,
+			      (uint32_t) space->def->field_count) == NULL)
 		return;
 	struct region *region = &pParse->region;
 	z = region_alloc(region, pName->n + 1);
@@ -547,13 +513,14 @@ sqlite3AddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	memcpy(z, pName->z, pName->n);
 	z[pName->n] = 0;
 	sqlite3NormalizeName(z);
-	for (i = 0; i < (int)p->def->field_count; i++) {
-		if (strcmp(z, p->def->fields[i].name) == 0) {
+	for (i = 0; i < (int)space->def->field_count; i++) {
+		if (strcmp(z, space->def->fields[i].name) == 0) {
 			sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
 			return;
 		}
 	}
-	struct field_def *column_def = &p->def->fields[p->def->field_count];
+	struct field_def *column_def =
+		&space->def->fields[space->def->field_count];
 	memcpy(column_def, &field_def_default, sizeof(field_def_default));
 	column_def->name = z;
 	/*
@@ -565,7 +532,7 @@ sqlite3AddColumn(Parse * pParse, Token * pName, struct type_def *type_def)
 	column_def->is_nullable = true;
 	column_def->affinity = type_def->type;
 	column_def->type = sql_affinity_to_field_type(column_def->affinity);
-	p->def->field_count++;
+	space->def->field_count++;
 	pParse->constraintName.n = 0;
 }
 
@@ -573,17 +540,18 @@ void
 sql_column_add_nullable_action(struct Parse *parser,
 			       enum on_conflict_action nullable_action)
 {
-	struct Table *p = parser->pNewTable;
-	if (p == NULL || NEVER(p->def->field_count < 1))
+	if (parser->new_space == NULL ||
+		NEVER(parser->new_space->def->field_count < 1))
 		return;
-	struct field_def *field = &p->def->fields[p->def->field_count - 1];
+	struct space *space = parser->new_space;
+	struct field_def *field = &space->def->fields[space->def->field_count - 1];
 	if (field->nullable_action != ON_CONFLICT_ACTION_DEFAULT &&
 	    nullable_action != field->nullable_action) {
 		/* Prevent defining nullable_action many times. */
 		const char *err_msg =
 			tt_sprintf("NULL declaration for column '%s' of table "
 				   "'%s' has been already set to '%s'",
-				   field->name, p->def->name,
+				   field->name, space->def->name,
 				   on_conflict_action_strs[field->
 							   nullable_action]);
 		diag_set(ClientError, ER_SQL, err_msg);
@@ -608,20 +576,20 @@ sql_column_add_nullable_action(struct Parse *parser,
 void
 sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 {
-	Table *p;
 	sqlite3 *db = pParse->db;
-	p = pParse->pNewTable;
-	assert(p->def->opts.is_temporary);
-	if (p != 0) {
+	assert(pParse->new_space->def->opts.is_temporary);
+	if (pParse->new_space != 0) {
+		struct space_def *space_def = pParse->new_space->def;
 		if (!sqlite3ExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
 			sqlite3ErrorMsg(pParse,
 					"default value of column [%s] is not constant",
-					p->def->fields[p->def->field_count - 1].name);
+					space_def->
+					fields[space_def->field_count - 1].name);
 		} else {
-			assert(p->def != NULL);
+			assert(space_def != NULL);
 			struct field_def *field =
-				&p->def->fields[p->def->field_count - 1];
+				&space_def->fields[space_def->field_count - 1];
 			struct region *region = &pParse->region;
 			uint32_t default_length = (int)(pSpan->zEnd - pSpan->zStart);
 			field->default_value = region_alloc(region,
@@ -678,19 +646,19 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 		     enum sort_order sortOrder
     )
 {
-	Table *pTab = pParse->pNewTable;
 	int iCol = -1, i;
 	int nTerm;
-	if (pTab == 0)
+	if (pParse->new_space == 0)
 		goto primary_key_exit;
-	if (sql_table_primary_key(pTab) != NULL) {
+	struct space *space = pParse->new_space;
+	if (sql_table_primary_key(space) != NULL) {
 		sqlite3ErrorMsg(pParse,
 				"table \"%s\" has more than one primary key",
-				pTab->def->name);
+				space->def->name);
 		goto primary_key_exit;
 	}
 	if (pList == 0) {
-		iCol = pTab->def->field_count - 1;
+		iCol = space->def->field_count - 1;
 		nTerm = 1;
 	} else {
 		nTerm = pList->nExpr;
@@ -704,7 +672,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 				goto primary_key_exit;
 			}
 			const char *name = pCExpr->u.zToken;
-			struct space_def *def = pTab->def;
+			struct space_def *def = space->def;
 			for (uint32_t idx = 0; idx < def->field_count; idx++) {
 				if (strcmp(name, def->fields[idx].name) == 0) {
 					iCol = idx;
@@ -714,14 +682,14 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 		}
 	}
 	if (nTerm == 1 && iCol != -1 &&
-	    pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER &&
-	    sortOrder != SORT_ORDER_DESC) {
+		space->def->fields[iCol].type == FIELD_TYPE_INTEGER &&
+		sortOrder != SORT_ORDER_DESC) {
 		assert(autoInc == 0 || autoInc == 1);
 		pParse->is_new_table_autoinc = autoInc;
 		struct sqlite3 *db = pParse->db;
 		struct ExprList *list;
 		struct Token token;
-		sqlite3TokenInit(&token, pTab->def->fields[iCol].name);
+		sqlite3TokenInit(&token, space->def->fields[iCol].name);
 		list = sql_expr_list_append(db, NULL,
 					    sqlite3ExprAlloc(db, TK_ID,
 							     &token, 0));
@@ -743,13 +711,13 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 			goto primary_key_exit;
 	}
 
-	struct index *pk = sql_table_primary_key(pTab);
+	struct index *pk = sql_table_primary_key(space);
 	assert(pk != NULL);
 	struct key_def *pk_key_def = pk->def->key_def;
 	for (uint32_t i = 0; i < pk_key_def->part_count; i++) {
 		uint32_t idx = pk_key_def->parts[i].fieldno;
-		field_def_create_for_pk(pParse, &pTab->def->fields[idx],
-					pTab->def->name);
+		field_def_create_for_pk(pParse, &space->def->fields[idx],
+					space->def->name);
 	}
 primary_key_exit:
 	sql_expr_list_delete(pParse->db, pList);
@@ -760,22 +728,22 @@ void
 sql_add_check_constraint(struct Parse *parser, struct ExprSpan *span)
 {
 	struct Expr *expr = span->pExpr;
-	struct Table *table = parser->pNewTable;
-	if (table != NULL) {
+	if (parser->new_space != NULL) {
+		struct space *space = parser->new_space;
 		expr->u.zToken =
 			sqlite3DbStrNDup(parser->db, (char *)span->zStart,
 					 (int)(span->zEnd - span->zStart));
 		if (expr->u.zToken == NULL)
 			goto release_expr;
-		table->def->opts.checks =
+		space->def->opts.checks =
 			sql_expr_list_append(parser->db,
-					     table->def->opts.checks, expr);
-		if (table->def->opts.checks == NULL) {
+					     space->def->opts.checks, expr);
+		if (space->def->opts.checks == NULL) {
 			sqlite3DbFree(parser->db, expr->u.zToken);
 			goto release_expr;
 		}
 		if (parser->constraintName.n) {
-			sqlite3ExprListSetName(parser, table->def->opts.checks,
+			sqlite3ExprListSetName(parser, space->def->opts.checks,
 					       &parser->constraintName, 1);
 		}
 	} else {
@@ -791,26 +759,26 @@ release_expr:
 void
 sqlite3AddCollateType(Parse * pParse, Token * pToken)
 {
-	Table *p = pParse->pNewTable;
-	if (p == NULL)
+	if (pParse->new_space == NULL)
 		return;
-	uint32_t i = p->def->field_count - 1;
+	struct space *space= pParse->new_space;
+	uint32_t i = space->def->field_count - 1;
 	sqlite3 *db = pParse->db;
 	char *zColl = sqlite3NameFromToken(db, pToken);
 	if (!zColl)
 		return;
-	uint32_t *coll_id = &p->def->fields[i].coll_id;
+	uint32_t *coll_id = &space->def->fields[i].coll_id;
 	if (sql_get_coll_seq(pParse, zColl, coll_id) != NULL) {
 		/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
 		 * then an index may have been created on this column before the
 		 * collation type was added. Correct this if it is the case.
 		 */
-		for (uint32_t i = 0; i < p->space->index_count; ++i) {
-			struct index *idx = p->space->index[i];
+		for (uint32_t i = 0; i < space->index_count; ++i) {
+			struct index *idx = space->index[i];
 			assert(idx->def->key_def->part_count == 1);
 			if (idx->def->key_def->parts[0].fieldno == i) {
 				coll_id = &idx->def->key_def->parts[0].coll_id;
-				(void)sql_column_collation(p->def, i, coll_id);
+				(void)sql_column_collation(space->def, i, coll_id);
 			}
 		}
 	}
@@ -924,7 +892,7 @@ vdbe_emit_create_index(struct Parse *parse, struct space_def *def,
 	memcpy(raw, index_parts, index_parts_sz);
 	index_parts = raw;
 
-	if (parse->pNewTable != NULL) {
+	if (parse->new_space != NULL) {
 		sqlite3VdbeAddOp2(v, OP_SCopy, space_id_reg, entry_reg);
 		sqlite3VdbeAddOp2(v, OP_Integer, idx_def->iid, entry_reg + 1);
 	} else {
@@ -964,18 +932,18 @@ error:
 static void
 createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 {
-	struct Table *table = pParse->pNewTable;
 	Vdbe *v = sqlite3GetVdbe(pParse);
 	int iFirstCol = ++pParse->nMem;
 	int iRecord = (pParse->nMem += 7);
 	struct region *region = &pParse->region;
 	uint32_t table_opts_stmt_sz = 0;
-	char *table_opts_stmt = sql_encode_table_opts(region, table, zStmt,
+	struct space *space = pParse->new_space;
+	char *table_opts_stmt = sql_encode_table_opts(region, space, zStmt,
 						      &table_opts_stmt_sz);
 	if (table_opts_stmt == NULL)
 		goto error;
 	uint32_t table_stmt_sz = 0;
-	char *table_stmt = sql_encode_table(region, table, &table_stmt_sz);
+	char *table_stmt = sql_encode_table(region, space, &table_stmt_sz);
 	if (table_stmt == NULL)
 		goto error;
 	char *raw = sqlite3DbMallocRaw(pParse->db,
@@ -993,12 +961,12 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 	sqlite3VdbeAddOp2(v, OP_Integer, effective_user()->uid,
 			  iFirstCol + 1 /* owner */ );
 	sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol + 2 /* name */ , 0,
-			  sqlite3DbStrDup(pParse->db, table->def->name),
+			  sqlite3DbStrDup(pParse->db, space->def->name),
 			  P4_DYNAMIC);
 	sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol + 3 /* engine */ , 0,
-			  sqlite3DbStrDup(pParse->db, table->def->engine_name),
+			  sqlite3DbStrDup(pParse->db, space->def->engine_name),
 			  P4_DYNAMIC);
-	sqlite3VdbeAddOp2(v, OP_Integer, table->def->field_count,
+	sqlite3VdbeAddOp2(v, OP_Integer, space->def->field_count,
 			  iFirstCol + 4 /* field_count */ );
 	sqlite3VdbeAddOp4(v, OP_Blob, table_opts_stmt_sz, iFirstCol + 5,
 			  SQL_SUBTYPE_MSGPACK, table_opts_stmt, P4_DYNAMIC);
@@ -1114,14 +1082,14 @@ vdbe_emit_fkey_create(struct Parse *parse_context, const struct fkey_def *fk)
 	 * of <CREATE TABLE ...> statement, we don't have child
 	 * id, but we know register where it will be stored.
 	 */
-	if (parse_context->pNewTable != NULL) {
+	if (parse_context->new_space != NULL) {
 		sqlite3VdbeAddOp2(vdbe, OP_SCopy, fk->child_id,
 				  constr_tuple_reg + 1);
 	} else {
 		sqlite3VdbeAddOp2(vdbe, OP_Integer, fk->child_id,
 				  constr_tuple_reg + 1);
 	}
-	if (parse_context->pNewTable != NULL && fkey_is_self_referenced(fk)) {
+	if (parse_context->new_space != NULL && fkey_is_self_referenced(fk)) {
 		sqlite3VdbeAddOp2(vdbe, OP_SCopy, fk->parent_id,
 				  constr_tuple_reg + 2);
 	} else {
@@ -1184,7 +1152,7 @@ vdbe_emit_fkey_create(struct Parse *parse_context, const struct fkey_def *fk)
 			  constr_tuple_reg + 9);
 	sqlite3VdbeAddOp3(vdbe, OP_SInsert, BOX_FK_CONSTRAINT_ID, 0,
 			  constr_tuple_reg + 9);
-	if (parse_context->pNewTable == NULL)
+	if (parse_context->new_space == NULL)
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_NCHANGE);
 	save_record(parse_context, BOX_FK_CONSTRAINT_ID, constr_tuple_reg, 2,
 		    vdbe->nOp - 1);
@@ -1229,7 +1197,7 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
  * This routine is called to report the final ")" that terminates
  * a CREATE TABLE statement.
  *
- * The table structure that other action routines have been building
+ * The space structure that other action routines have been building
  * is added to the internal hash tables, assuming no errors have
  * occurred.
  *
@@ -1238,12 +1206,12 @@ resolve_link(struct Parse *parse_context, const struct space_def *def,
  *     space and all necessary Tarantool indexes is emitted
  *  2. When db->init.busy == 1. This means that byte code for creation
  *     of new table is executing right now, and it's time to add new entry
- *     for the table into SQL memory represenation
+ *     for the table into SQL memory representation
  *
  * If the pSelect argument is not NULL, it means that this routine
- * was called to create a table generated from a
+ * was called to create a space generated from a
  * "CREATE TABLE ... AS SELECT ..." statement.  The column names of
- * the new table will match the result set of the SELECT.
+ * the new space will match the result set of the SELECT.
  */
 void
 sqlite3EndTable(Parse * pParse,	/* Parse context */
@@ -1251,24 +1219,24 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		Select * pSelect	/* Select from a "CREATE ... AS SELECT" */
     )
 {
-	Table *p;		/* The new table */
 	sqlite3 *db = pParse->db;	/* The database connection */
 
 	if (pEnd == 0 && pSelect == 0) {
 		return;
 	}
 	assert(!db->mallocFailed);
-	p = pParse->pNewTable;
-	if (p == 0)
+	if (pParse->new_space == 0)
 		return;
 
+	struct space *new_space = pParse->new_space;
+
 	assert(!db->init.busy);
 
-	if (!p->def->opts.is_view) {
-		if (sql_table_primary_key(p) == NULL) {
+	if (!new_space->def->opts.is_view) {
+		if (sql_table_primary_key(new_space) == NULL) {
 			sqlite3ErrorMsg(pParse,
 					"PRIMARY KEY missing on table %s",
-					p->def->name);
+					new_space->def->name);
 			goto cleanup;
 		}
 	}
@@ -1278,8 +1246,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	 * Set defaults for columns having no separate
 	 * NULL/NOT NULL specifiers.
 	 */
-	struct field_def *field = p->def->fields;
-	for (uint32_t i = 0; i < p->def->field_count; ++i, ++field) {
+	struct field_def *field = new_space->def->fields;
+	for (uint32_t i = 0; i < new_space->def->field_count; ++i, ++field) {
 		if (field->nullable_action == ON_CONFLICT_ACTION_DEFAULT) {
 			/* Set default nullability NONE. */
 			field->nullable_action = ON_CONFLICT_ACTION_NONE;
@@ -1295,7 +1263,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 
 	/* Text of the CREATE VIEW statement. */
 	char *stmt = NULL;
-	if (p->def->opts.is_view) {
+	if (new_space->def->opts.is_view) {
 		struct Token *pEnd2 = &pParse->sLastToken;
 		int n = pEnd2->z - pParse->sNameToken.z;
 		if (pEnd2->z[0] != ';')
@@ -1306,10 +1274,10 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	int reg_space_id = getNewSpaceId(pParse);
 	createSpace(pParse, reg_space_id, stmt);
 	/* Indexes aren't required for VIEW's.. */
-	if (!p->def->opts.is_view) {
-		for (uint32_t i = 0; i < p->space->index_count; ++i) {
-			struct index *idx = p->space->index[i];
-			vdbe_emit_create_index(pParse, p->def, idx->def,
+	if (!new_space->def->opts.is_view) {
+		for (uint32_t i = 0; i < new_space->index_count; ++i) {
+			struct index *idx = new_space->index[i];
+			vdbe_emit_create_index(pParse, new_space->def, idx->def,
 					       reg_space_id, idx->def->iid);
 		}
 	}
@@ -1325,7 +1293,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		sqlite3VdbeAddOp2(v, OP_NextSequenceId, 0, reg_seq_id);
 		int reg_seq_record =
 			emitNewSysSequenceRecord(pParse, reg_seq_id,
-						 p->def->name);
+						 new_space->def->name);
 		sqlite3VdbeAddOp3(v, OP_SInsert, BOX_SEQUENCE_ID, 0,
 				  reg_seq_record);
 		save_record(pParse, BOX_SEQUENCE_ID, reg_seq_record + 1, 1,
@@ -1346,7 +1314,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		if (fk_parse->selfref_cols != NULL) {
 			struct ExprList *cols = fk_parse->selfref_cols;
 			for (uint32_t i = 0; i < fk->field_count; ++i) {
-				if (resolve_link(pParse, p->def,
+				if (resolve_link(pParse, new_space->def,
 						 cols->a[i].zName,
 						 &fk->links[i].parent_field,
 						 fk->name) != 0)
@@ -1354,7 +1322,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 			}
 			fk->parent_id = reg_space_id;
 		} else if (fk_parse->is_self_referenced) {
-			struct index *pk = sql_table_primary_key(p);
+			struct index *pk = sql_table_primary_key(new_space);
 			if (pk->def->key_def->part_count != fk->field_count) {
 				diag_set(ClientError, ER_CREATE_FK_CONSTRAINT,
 					 fk->name, "number of columns in "\
@@ -1375,8 +1343,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		vdbe_emit_fkey_create(pParse, fk);
 	}
 cleanup:
-	sql_expr_list_delete(db, p->def->opts.checks);
-	p->def->opts.checks = NULL;
+	sql_expr_list_delete(db, new_space->def->opts.checks);
+	new_space->def->opts.checks = NULL;
 }
 
 void
@@ -1385,38 +1353,39 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		struct Select *select, bool if_exists)
 {
 	struct sqlite3 *db = parse_context->db;
-	struct Table *sel_tab = NULL;
 	if (parse_context->nVar > 0) {
 		sqlite3ErrorMsg(parse_context,
 				"parameters are not allowed in views");
 		goto create_view_fail;
 	}
 	sqlite3StartTable(parse_context, name, if_exists);
-	struct Table *p = parse_context->pNewTable;
-	if (p == NULL || parse_context->nErr != 0)
+	if (parse_context->new_space == NULL || parse_context->nErr != 0)
 		goto create_view_fail;
-	sel_tab = sqlite3ResultSetOfSelect(parse_context, select);
-	if (sel_tab == NULL)
+	struct space *space = parse_context->new_space;
+
+	struct space *select_res_space =
+		sqlite3ResultSetOfSelect(parse_context, select);
+	if (select_res_space == NULL)
 		goto create_view_fail;
 	if (aliases != NULL) {
-		if ((int)sel_tab->def->field_count != aliases->nExpr) {
+		if ((int)select_res_space->def->field_count != aliases->nExpr) {
 			sqlite3ErrorMsg(parse_context, "expected %d columns "\
 					"for '%s' but got %d", aliases->nExpr,
-					p->def->name,
-					sel_tab->def->field_count);
+					space->def->name,
+					select_res_space->def->field_count);
 			goto create_view_fail;
 		}
-		sqlite3ColumnsFromExprList(parse_context, aliases, p);
-		sqlite3SelectAddColumnTypeAndCollation(parse_context, p,
+		sqlite3ColumnsFromExprList(parse_context, aliases, space->def);
+		sqlite3SelectAddColumnTypeAndCollation(parse_context, space,
 						       select);
 	} else {
-		assert(sel_tab->def->opts.is_temporary);
-		p->def->fields = sel_tab->def->fields;
-		p->def->field_count = sel_tab->def->field_count;
-		sel_tab->def->fields = NULL;
-		sel_tab->def->field_count = 0;
+		assert(select_res_space->def->opts.is_temporary);
+		space->def->fields = select_res_space->def->fields;
+		space->def->field_count = select_res_space->def->field_count;
+		select_res_space->def->fields = NULL;
+		select_res_space->def->field_count = 0;
 	}
-	p->def->opts.is_view = true;
+	space->def->opts.is_view = true;
 	/*
 	 * Locate the end of the CREATE VIEW statement.
 	 * Make sEnd point to the end.
@@ -1433,8 +1402,8 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 		n--;
 	end.z = &z[n - 1];
 	end.n = 1;
-	p->def->opts.sql = strndup(begin->z, n);
-	if (p->def->opts.sql == NULL) {
+	space->def->opts.sql = strndup(begin->z, n);
+	if (space->def->opts.sql == NULL) {
 		diag_set(OutOfMemory, n, "strndup", "opts.sql");
 		parse_context->rc = SQL_TARANTOOL_ERROR;
 		parse_context->nErr++;
@@ -1445,7 +1414,6 @@ sql_create_view(struct Parse *parse_context, struct Token *begin,
 	sqlite3EndTable(parse_context, &end, 0);
 
  create_view_fail:
-	sqlite3DbFree(db, sel_tab);
 	sql_expr_list_delete(db, aliases);
 	sql_select_delete(db, select);
 	return;
@@ -1820,13 +1788,15 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	char *parent_name = NULL;
 	char *constraint_name = NULL;
 	bool is_self_referenced = false;
-	/*
-	 * Table under construction during CREATE TABLE
+	/**
+	 * Space under construction during CREATE TABLE
 	 * processing. NULL for ALTER TABLE statement handling.
 	 */
-	struct Table *new_tab = parse_context->pNewTable;
-	/* Whether we are processing ALTER TABLE or CREATE TABLE. */
-	bool is_alter = new_tab == NULL;
+	bool is_alter = (parse_context->new_space == NULL);
+	struct space *space;
+	if (!is_alter)
+		space = parse_context->new_space;
+
 	uint32_t child_cols_count;
 	if (child_cols == NULL) {
 		assert(!is_alter);
@@ -1864,7 +1834,7 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 	 * also child) table will definitely exist.
 	 */
 	is_self_referenced = !is_alter &&
-			     strcmp(parent_name, new_tab->def->name) == 0;
+			     strcmp(parent_name, space->def->name) == 0;
 	struct space *parent_space = space_by_name(parent_name);
 	if (parent_space == NULL) {
 		if (is_self_referenced) {
@@ -1889,7 +1859,7 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 			constraint_name =
 				sqlite3MPrintf(db, "FK_CONSTRAINT_%d_%s",
 					       ++parse_context->fkey_count,
-					       new_tab->def->name);
+					       space->def->name);
 		} else {
 			struct Token *cnstr_nm = &parse_context->constraintName;
 			constraint_name = sqlite3NameFromToken(db, cnstr_nm);
@@ -1959,10 +1929,10 @@ sql_create_foreign_key(struct Parse *parse_context, struct SrcList *child,
 				 * immediately.
 				 */
 				fk->links[0].child_field =
-					new_tab->def->field_count - 1;
+					space->def->field_count - 1;
 				break;
 			}
-			if (resolve_link(parse_context, new_tab->def,
+			if (resolve_link(parse_context, space->def,
 					 child_cols->a[i].zName,
 					 &fk->links[i].child_field,
 					 constraint_name) != 0)
@@ -2148,12 +2118,12 @@ index_fill_def(struct Parse *parse, struct index *index,
 			 "region", "key parts");
 		goto tnt_error;
 	}
-	struct Table tmp_tab;
-	tmp_tab.def = space_def;
-	tmp_tab.nTabRef = 2;
+	struct space tmp_space;
+	tmp_space.def = space_def;
+	assert(tmp_space.def != NULL);
 	for (int i = 0; i < expr_list->nExpr; i++) {
 		struct Expr *expr = expr_list->a[i].pExpr;
-		sql_resolve_self_reference(parse, &tmp_tab, NC_IdxExpr,
+		sql_resolve_self_reference(parse, &tmp_space, NC_IdxExpr,
 					   expr, 0);
 		if (parse->nErr > 0)
 			goto cleanup;
@@ -2249,7 +2219,6 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 * Return early if not found.
 	 */
 	struct space *space = NULL;
-	struct space_def *def = NULL;
 	if (tbl_name != NULL) {
 		assert(token != NULL && token->z != NULL);
 		const char *name = tbl_name->a[0].zName;
@@ -2262,17 +2231,16 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			}
 			goto exit_create_index;
 		}
-		def = space->def;
 	} else {
-		if (parse->pNewTable == NULL)
+		if (parse->new_space == NULL)
 			goto exit_create_index;
 		assert(token == NULL);
 		assert(start == NULL);
-		space = parse->pNewTable->space;
-		def = parse->pNewTable->def;
+		space = parse->new_space;
+		space->def = parse->new_space->def;
 	}
 
-	if (def->opts.is_view) {
+	if (space->def->opts.is_view) {
 		sqlite3ErrorMsg(parse, "views can not be indexed");
 		goto exit_create_index;
 	}
@@ -2305,7 +2273,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			if (!if_not_exist) {
 				sqlite3ErrorMsg(parse,
 						"index %s.%s already exists",
-						def->name, name);
+						space->def->name, name);
 			}
 			goto exit_create_index;
 		}
@@ -2337,7 +2305,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		uint32_t idx_count = space->index_count;
 		if (constraint_name == NULL ||
 		    strcmp(constraint_name, "") == 0) {
-			name = sqlite3MPrintf(db, prefix, def->name,
+			name = sqlite3MPrintf(db, prefix, space->def->name,
 					      idx_count + 1);
 		} else {
 			name = sqlite3MPrintf(db, prefix,
@@ -2350,7 +2318,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		goto exit_create_index;
 
 	if (tbl_name != NULL && space_is_system(space)) {
-		diag_set(ClientError, ER_MODIFY_INDEX, name, def->name,
+		diag_set(ClientError, ER_MODIFY_INDEX, name, space->def->name,
 			 "can't create index on system space");
 		parse->nErr++;
 		parse->rc = SQL_TARANTOOL_ERROR;
@@ -2365,8 +2333,8 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 */
 	if (col_list == NULL) {
 		struct Token prev_col;
-		uint32_t last_field = def->field_count - 1;
-		sqlite3TokenInit(&prev_col, def->fields[last_field].name);
+		uint32_t last_field = space->def->field_count - 1;
+		sqlite3TokenInit(&prev_col, space->def->fields[last_field].name);
 		col_list = sql_expr_list_append(parse->db, NULL,
 						sqlite3ExprAlloc(db, TK_ID,
 								 &prev_col, 0));
@@ -2398,7 +2366,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		iid = space->index_id_max + 1;
 	else
 		iid = 0;
-	if (index_fill_def(parse, index, def, iid, name, strlen(name),
+	if (index_fill_def(parse, index, space->def, iid, name, strlen(name),
 			   col_list, idx_type) != 0)
 		goto exit_create_index;
 	/*
@@ -2422,7 +2390,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	}
 	index->def->key_def->part_count = new_part_count;
 
-	if (!index_def_is_valid(index->def, def->name))
+	if (!index_def_is_valid(index->def, space->def->name))
 		goto exit_create_index;
 
 	/*
@@ -2454,7 +2422,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 	 * constraint, but has different onError (behavior on
 	 * constraint violation), then an error is raised.
 	 */
-	if (parse->pNewTable != NULL) {
+	if (parse->new_space != NULL) {
 		for (uint32_t i = 0; i < space->index_count; ++i) {
 			struct index *existing_idx = space->index[i];
 			uint32_t iid = existing_idx->def->iid;
@@ -2523,10 +2491,10 @@ sql_create_index(struct Parse *parse, struct Token *token,
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
 
 		assert(start != NULL);
-		int index_id = getNewIid(parse, def->id, cursor);
+		int index_id = getNewIid(parse, space->def->id, cursor);
 		sqlite3VdbeAddOp1(vdbe, OP_Close, cursor);
-		vdbe_emit_create_index(parse, def, index->def,
-				       def->id, index_id);
+		vdbe_emit_create_index(parse, space->def, index->def,
+				       space->def->id, index_id);
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_NCHANGE);
 		sqlite3VdbeAddOp0(vdbe, OP_Expire);
 	}
@@ -2883,7 +2851,7 @@ sqlite3SrcListDelete(sqlite3 * db, SrcList * pList)
 			sqlite3DbFree(db, pItem->u1.zIndexedBy);
 		if (pItem->fg.isTabFunc)
 			sql_expr_list_delete(db, pItem->u1.pFuncArg);
-		sqlite3DeleteTable(db, pItem->pTab);
+		sql_space_delete(db, pItem->space);
 		sql_select_delete(db, pItem->pSelect);
 		sql_expr_delete(db, pItem->pOn, false);
 		sqlite3IdListDelete(db, pItem->pUsing);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index f9c42fdec..b687ff8f4 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -35,14 +35,14 @@
 #include "sqliteInt.h"
 #include "tarantoolInt.h"
 
-struct Table *
-sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
+struct space *
+sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
 {
-	assert(tbl_name != NULL);
-	assert(tbl_name->pTab == NULL);
-	struct space *space = space_by_name(tbl_name->zName);
+	assert(space_name != NULL);
+	assert(space_name->space == NULL);
+	struct space *space = space_by_name(space_name->zName);
 	if (space == NULL) {
-		sqlite3ErrorMsg(parse, "no such table: %s", tbl_name->zName);
+		sqlite3ErrorMsg(parse, "no such table: %s", space_name->zName);
 		return NULL;
 	}
 	assert(space != NULL);
@@ -53,16 +53,10 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
 		parse->nErr++;
 		return NULL;
 	}
-	struct Table *table = sqlite3DbMallocZero(parse->db, sizeof(*table));
-	if (table == NULL)
-		return NULL;
-	table->def = space->def;
-	table->space = space;
-	table->nTabRef = 1;
-	tbl_name->pTab = table;
-	if (sqlite3IndexedByLookup(parse, tbl_name) != 0)
-		table = NULL;
-	return table;
+	space_name->space = space;
+	if (sqlite3IndexedByLookup(parse, space_name) != 0)
+		space = NULL;
+	return space;
 }
 
 void
@@ -149,21 +143,20 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 	/* True if there are triggers or FKs or subqueries in the
 	 * WHERE clause.
 	 */
-	struct Table *table = sql_lookup_table(parse, tab_list->a);
-	if (table == NULL)
+	struct space *space = sql_lookup_space(parse, tab_list->a);
+	if (space == NULL)
 		goto delete_from_cleanup;
-	assert(table->space != NULL);
-	trigger_list = sql_triggers_exist(table, TK_DELETE, NULL, NULL);
+	assert(space != NULL);
+	trigger_list = sql_triggers_exist(space->def, TK_DELETE, NULL, NULL);
 	bool is_complex = trigger_list != NULL ||
-			  fkey_is_required(table->def->id, NULL);
-	struct space *space = table->space;
+			  fkey_is_required(space->def->id, NULL);
 	bool is_view = space->def->opts.is_view;
 
 	/* If table is really a view, make sure it has been
 	 * initialized.
 	 */
 	if (is_view) {
-		if (sql_view_assign_cursors(parse, table->def->opts.sql) != 0)
+		if (sql_view_assign_cursors(parse, space->def->opts.sql) != 0)
 			goto delete_from_cleanup;
 
 		if (trigger_list == NULL) {
@@ -243,7 +236,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		int eph_cursor = parse->nTab++;
 		int addr_eph_open = sqlite3VdbeCurrentAddr(v);
 		if (is_view) {
-			pk_len = table->def->field_count;
+			pk_len = space->def->field_count;
 			parse->nMem += pk_len;
 			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
 					  pk_len);
@@ -381,7 +374,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		if (one_pass != ONEPASS_OFF) {
 			/* OP_Found will use an unpacked key. */
 			assert(key_len == pk_len);
-			assert(pk_info != NULL || table->def->opts.is_view);
+			assert(pk_info != NULL || space->def->opts.is_view);
 			sqlite3VdbeAddOp4Int(v, OP_NotFound, tab_cursor,
 					     addr_bypass, reg_key, key_len);
 
@@ -403,7 +396,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		    && one_pass != ONEPASS_OFF)
 			idx_noseek = one_pass_cur[1];
 
-		sql_generate_row_delete(parse, table, trigger_list, tab_cursor,
+		sql_generate_row_delete(parse, space, trigger_list, tab_cursor,
 					reg_key, key_len, true,
 					ON_CONFLICT_ACTION_DEFAULT, one_pass,
 					idx_noseek);
@@ -422,7 +415,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 
 	/* Return the number of rows that were deleted. */
 	if ((user_session->sql_flags & SQLITE_CountRows) != 0 &&
-	    parse->pTriggerTab != NULL) {
+	    parse->trigger_space != NULL) {
 		sqlite3VdbeAddOp2(v, OP_ResultRow, reg_count, 1);
 		sqlite3VdbeSetNumCols(v, 1);
 		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted",
@@ -435,7 +428,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 }
 
 void
-sql_generate_row_delete(struct Parse *parse, struct Table *table,
+sql_generate_row_delete(struct Parse *parse, struct space *space,
 			struct sql_trigger *trigger_list, int cursor,
 			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
@@ -464,31 +457,31 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 	/* If there are any triggers to fire, allocate a range of registers to
 	 * use for the old.* references in the triggers.
 	 */
-	if (table != NULL &&
-	    (fkey_is_required(table->def->id, NULL) || trigger_list != NULL)) {
+	if (space != NULL &&
+		(fkey_is_required(space->def->id, NULL) ||
+			trigger_list != NULL)) {
 		/* Mask of OLD.* columns in use */
 		/* TODO: Could use temporary registers here. */
 		uint32_t mask =
 			sql_trigger_colmask(parse, trigger_list, 0, 0,
 					    TRIGGER_BEFORE | TRIGGER_AFTER,
-					    table, onconf);
-		struct space *space = space_by_id(table->def->id);
+					    space, onconf);
 		assert(space != NULL);
 		mask |= space->fkey_mask;
 		first_old_reg = parse->nMem + 1;
-		parse->nMem += (1 + (int)table->def->field_count);
+		parse->nMem += (1 + (int)space->def->field_count);
 
 		/* Populate the OLD.* pseudo-table register array.
 		 * These values will be used by any BEFORE and
 		 * AFTER triggers that exist.
 		 */
 		sqlite3VdbeAddOp2(v, OP_Copy, reg_pk, first_old_reg);
-		for (int i = 0; i < (int)table->def->field_count; i++) {
+		for (int i = 0; i < (int)space->def->field_count; i++) {
 			testcase(mask != 0xffffffff && iCol == 31);
 			testcase(mask != 0xffffffff && iCol == 32);
 			if (mask == 0xffffffff
 			    || (i <= 31 && (mask & MASKBIT32(i)) != 0)) {
-				sqlite3ExprCodeGetColumnOfTable(v, table->def,
+				sqlite3ExprCodeGetColumnOfTable(v, space->def,
 								cursor, i,
 								first_old_reg +
 								i + 1);
@@ -498,7 +491,7 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 		/* Invoke BEFORE DELETE trigger programs. */
 		int addr_start = sqlite3VdbeCurrentAddr(v);
 		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, NULL,
-				      TRIGGER_BEFORE, table, first_old_reg,
+				      TRIGGER_BEFORE, space, first_old_reg,
 				      onconf, label);
 
 		/* If any BEFORE triggers were coded, then seek
@@ -518,7 +511,7 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 		 * constraints attached to other tables) are not
 		 * violated by deleting this row.
 		 */
-		fkey_emit_check(parse, table, first_old_reg, 0, NULL);
+		fkey_emit_check(parse, space, first_old_reg, 0, NULL);
 	}
 
 	/* Delete the index and table entries. Skip this step if
@@ -526,7 +519,7 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 	 * of the DELETE statement is to fire the INSTEAD OF
 	 * triggers).
 	 */
-	if (table == NULL || !table->def->opts.is_view) {
+	if (space == NULL || !space->def->opts.is_view) {
 		uint8_t p5 = 0;
 		sqlite3VdbeAddOp2(v, OP_Delete, cursor,
 				  (need_update_count ? OPFLAG_NCHANGE : 0));
@@ -541,18 +534,18 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 		sqlite3VdbeChangeP5(v, p5);
 	}
 
-	if (table != NULL) {
+	if (space != NULL) {
 		/* Do any ON CASCADE, SET NULL or SET DEFAULT
 		 * operations required to handle rows (possibly
 		 * in other tables) that refer via a foreign
 		 * key to the row just deleted.
 		 */
 
-		fkey_emit_actions(parse, table, first_old_reg, NULL);
+		fkey_emit_actions(parse, space, first_old_reg, NULL);
 
 		/* Invoke AFTER DELETE trigger programs. */
 		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, 0,
-				      TRIGGER_AFTER, table, first_old_reg,
+				      TRIGGER_AFTER, space, first_old_reg,
 				      onconf, label);
 	}
 
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index d83be5101..e2fbcedf8 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -208,8 +208,11 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id)
 		if ((op == TK_AGG_COLUMN || op == TK_COLUMN ||
 		     op == TK_REGISTER || op == TK_TRIGGER) &&
 		    p->space_def != NULL) {
-			/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
-			 * a TK_COLUMN but was previously evaluated and cached in a register
+			/*
+			 * op==TK_REGISTER && p->space_def!=0
+			 * happens when pExpr was originally
+			 * a TK_COLUMN but was previously
+			 * evaluated and cached in a register.
 			 */
 			int j = p->iColumn;
 			if (j >= 0) {
@@ -1573,7 +1576,6 @@ sqlite3SrcListDup(sqlite3 * db, SrcList * p, int flags)
 	for (i = 0; i < p->nSrc; i++) {
 		struct SrcList_item *pNewItem = &pNew->a[i];
 		struct SrcList_item *pOldItem = &p->a[i];
-		Table *pTab;
 		pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
 		pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
 		pNewItem->fg = pOldItem->fg;
@@ -1589,10 +1591,7 @@ sqlite3SrcListDup(sqlite3 * db, SrcList * p, int flags)
 			pNewItem->u1.pFuncArg =
 			    sql_expr_list_dup(db, pOldItem->u1.pFuncArg, flags);
 		}
-		pTab = pNewItem->pTab = pOldItem->pTab;
-		if (pTab) {
-			pTab->nTabRef++;
-		}
+		pNewItem->space = pOldItem->space;
 		pNewItem->pSelect =
 		    sqlite3SelectDup(db, pOldItem->pSelect, flags);
 		pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
@@ -2202,7 +2201,6 @@ isCandidateForInOpt(Expr * pX)
 	Select *p;
 	SrcList *pSrc;
 	ExprList *pEList;
-	Table MAYBE_UNUSED *pTab;
 	int i;
 	if (!ExprHasProperty(pX, EP_xIsSelect))
 		return 0;	/* Not a subquery */
@@ -2230,10 +2228,9 @@ isCandidateForInOpt(Expr * pX)
 		return 0;	/* Single term in FROM clause */
 	if (pSrc->a[0].pSelect)
 		return 0;	/* FROM is not a subquery or view */
-	pTab = pSrc->a[0].pTab;
-	assert(pTab != 0);
+	assert(pSrc->a[0].space != 0);
 	/* FROM clause is not a view */
-	assert(!pTab->def->opts.is_view);
+	assert(!pSrc->a[0].space->def->opts.is_view);
 	pEList = p->pEList;
 	assert(pEList != 0);
 	/* All SELECT results must be columns. */
@@ -2412,19 +2409,18 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 	 */
 	if (pParse->nErr == 0 && (p = isCandidateForInOpt(pX)) != 0) {
 		sqlite3 *db = pParse->db;	/* Database connection */
-		Table *pTab;	/* Table <table>. */
 		ExprList *pEList = p->pEList;
 		int nExpr = pEList->nExpr;
 
 		assert(p->pEList != 0);	/* Because of isCandidateForInOpt(p) */
 		assert(p->pEList->a[0].pExpr != 0);	/* Because of isCandidateForInOpt(p) */
 		assert(p->pSrc != 0);	/* Because of isCandidateForInOpt(p) */
-		pTab = p->pSrc->a[0].pTab;
 		assert(v);	/* sqlite3GetVdbe() has always been previously called */
 
 		int affinity_ok = 1;
 		int i;
 
+		struct space *space = p->pSrc->a[0].space;
 		/* Check that the affinity that will be used to perform each
 		 * comparison is the same as the affinity of each column in table
 		 * on the RHS of the IN operator.  If it not, it is not possible to
@@ -2435,7 +2431,7 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 			int iCol = pEList->a[i].pExpr->iColumn;
 			/* RHS table */
 			enum affinity_type idxaff =
-				sqlite3TableColumnAffinity(pTab->def, iCol);
+				sqlite3TableColumnAffinity(space->def, iCol);
 			enum affinity_type lhs_aff = sqlite3ExprAffinity(pLhs);
 			/*
 			 * Index search is possible only if types
@@ -2450,7 +2446,7 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 			 * Here we need real space since further
 			 * it is used in cursor opening routine.
 			 */
-			struct space *space = space_by_id(pTab->def->id);
+
 			/* Search for an existing index that will work for this IN operator */
 			for (uint32_t k = 0; k < space->index_count &&
 			     eType == 0; ++k) {
@@ -4339,7 +4335,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			break;
 		}
 	case TK_RAISE:
-		if (pParse->pTriggerTab == NULL) {
+		if (pParse->trigger_space == NULL) {
 			sqlite3ErrorMsg(pParse, "RAISE() may only be used "
 					"within a trigger-program");
 			return 0;
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 3033cfcbb..93f98b9db 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -302,20 +302,23 @@ fkey_lookup_parent(struct Parse *parse_context, struct space *parent,
 	sqlite3VdbeAddOp1(v, OP_Close, cursor);
 }
 
-/*
- * Return an Expr object that refers to a memory register corresponding
- * to column iCol of table pTab.
+/**
+ * Return an Expr object that refers to a memory register
+ * corresponding to column iCol of given space.
  *
- * regBase is the first of an array of register that contains the data
- * for pTab.  regBase+1 holds the first column.
+ * regBase is the first of an array of register that contains
+ * the data for given space.  regBase+1 holds the first column.
  * regBase+2 holds the second column, and so forth.
+ *
+ * @param pParse Parsing and code generating context.
+ * @param space  The space whose content is at r[regBase]...
+ * @param regBase Contents of table pTab.
+ * @param iCol Which column of pTab is desired.
+ * @return an Expr object that refers to a memory register
+ *         corresponding to column iCol of given space.
  */
 static Expr *
-exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
-		  Table * pTab,	/* The table whose content is at r[regBase]... */
-		  int regBase,	/* Contents of table pTab */
-		  i16 iCol	/* Which column of pTab is desired */
-    )
+exprTableRegister(Parse * pParse, struct space * space, int regBase, i16 iCol)
 {
 	Expr *pExpr;
 	sqlite3 *db = pParse->db;
@@ -324,7 +327,7 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
 	if (pExpr) {
 		if (iCol >= 0) {
 			pExpr->iTable = regBase + iCol + 1;
-			char affinity = pTab->def->fields[iCol].affinity;
+			char affinity = space->def->fields[iCol].affinity;
 			pExpr->affinity = affinity;
 		} else {
 			pExpr->iTable = regBase;
@@ -400,7 +403,7 @@ exprTableColumn(sqlite3 * db, struct space_def *def, int cursor, i16 column)
  * @param incr_count Amount to increment deferred counter by.
  */
 static void
-fkey_scan_children(struct Parse *parser, struct SrcList *src, struct Table *tab,
+fkey_scan_children(struct Parse *parser, struct SrcList *src, struct space *space,
 		   struct fkey_def *fkey, int reg_data, int incr_count)
 {
 	assert(incr_count == -1 || incr_count == 1);
@@ -432,7 +435,7 @@ fkey_scan_children(struct Parse *parser, struct SrcList *src, struct Table *tab,
 	for (uint32_t i = 0; i < fkey->field_count; i++) {
 		uint32_t fieldno = fkey->links[i].parent_field;
 		struct Expr *pexpr =
-			exprTableRegister(parser, tab, reg_data, fieldno);
+			exprTableRegister(parser, space, reg_data, fieldno);
 		fieldno = fkey->links[i].child_field;
 		const char *field_name = child_space->def->fields[fieldno].name;
 		struct Expr *chexpr = sqlite3Expr(db, TK_ID, field_name);
@@ -449,13 +452,13 @@ fkey_scan_children(struct Parse *parser, struct SrcList *src, struct Table *tab,
 	 *     NOT( $current_a==a AND $current_b==b AND ... )
 	 *     The primary key is (a,b,...)
 	 */
-	if (tab->def->id == fkey->child_id && incr_count > 0) {
+	if (space->def->id == fkey->child_id && incr_count > 0) {
 		struct Expr *expr = NULL, *pexpr, *chexpr, *eq;
 		for (uint32_t i = 0; i < fkey->field_count; i++) {
 			uint32_t fieldno = fkey->links[i].parent_field;
-			pexpr = exprTableRegister(parser, tab, reg_data,
+			pexpr = exprTableRegister(parser, space, reg_data,
 						  fieldno);
-			chexpr = exprTableColumn(db, tab->def,
+			chexpr = exprTableColumn(db, space->def,
 						 src->a[0].iCursor, fieldno);
 			eq = sqlite3PExpr(parser, TK_EQ, pexpr, chexpr);
 			expr = sqlite3ExprAnd(db, expr, eq);
@@ -527,7 +530,7 @@ fkey_action_is_set_null(struct Parse *parse_context, const struct fkey *fkey)
 }
 
 void
-fkey_emit_check(struct Parse *parser, struct Table *tab, int reg_old,
+fkey_emit_check(struct Parse *parser, struct space *space, int reg_old,
 		int reg_new, const int *changed_cols)
 {
 	bool is_update = changed_cols != NULL;
@@ -543,7 +546,6 @@ fkey_emit_check(struct Parse *parser, struct Table *tab, int reg_old,
 	 * Loop through all the foreign key constraints for which
 	 * tab is the child table.
 	 */
-	struct space *space = space_by_id(tab->def->id);
 	assert(space != NULL);
 	struct fkey *fk;
 	rlist_foreach_entry(fk, &space->child_fkey, child_link) {
@@ -618,27 +620,17 @@ fkey_emit_check(struct Parse *parser, struct Table *tab, int reg_old,
 		struct SrcList_item *item = src->a;
 		struct space *child = space_by_id(fk->def->child_id);
 		assert(child != NULL);
-		/*
-		 * Create surrogate struct Table wrapper around
-		 * space_def to support legacy interface.
-		 */
-		struct Table fake_tab;
-		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->space = child;
 		item->zName = sqlite3DbStrDup(db, child->def->name);
 		item->iCursor = parser->nTab++;
 
 		if (reg_new != 0) {
-			fkey_scan_children(parser, src, tab, fk->def, reg_new,
+			fkey_scan_children(parser, src, space, fk->def, reg_new,
 					   -1);
 		}
 		if (reg_old != 0) {
 			enum fkey_action action = fk_def->on_update;
-			fkey_scan_children(parser, src, tab, fk->def, reg_old,
+			fkey_scan_children(parser, src, space, fk->def, reg_old,
 					   1);
 			/*
 			 * If this is a deferred FK constraint, or
@@ -739,7 +731,7 @@ fkey_is_required(uint32_t space_id, const int *changes)
  * foreign key object by fkey_delete().
  *
  * @param pParse Parse context.
- * @param pTab Table being updated or deleted from.
+ * @param space Space being updated or deleted from.
  * @param fkey Foreign key to get action for.
  * @param is_update True if action is on update.
  *
@@ -747,7 +739,7 @@ fkey_is_required(uint32_t space_id, const int *changes)
  * @retval NULL on failure.
  */
 static struct sql_trigger *
-fkey_action_trigger(struct Parse *pParse, struct Table *pTab, struct fkey *fkey,
+fkey_action_trigger(struct Parse *pParse, struct space *space, struct fkey *fkey,
 		    bool is_update)
 {
 	struct sqlite3 *db = pParse->db;
@@ -776,7 +768,7 @@ fkey_action_trigger(struct Parse *pParse, struct Table *pTab, struct fkey *fkey,
 		struct field_def *child_fields = child_space->def->fields;
 
 		uint32_t pcol = fk_def->links[i].parent_field;
-		sqlite3TokenInit(&t_to_col, pTab->def->fields[pcol].name);
+		sqlite3TokenInit(&t_to_col, space->def->fields[pcol].name);
 
 		uint32_t chcol = fk_def->links[i].child_field;
 		sqlite3TokenInit(&t_from_col, child_fields[chcol].name);
@@ -934,7 +926,7 @@ fkey_action_trigger(struct Parse *pParse, struct Table *pTab, struct fkey *fkey,
 }
 
 void
-fkey_emit_actions(struct Parse *parser, struct Table *tab, int reg_old,
+fkey_emit_actions(struct Parse *parser, struct space *space, int reg_old,
 		  const int *changes)
 {
 	/*
@@ -943,7 +935,6 @@ fkey_emit_actions(struct Parse *parser, struct Table *tab, int reg_old,
 	 * this operation (either update or delete),
 	 * invoke the associated trigger sub-program.
 	 */
-	struct space *space = space_by_id(tab->def->id);
 	assert(space != NULL);
 	struct fkey *fk;
 	rlist_foreach_entry(fk, &space->parent_fkey, parent_link)  {
@@ -951,10 +942,10 @@ fkey_emit_actions(struct Parse *parser, struct Table *tab, int reg_old,
 		    !fkey_is_modified(fk->def, FIELD_LINK_PARENT, changes))
 			continue;
 		struct sql_trigger *pAct =
-			fkey_action_trigger(parser, tab, fk, changes != NULL);
+			fkey_action_trigger(parser, space, fk, changes != NULL);
 		if (pAct == NULL)
 			continue;
-		vdbe_code_row_trigger_direct(parser, pAct, tab, reg_old,
+		vdbe_code_row_trigger_direct(parser, pAct, space, reg_old,
 					     ON_CONFLICT_ACTION_ABORT, 0);
 	}
 }
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index f147f6a50..c45bcc21b 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -117,14 +117,14 @@ sql_space_autoinc_fieldno(struct space *space)
  * SELECT.
  *
  * @param parser Parse context.
- * @param table Table AST object.
- * @retval  true if the table table in database or any of its
+ * @param space_def Space definition.
+ * @retval  true if the space (given by space_id) in database or any of its
  *          indices have been opened at any point in the VDBE
  *          program.
  * @retval  false else.
  */
 static bool
-vdbe_has_table_read(struct Parse *parser, const struct Table *table)
+vdbe_has_table_read(struct Parse *parser, const struct space_def *space_def)
 {
 	struct Vdbe *v = sqlite3GetVdbe(parser);
 	int last_instr = sqlite3VdbeCurrentAddr(v);
@@ -141,7 +141,7 @@ vdbe_has_table_read(struct Parse *parser, const struct Table *table)
 				space = op->p4.space;
 			else
 				continue;
-			if (space->def->id == table->def->id)
+			if (space->def->id == space_def->id)
 				return true;
 		}
 	}
@@ -260,7 +260,6 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	      enum on_conflict_action on_error)
 {
 	sqlite3 *db;		/* The main database structure */
-	Table *pTab;		/* The table to insert into.  aka TABLE */
 	char *zTab;		/* Name of the table into which we are inserting */
 	int i, j;		/* Loop counters */
 	Vdbe *v;		/* Generate code into this virtual machine */
@@ -311,32 +310,32 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	zTab = pTabList->a[0].zName;
 	if (NEVER(zTab == 0))
 		goto insert_cleanup;
-	pTab = sql_lookup_table(pParse, pTabList->a);
-	if (pTab == NULL)
-		goto insert_cleanup;
+	struct space *space = sql_lookup_space(pParse, pTabList->a);
 
-	space_id = pTab->def->id;
+	if (space == NULL)
+		goto insert_cleanup;
 
 	/* Figure out if we have any triggers and if the table being
 	 * inserted into is a view
 	 */
-	trigger = sql_triggers_exist(pTab, TK_INSERT, NULL, &tmask);
-	bool is_view = pTab->def->opts.is_view;
+	trigger = sql_triggers_exist(space->def, TK_INSERT, NULL, &tmask);
+	struct space_def *space_def = space->def;
+	space_id = space->def->id;
+
+	bool is_view = space_def->opts.is_view;
 	assert((trigger != NULL && tmask != 0) ||
 	       (trigger == NULL && tmask == 0));
 
 	/* If pTab is really a view, make sure it has been initialized.
 	 * ViewGetColumnNames() is a no-op if pTab is not a view.
 	 */
-	if (is_view &&
-	    sql_view_assign_cursors(pParse, pTab->def->opts.sql) != 0)
+	if (is_view && sql_view_assign_cursors(pParse, space_def->opts.sql) != 0)
 		goto insert_cleanup;
 
-	struct space_def *def = pTab->def;
 	/* Cannot insert into a read-only table. */
 	if (is_view && tmask == 0) {
 		sqlite3ErrorMsg(pParse, "cannot modify %s because it is a view",
-				def->name);
+				space_def->name);
 		goto insert_cleanup;
 	}
 
@@ -358,7 +357,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	 * This is the 2nd template.
 	 */
 	if (pColumn == 0 &&
-	    xferOptimization(pParse, pTab->space, pSelect, on_error)) {
+	    xferOptimization(pParse, space, pSelect, on_error)) {
 		assert(trigger == NULL);
 		assert(pList == 0);
 		goto insert_end;
@@ -372,7 +371,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	 * row record.
 	 */
 	regTupleid = regIns = ++pParse->nMem;
-	pParse->nMem += def->field_count + 1;
+	pParse->nMem += space_def->field_count + 1;
 	regData = regTupleid + 1;
 
 	/* If the INSERT statement included an IDLIST term, then make sure
@@ -384,23 +383,23 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	/* The size of used_columns buffer is checked during compilation time
 	 * using SQLITE_MAX_COLUMN constant.
 	 */
-	memset(used_columns, 0, (def->field_count + 7) / 8);
+	memset(used_columns, 0, (space_def->field_count + 7) / 8);
 	bIdListInOrder = 1;
 	if (pColumn) {
 		for (i = 0; i < pColumn->nId; i++) {
 			pColumn->a[i].idx = -1;
 		}
 		for (i = 0; i < pColumn->nId; i++) {
-			for (j = 0; j < (int) def->field_count; j++) {
+			for (j = 0; j < (int) space_def->field_count; j++) {
 				if (strcmp(pColumn->a[i].zName,
-					   def->fields[j].name) == 0) {
+					   space_def->fields[j].name) == 0) {
 					pColumn->a[i].idx = j;
 					if (i != j)
 						bIdListInOrder = 0;
 					break;
 				}
 			}
-			if (j >= (int) def->field_count) {
+			if (j >= (int) space_def->field_count) {
 				sqlite3ErrorMsg(pParse,
 						"table %S has no column named %s",
 						pTabList, 0, pColumn->a[i].zName);
@@ -436,7 +435,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
 		sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield, -1);
 		dest.iSdst = bIdListInOrder ? regData : 0;
-		dest.nSdst = def->field_count;
+		dest.nSdst = space_def->field_count;
 		rc = sqlite3Select(pParse, pSelect, &dest);
 		regFromSelect = dest.iSdst;
 		if (rc || db->mallocFailed || pParse->nErr)
@@ -459,7 +458,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		 * the SELECT statement. Also use a temp table in
 		 * the case of row triggers.
 		 */
-		if (trigger != NULL || vdbe_has_table_read(pParse, pTab))
+		if (trigger != NULL || vdbe_has_table_read(pParse, space_def))
 			useTempTable = 1;
 
 		if (useTempTable) {
@@ -519,10 +518,10 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		}
 	}
 
-	if (pColumn == 0 && nColumn && nColumn != (int)def->field_count) {
+	if (pColumn == 0 && nColumn && nColumn != (int)space_def->field_count) {
 		sqlite3ErrorMsg(pParse,
 				"table %S has %d columns but %d values were supplied",
-				pTabList, 0, def->field_count, nColumn);
+				pTabList, 0, space_def->field_count, nColumn);
 		goto insert_cleanup;
 	}
 	if (pColumn != 0 && nColumn != pColumn->nId) {
@@ -565,18 +564,18 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		    sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
 		VdbeCoverage(v);
 	}
-	struct space *space = space_by_id(pTab->def->id);
 	assert(space != NULL);
 	uint32_t autoinc_fieldno = sql_space_autoinc_fieldno(space);
 	/* Run the BEFORE and INSTEAD OF triggers, if there are any
 	 */
 	endOfLoop = sqlite3VdbeMakeLabel(v);
 	if (tmask & TRIGGER_BEFORE) {
-		int regCols = sqlite3GetTempRange(pParse, def->field_count + 1);
+		int regCols =
+			sqlite3GetTempRange(pParse, space_def->field_count + 1);
 
 		/* Create the new column data
 		 */
-		for (i = j = 0; i < (int)def->field_count; i++) {
+		for (i = j = 0; i < (int)space_def->field_count; i++) {
 			if (pColumn) {
 				for (j = 0; j < pColumn->nId; j++) {
 					if (pColumn->a[j].idx == i)
@@ -616,15 +615,15 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		 * table column affinities.
 		 */
 		if (!is_view)
-			sql_emit_table_affinity(v, pTab->def, regCols + 1);
+			sql_emit_table_affinity(v, space_def, regCols + 1);
 
 		/* Fire BEFORE or INSTEAD OF triggers */
 		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
-				      TRIGGER_BEFORE, pTab,
-				      regCols - def->field_count - 1, on_error,
+				      TRIGGER_BEFORE, space,
+				      regCols - space_def->field_count - 1, on_error,
 				      endOfLoop);
 
-		sqlite3ReleaseTempRange(pParse, regCols, def->field_count + 1);
+		sqlite3ReleaseTempRange(pParse, regCols, space_def->field_count + 1);
 	}
 
 	/* Compute the content of the next row to insert into a range of
@@ -636,7 +635,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		/* Compute data for all columns of the new entry, beginning
 		 * with the first column.
 		 */
-		for (i = 0; i < (int) def->field_count; i++) {
+		for (i = 0; i < (int) space_def->field_count; i++) {
 			int iRegStore = regData + i;
 			if (pColumn == 0) {
 				j = i;
@@ -651,7 +650,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 				if (i == (int) autoinc_fieldno) {
 					sqlite3VdbeAddOp2(v,
 							  OP_NextAutoincValue,
-							  pTab->def->id,
+							  space->def->id,
 							  iRegStore);
 					continue;
 				}
@@ -758,11 +757,11 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		 * Generate code to check constraints and process
 		 * final insertion.
 		 */
-		vdbe_emit_constraint_checks(pParse, pTab, regIns + 1,
+		vdbe_emit_constraint_checks(pParse, space, regIns + 1,
 					    on_error, endOfLoop, 0);
-		fkey_emit_check(pParse, pTab, 0, regIns, 0);
+		fkey_emit_check(pParse, space, 0, regIns, 0);
 		vdbe_emit_insertion_completion(v, space, regIns + 1,
-					       pTab->def->field_count,
+					       space->def->field_count,
 					       on_error);
 	}
 
@@ -775,8 +774,8 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	if (trigger != NULL) {
 		/* Code AFTER triggers */
 		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
-				      TRIGGER_AFTER, pTab,
-				      regData - 2 - def->field_count, on_error,
+				      TRIGGER_AFTER, space,
+				      regData - 2 - space_def->field_count, on_error,
 				      endOfLoop);
 	}
 
@@ -798,7 +797,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 
 	/* Return the number of rows inserted. */
 	if ((user_session->sql_flags & SQLITE_CountRows) != 0 &&
-	    pParse->pTriggerTab == NULL) {
+	    pParse->trigger_space == NULL) {
 		sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
 		sqlite3VdbeSetNumCols(v, 1);
 		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted",
@@ -861,7 +860,7 @@ checkConstraintUnchanged(Expr * pExpr, int *aiChng)
 }
 
 void
-vdbe_emit_constraint_checks(struct Parse *parse_context, struct Table *tab,
+vdbe_emit_constraint_checks(struct Parse *parse_context, struct space *space,
 			    int new_tuple_reg,
 			    enum on_conflict_action on_conflict,
 			    int ignore_label, int *upd_cols)
@@ -870,7 +869,6 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct Table *tab,
 	struct Vdbe *v = sqlite3GetVdbe(parse_context);
 	assert(v != NULL);
 	bool is_update = upd_cols != NULL;
-	struct space *space = space_by_id(tab->def->id);
 	assert(space != NULL);
 	struct space_def *def = space->def;
 	/* Insertion into VIEW is prohibited. */
@@ -964,7 +962,7 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct Table *tab,
 			sqlite3VdbeResolveLabel(v, all_ok);
 		}
 	}
-	sql_emit_table_affinity(v, tab->def, new_tuple_reg);
+	sql_emit_table_affinity(v, space->def, new_tuple_reg);
 	/*
 	 * Other actions except for REPLACE and UPDATE OR IGNORE
 	 * can be handled by setting appropriate flag in OP_Halt.
@@ -974,8 +972,8 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct Table *tab,
 		return;
 	/* Calculate MAX range of register we may occupy. */
 	uint32_t reg_count = 0;
-	for (uint32_t i = 0; i < tab->space->index_count; ++i) {
-		struct index *idx = tab->space->index[i];
+	for (uint32_t i = 0; i < space->index_count; ++i) {
+		struct index *idx = space->index[i];
 		if (idx->def->key_def->part_count > reg_count)
 			reg_count = idx->def->key_def->part_count;
 	}
@@ -991,8 +989,8 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct Table *tab,
 	 * Otherwise, we should skip removal of old entry and
 	 * insertion of new one.
 	 */
-	for (uint32_t i = 0; i < tab->space->index_count; ++i) {
-		struct index *idx = tab->space->index[i];
+	for (uint32_t i = 0; i < space->index_count; ++i) {
+		struct index *idx = space->index[i];
 		/* Conflicts may occur only in UNIQUE indexes. */
 		if (!idx->def->opts.is_unique)
 			continue;
@@ -1041,8 +1039,9 @@ process_index:  ;
 					     part_count);
 			sql_set_multi_write(parse_context, true);
 			struct sql_trigger *trigger =
-				sql_triggers_exist(tab, TK_DELETE, NULL, NULL);
-			sql_generate_row_delete(parse_context, tab, trigger,
+				sql_triggers_exist(space->def, TK_DELETE, NULL,
+						   NULL);
+			sql_generate_row_delete(parse_context, space, trigger,
 						cursor, idx_key_reg, part_count,
 						true,
 						ON_CONFLICT_ACTION_REPLACE,
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 6462467bc..226ed80d0 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -185,8 +185,8 @@ sqlite3MatchSpanName(const char *zSpan,
  *
  *    pExpr->iTable        Set to the cursor number for the table obtained
  *                         from pSrcList.
- *    pExpr->pTab          Points to the Table structure of X.Y (even if
- *                         X and/or Y are implied.)
+ *    pExpr->space_def     Points to the space_def structure of X.Y
+ *                         (even if X and/or Y are implied.)
  *    pExpr->iColumn       Set to the column number within the table.
  *    pExpr->op            Set to TK_COLUMN.
  *    pExpr->pLeft         Any expression this points to is deleted
@@ -218,7 +218,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 	struct SrcList_item *pMatch = 0;	/* The matching pSrcList item */
 	NameContext *pTopNC = pNC;	/* First namecontext in the list */
 	int isTrigger = 0;	/* True if resolved to a trigger column */
-	Table *pTab = 0;	/* Table hold the row */
+	struct space_def *space_def;
 
 	assert(pNC);		/* the name context cannot be NULL. */
 	assert(zCol);		/* The Z in X.Y.Z cannot be NULL */
@@ -237,9 +237,10 @@ lookupName(Parse * pParse,	/* The parsing context */
 		if (pSrcList) {
 			for (i = 0, pItem = pSrcList->a; i < pSrcList->nSrc;
 			     i++, pItem++) {
-				pTab = pItem->pTab;
-				assert(pTab != 0 && pTab->def->name != NULL);
-				assert(pTab->def->field_count > 0);
+				assert(pItem->space != NULL &&
+					pItem->space->def->name != NULL);
+				struct space_def *space_def = pItem->space->def;
+				assert(space_def->field_count > 0);
 				if (pItem->pSelect
 				    && (pItem->pSelect->
 					selFlags & SF_NestedFrom) != 0) {
@@ -262,7 +263,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 				if (zTab) {
 					const char *zTabName =
 					    pItem->zAlias ? pItem->
-					    zAlias : pTab->def->name;
+					    zAlias : space_def->name;
 					assert(zTabName != 0);
 					if (strcmp(zTabName, zTab) != 0) {
 						continue;
@@ -271,9 +272,9 @@ lookupName(Parse * pParse,	/* The parsing context */
 				if (0 == (cntTab++)) {
 					pMatch = pItem;
 				}
-				for (j = 0; j < (int)pTab->def->field_count;
+				for (j = 0; j < (int)space_def->field_count;
 				     j++) {
-					if (strcmp(pTab->def->fields[j].name,
+					if (strcmp(space_def->fields[j].name,
 						   zCol) == 0) {
 						/* If there has been exactly one prior match and this match
 						 * is for the right-hand table of a NATURAL JOIN or is in a
@@ -298,7 +299,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 			}
 			if (pMatch) {
 				pExpr->iTable = pMatch->iCursor;
-				pExpr->space_def = pMatch->pTab->def;
+				pExpr->space_def = pMatch->space->def;
 				/* RIGHT JOIN not (yet) supported */
 				assert((pMatch->fg.jointype & JT_RIGHT) == 0);
 				if ((pMatch->fg.jointype & JT_LEFT) != 0) {
@@ -311,32 +312,32 @@ lookupName(Parse * pParse,	/* The parsing context */
 		 * it is a new.* or old.* trigger argument reference
 		 */
 		if (zTab != 0 && cntTab == 0
-		    && pParse->pTriggerTab != 0) {
+		    && pParse->trigger_space != 0) {
 			int op = pParse->eTriggerOp;
 			assert(op == TK_DELETE || op == TK_UPDATE
 			       || op == TK_INSERT);
 			if (op != TK_DELETE && sqlite3StrICmp("new", zTab) == 0) {
 				pExpr->iTable = 1;
-				pTab = pParse->pTriggerTab;
+				space_def = pParse->trigger_space->def;
 			} else if (op != TK_INSERT
 				   && sqlite3StrICmp("old", zTab) == 0) {
 				pExpr->iTable = 0;
-				pTab = pParse->pTriggerTab;
+				space_def = pParse->trigger_space->def;
 			} else {
-				pTab = 0;
+				space_def = 0;
 			}
 
-			if (pTab) {
+			if (space_def) {
 				int iCol;
 				cntTab++;
 				for (iCol = 0; iCol <
-				     (int)pTab->def->field_count; iCol++) {
-					if (strcmp(pTab->def->fields[iCol].name,
+				     (int)space_def->field_count; iCol++) {
+					if (strcmp(space_def->fields[iCol].name,
 						   zCol) == 0) {
 						break;
 					}
 				}
-				if (iCol < (int)pTab->def->field_count) {
+				if (iCol < (int)space_def->field_count) {
 					cnt++;
 					if (iCol < 0) {
 						pExpr->affinity =
@@ -357,7 +358,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 						     : (((u32) 1) << iCol));
 					}
 					pExpr->iColumn = (i16) iCol;
-					pExpr->space_def = pTab->def;
+					pExpr->space_def = space_def;
 					isTrigger = 1;
 				}
 			}
@@ -489,7 +490,7 @@ sqlite3CreateColumnExpr(sqlite3 * db, SrcList * pSrc, int iSrc, int iCol)
 	Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
 	if (p) {
 		struct SrcList_item *pItem = &pSrc->a[iSrc];
-		p->space_def = pItem->pTab->def;
+		p->space_def = pItem->space->def;
 		p->iTable = pItem->iCursor;
 		p->iColumn = (ynVar) iCol;
 		testcase(iCol == BMS);
@@ -1613,7 +1614,7 @@ sqlite3ResolveSelectNames(Parse * pParse,	/* The parser context */
 }
 
 void
-sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
+sql_resolve_self_reference(struct Parse *parser, struct space *space, int type,
 			   struct Expr *expr, struct ExprList *expr_list)
 {
 	/* Fake SrcList for parser->pNewTable */
@@ -1625,8 +1626,8 @@ sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
 	memset(&sNC, 0, sizeof(sNC));
 	memset(&sSrc, 0, sizeof(sSrc));
 	sSrc.nSrc = 1;
-	sSrc.a[0].zName = table->def->name;
-	sSrc.a[0].pTab = table;
+	sSrc.a[0].zName = space->def->name;
+	sSrc.a[0].space = space;
 	sSrc.a[0].iCursor = -1;
 	sNC.pParse = parser;
 	sNC.pSrcList = &sSrc;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 02ee225f1..55739d96a 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -419,11 +419,11 @@ sqlite3JoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
  * is not contained in the table.
  */
 static int
-columnIndex(Table * pTab, const char *zCol)
+columnIndex(struct space *space, const char *zCol)
 {
 	int i;
-	for (i = 0; i < (int)pTab->def->field_count; i++) {
-		if (strcmp(pTab->def->fields[i].name, zCol) == 0)
+	for (i = 0; i < (int)space->def->field_count; i++) {
+		if (strcmp(space->def->fields[i].name, zCol) == 0)
 			return i;
 	}
 	return -1;
@@ -450,7 +450,7 @@ tableAndColumnIndex(SrcList * pSrc,	/* Array of tables to search */
 
 	assert((piTab == 0) == (piCol == 0));	/* Both or neither are NULL */
 	for (i = 0; i < N; i++) {
-		iCol = columnIndex(pSrc->a[i].pTab, zCol);
+		iCol = columnIndex(pSrc->a[i].space, zCol);
 		if (iCol >= 0) {
 			if (piTab) {
 				*piTab = i;
@@ -490,8 +490,8 @@ addWhereTerm(Parse * pParse,	/* Parsing context */
 
 	assert(iLeft < iRight);
 	assert(pSrc->nSrc > iRight);
-	assert(pSrc->a[iLeft].pTab);
-	assert(pSrc->a[iRight].pTab);
+	assert(pSrc->a[iLeft].space);
+	assert(pSrc->a[iRight].space);
 
 	pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft);
 	pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);
@@ -577,11 +577,11 @@ sqliteProcessJoin(Parse * pParse, Select * p)
 	pLeft = &pSrc->a[0];
 	pRight = &pLeft[1];
 	for (i = 0; i < pSrc->nSrc - 1; i++, pRight++, pLeft++) {
-		Table *pLeftTab = pLeft->pTab;
-		Table *pRightTab = pRight->pTab;
+		struct space *left_space = pLeft->space;
+		struct space *right_space = pRight->space;
 		int isOuter;
 
-		if (NEVER(pLeftTab == 0 || pRightTab == 0))
+		if (NEVER(left_space == 0 || right_space == 0))
 			continue;
 		isOuter = (pRight->fg.jointype & JT_OUTER) != 0;
 
@@ -595,12 +595,12 @@ sqliteProcessJoin(Parse * pParse, Select * p)
 						"an ON or USING clause", 0);
 				return 1;
 			}
-			for (j = 0; j < (int)pRightTab->def->field_count; j++) {
+			for (j = 0; j < (int)right_space->def->field_count; j++) {
 				char *zName;	/* Name of column in the right table */
 				int iLeft;	/* Matching left table */
 				int iLeftCol;	/* Matching column in the left table */
 
-				zName = pRightTab->def->fields[j].name;
+				zName = right_space->def->fields[j].name;
 				if (tableAndColumnIndex
 				    (pSrc, i + 1, zName, &iLeft, &iLeftCol)) {
 					addWhereTerm(pParse, pSrc, iLeft,
@@ -645,7 +645,7 @@ sqliteProcessJoin(Parse * pParse, Select * p)
 				int iRightCol;	/* Column number of matching column on the right */
 
 				zName = pList->a[j].zName;
-				iRightCol = columnIndex(pRightTab, zName);
+				iRightCol = columnIndex(right_space, zName);
 				if (iRightCol < 0
 				    || !tableAndColumnIndex(pSrc, i + 1, zName,
 							    &iLeft, &iLeftCol)
@@ -1757,7 +1757,6 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 			sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
 					      SQLITE_TRANSIENT);
 		} else if (p->op == TK_COLUMN || p->op == TK_AGG_COLUMN) {
-			Table *pTab;
 			char *zCol;
 			int iCol = p->iColumn;
 			for (j = 0; ALWAYS(j < pTabList->nSrc); j++) {
@@ -1765,9 +1764,9 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 					break;
 			}
 			assert(j < pTabList->nSrc);
-			pTab = pTabList->a[j].pTab;
-			assert(iCol >= 0 && iCol < (int)pTab->def->field_count);
-			zCol = pTab->def->fields[iCol].name;
+			struct space_def *space_def = pTabList->a[j].space->def;
+			assert(iCol >= 0 && iCol < (int)space_def->field_count);
+			zCol = space_def->fields[iCol].name;
 			if (!shortNames && !fullNames) {
 				sqlite3VdbeSetColName(v, i, COLNAME_NAME,
 						      sqlite3DbStrDup(db,
@@ -1775,9 +1774,8 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 						      SQLITE_DYNAMIC);
 			} else if (fullNames) {
 				char *zName = 0;
-				zName =
-				    sqlite3MPrintf(db, "%s.%s", pTab->def->name,
-						   zCol);
+				zName = sqlite3MPrintf(db, "%s.%s",
+						       space_def->name, zCol);
 				sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
 						      SQLITE_DYNAMIC);
 			} else {
@@ -1819,7 +1817,8 @@ generateColumnNames(Parse * pParse,	/* Parser context */
  * store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM.
  */
 int
-sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table)
+sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list,
+			   struct space_def *space_def)
 {
 	/* Database connection */
 	sqlite3 *db = parse->db;
@@ -1837,22 +1836,22 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table)
 	 * sqlite3ViewGetColumnNames could use it to resolve
 	 * names for existing table.
 	 */
-	assert(table->def->fields == NULL);
+	assert(space_def->fields == NULL);
 	struct region *region = &parse->region;
-	table->def->fields =
+	space_def->fields =
 		region_alloc(region,
-			     column_count * sizeof(table->def->fields[0]));
-	if (table->def->fields == NULL) {
+			     column_count * sizeof(space_def->fields[0]));
+	if (space_def->fields == NULL) {
 		sqlite3OomFault(db);
 		goto cleanup;
 	}
 	for (uint32_t i = 0; i < column_count; i++) {
-		memcpy(&table->def->fields[i], &field_def_default,
+		memcpy(&space_def->fields[i], &field_def_default,
 		       sizeof(field_def_default));
-		table->def->fields[i].nullable_action = ON_CONFLICT_ACTION_NONE;
-		table->def->fields[i].is_nullable = true;
+		space_def->fields[i].nullable_action = ON_CONFLICT_ACTION_NONE;
+		space_def->fields[i].is_nullable = true;
 	}
-	table->def->field_count = column_count;
+	space_def->field_count = column_count;
 
 	for (uint32_t i = 0; i < column_count; i++) {
 		/* Get an appropriate name for the column
@@ -1903,18 +1902,17 @@ sqlite3ColumnsFromExprList(Parse * parse, ExprList * expr_list, Table *table)
 			    sqlite3MPrintf(db, "%.*z_%u", nName, zName, ++cnt);
 		}
 		size_t name_len = strlen(zName);
-		void *field = &table->def->fields[i];
+		void *field = &space_def->fields[i];
 		if (zName != NULL &&
 		    sqlite3HashInsert(&ht, zName, field) == field)
 			sqlite3OomFault(db);
-		table->def->fields[i].name =
-			region_alloc(region, name_len + 1);
-		if (table->def->fields[i].name == NULL) {
+		space_def->fields[i].name = region_alloc(region, name_len + 1);
+		if (space_def->fields[i].name == NULL) {
 			sqlite3OomFault(db);
 			goto cleanup;
 		} else {
-			memcpy(table->def->fields[i].name, zName, name_len);
-			table->def->fields[i].name[name_len] = '\0';
+			memcpy(space_def->fields[i].name, zName, name_len);
+			space_def->fields[i].name[name_len] = '\0';
 		}
 	}
 cleanup:
@@ -1925,8 +1923,8 @@ cleanup:
 		 * pTable->def could be not temporal in
 		 * sqlite3ViewGetColumnNames so we need clean-up.
 		 */
-		table->def->fields = NULL;
-		table->def->field_count = 0;
+		space_def->fields = NULL;
+		space_def->field_count = 0;
 		rc = SQLITE_NOMEM_BKPT;
 	}
 	return rc;
@@ -1934,20 +1932,28 @@ cleanup:
 }
 
 /*
+
+ */
+
+/**
  * Add type and collation information to a column list based on
  * a SELECT statement.
  *
- * The column list presumably came from selectColumnNamesFromExprList().
- * The column list has only names, not types or collations.  This
- * routine goes through and adds the types and collations.
+ * The column list presumably came from
+ * selectColumnNamesFromExprList(). The column list has only
+ * names, not types or collations.  This routine goes through
+ * and adds the types and collations.
  *
  * This routine requires that all identifiers in the SELECT
  * statement be resolved.
+ *
+ * @param pParse Parsing contexts.
+ * @param space Add column type information to this table.
+ * @param pSelect SELECT used to determine types and collations.
  */
 void
-sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
-				       Table * pTab,		/* Add column type information to this table */
-				       Select * pSelect)	/* SELECT used to determine types and collations */
+sqlite3SelectAddColumnTypeAndCollation(Parse * pParse, struct space * space,
+				       Select * pSelect)
 {
 	sqlite3 *db = pParse->db;
 	NameContext sNC;
@@ -1956,35 +1962,35 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 
 	assert(pSelect != 0);
 	assert((pSelect->selFlags & SF_Resolved) != 0);
-	assert((int)pTab->def->field_count == pSelect->pEList->nExpr ||
+	assert((int)space->def->field_count == pSelect->pEList->nExpr ||
 	       db->mallocFailed);
 	if (db->mallocFailed)
 		return;
 	memset(&sNC, 0, sizeof(sNC));
 	sNC.pSrcList = pSelect->pSrc;
 	a = pSelect->pEList->a;
-	for (uint32_t i = 0; i < pTab->def->field_count; i++) {
+	for (uint32_t i = 0; i < space->def->field_count; i++) {
 		p = a[i].pExpr;
 		char affinity = sqlite3ExprAffinity(p);
 		if (affinity == 0)
 			affinity = AFFINITY_BLOB;
-		pTab->def->fields[i].affinity = affinity;
-		pTab->def->fields[i].type = sql_affinity_to_field_type(affinity);
+		space->def->fields[i].affinity = affinity;
+		space->def->fields[i].type = sql_affinity_to_field_type(affinity);
 		bool is_found;
 		uint32_t coll_id;
 
-		if (pTab->def->fields[i].coll_id == COLL_NONE &&
+		if (space->def->fields[i].coll_id == COLL_NONE &&
 		    sql_expr_coll(pParse, p, &is_found, &coll_id) &&
 		    coll_id != COLL_NONE)
-			pTab->def->fields[i].coll_id = coll_id;
+			space->def->fields[i].coll_id = coll_id;
 	}
 }
 
 /*
- * Given a SELECT statement, generate a Table structure that describes
+ * Given a SELECT statement, generate a space structure that describes
  * the result set of that SELECT.
  */
-Table *
+struct space *
 sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 {
 	sqlite3 *db = pParse->db;
@@ -2000,23 +2006,20 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	while (pSelect->pPrior)
 		pSelect = pSelect->pPrior;
 	user_session->sql_flags = savedFlags;
-	Table *table = sql_ephemeral_table_new(pParse, NULL);
-	if (table == NULL)
+	struct space *space = sql_ephemeral_space_new(pParse, NULL);
+
+	if (space == NULL)
 		return 0;
-	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
+	/* The sqlite3ResultSetOfSelect() is only used in contexts where lookaside
 	 * is disabled
 	 */
 	assert(db->lookaside.bDisable);
-	table->nTabRef = 1;
-	table->tuple_log_count = DEFAULT_TUPLE_LOG_COUNT;
-	assert(sqlite3LogEst(DEFAULT_TUPLE_COUNT) == DEFAULT_TUPLE_LOG_COUNT);
-	sqlite3ColumnsFromExprList(pParse, pSelect->pEList, table);
-	sqlite3SelectAddColumnTypeAndCollation(pParse, table, pSelect);
+	sqlite3ColumnsFromExprList(pParse, pSelect->pEList, space->def);
+	sqlite3SelectAddColumnTypeAndCollation(pParse, space, pSelect);
 	if (db->mallocFailed) {
-		sqlite3DeleteTable(db, table);
 		return 0;
 	}
-	return table;
+	return space;
 }
 
 /*
@@ -4043,24 +4046,9 @@ flattenSubquery(Parse * pParse,		/* Parsing context */
 	pSubitem->zAlias = 0;
 	pSubitem->pSelect = 0;
 
-	/* Defer deleting the Table object associated with the
-	 * subquery until code generation is
-	 * complete, since there may still exist Expr.pTab entries that
-	 * refer to the subquery even after flattening.  Ticket #3346.
-	 *
-	 * pSubitem->pTab is always non-NULL by test restrictions and tests above.
+	/* Deletion of the pSubitem->space will be done when a corresponding
+	 * region will be freed.
 	 */
-	if (ALWAYS(pSubitem->pTab != 0)) {
-		Table *pTabToDel = pSubitem->pTab;
-		if (pTabToDel->nTabRef == 1) {
-			Parse *pToplevel = sqlite3ParseToplevel(pParse);
-			pTabToDel->pNextZombie = pToplevel->pZombieTab;
-			pToplevel->pZombieTab = pTabToDel;
-		} else {
-			pTabToDel->nTabRef--;
-		}
-		pSubitem->pTab = 0;
-	}
 
 	/* The following loop runs once for each term in a compound-subquery
 	 * flattening (as described above).  If we are doing a different kind
@@ -4365,8 +4353,7 @@ is_simple_count(struct Select *select, struct AggInfo *agg_info)
 	    select->pSrc->nSrc != 1 || select->pSrc->a[0].pSelect != NULL) {
 		return NULL;
 	}
-	uint32_t space_id = select->pSrc->a[0].pTab->def->id;
-	struct space *space = space_by_id(space_id);
+	struct space *space = select->pSrc->a[0].space;
 	assert(space != NULL && !space->def->opts.is_view);
 	struct Expr *expr = select->pEList->a[0].pExpr;
 	assert(expr != NULL);
@@ -4391,14 +4378,14 @@ is_simple_count(struct Select *select, struct AggInfo *agg_info)
 int
 sqlite3IndexedByLookup(Parse * pParse, struct SrcList_item *pFrom)
 {
-	if (pFrom->pTab && pFrom->fg.isIndexedBy) {
-		Table *pTab = pFrom->pTab;
+	if (pFrom->space && pFrom->fg.isIndexedBy) {
+		struct space *space = pFrom->space;
 		char *zIndexedBy = pFrom->u1.zIndexedBy;
 		struct index *idx = NULL;
-		for (uint32_t i = 0; i < pTab->space->index_count; ++i) {
-			if (strcmp(pTab->space->index[i]->def->name,
+		for (uint32_t i = 0; i < space->index_count; ++i) {
+			if (strcmp(space->index[i]->def->name,
 				   zIndexedBy) == 0) {
-				idx = pTab->space->index[i];
+				idx = space->index[i];
 				break;
 			}
 		}
@@ -4572,9 +4559,9 @@ sqlite3WithPush(Parse * pParse, With * pWith, u8 bFree)
  * if currently processing a CTE expression, if it is a recursive
  * reference to the current CTE.
  *
- * If pFrom falls into either of the two categories above, pFrom->pTab
+ * If pFrom falls into either of the two categories above, pFrom->space
  * and other fields are populated accordingly. The caller should check
- * (pFrom->pTab!=0) to determine whether or not a successful match
+ * (pFrom->space!=0) to determine whether or not a successful match
  * was found.
  *
  * Whether or not a match is found, SQLITE_OK is returned if no error
@@ -4589,11 +4576,10 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 	struct Cte *pCte;	/* Matched CTE (or NULL if no match) */
 	With *pWith;		/* WITH clause that pCte belongs to */
 
-	assert(pFrom->pTab == 0);
+	assert(pFrom->space == 0);
 
 	pCte = searchWith(pParse->pWith, pFrom, &pWith);
 	if (pCte) {
-		Table *pTab;
 		ExprList *pEList;
 		Select *pSel;
 		Select *pLeft;	/* Left-most SELECT statement */
@@ -4612,15 +4598,10 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		if (cannotBeFunction(pParse, pFrom))
 			return SQLITE_ERROR;
 
-		assert(pFrom->pTab == 0);
-		pFrom->pTab = pTab =
-			sql_ephemeral_table_new(pParse, pCte->zName);
-		if (pTab == NULL)
+		assert(pFrom->space == 0);
+		pFrom->space = sql_ephemeral_space_new(pParse, pCte->zName);
+		if (pFrom->space == NULL)
 			return WRC_Abort;
-		pTab->nTabRef = 1;
-		pTab->tuple_log_count = DEFAULT_TUPLE_LOG_COUNT;
-		assert(sqlite3LogEst(DEFAULT_TUPLE_COUNT) ==
-		       DEFAULT_TUPLE_LOG_COUNT);
 		pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
 		if (db->mallocFailed)
 			return SQLITE_NOMEM_BKPT;
@@ -4629,6 +4610,7 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 		/* Check if this is a recursive CTE. */
 		pSel = pFrom->pSelect;
 		bMayRecursive = (pSel->op == TK_ALL || pSel->op == TK_UNION);
+		int ref_counter = 0;
 		if (bMayRecursive) {
 			int i;
 			SrcList *pSrc = pFrom->pSelect->pSrc;
@@ -4638,24 +4620,21 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 				    && 0 == sqlite3StrICmp(pItem->zName,
 							   pCte->zName)
 				    ) {
-					pItem->pTab = pTab;
+					pItem->space = pFrom->space;
 					pItem->fg.isRecursive = 1;
-					pTab->nTabRef++;
+					ref_counter++;
 					pSel->selFlags |= SF_Recursive;
 				}
 			}
 		}
-
-		/* Only one recursive reference is permitted. */
-		if (pTab->nTabRef > 2) {
+		if (ref_counter > 1) {
 			sqlite3ErrorMsg(pParse,
 					"multiple references to recursive table: %s",
 					pCte->zName);
 			return SQLITE_ERROR;
 		}
-		assert(pTab->nTabRef == 1
-		       || ((pSel->selFlags & SF_Recursive)
-			   && pTab->nTabRef == 2));
+		assert(ref_counter == 0 ||
+			((pSel->selFlags & SF_Recursive) && ref_counter == 1));
 
 		pCte->zCteErr = "circular reference: %s";
 		pSavedWith = pParse->pWith;
@@ -4677,7 +4656,7 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			pEList = pCte->pCols;
 		}
 
-		sqlite3ColumnsFromExprList(pParse, pEList, pTab);
+		sqlite3ColumnsFromExprList(pParse, pEList, pFrom->space->def);
 
 		if (bMayRecursive) {
 			if (pSel->selFlags & SF_Recursive) {
@@ -4777,25 +4756,24 @@ selectExpander(Walker * pWalker, Select * p)
 
 	/* Look up every table named in the FROM clause of the select.  If
 	 * an entry of the FROM clause is a subquery instead of a table or view,
-	 * then create a transient table structure to describe the subquery.
+	 * then create a transient space structure to describe the subquery.
 	 */
 	for (i = 0, pFrom = pTabList->a; i < pTabList->nSrc; i++, pFrom++) {
-		Table *pTab;
-		assert(pFrom->fg.isRecursive == 0 || pFrom->pTab != 0);
+		assert(pFrom->fg.isRecursive == 0 || pFrom->space != 0);
 		if (pFrom->fg.isRecursive)
 			continue;
-		assert(pFrom->pTab == 0);
+		assert(pFrom->space == 0);
 #ifndef SQLITE_OMIT_CTE
 		if (withExpand(pWalker, pFrom))
 			return WRC_Abort;
-		if (pFrom->pTab) {
+		if (pFrom->space) {
 		} else
 #endif
 		if (pFrom->zName == 0) {
 			Select *pSel = pFrom->pSelect;
 			/* A sub-query in the FROM clause of a SELECT */
 			assert(pSel != 0);
-			assert(pFrom->pTab == 0);
+			assert(pFrom->space == 0);
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
 			/*
@@ -4803,39 +4781,36 @@ selectExpander(Walker * pWalker, Select * p)
 			 * unique identifier.
 			 */
 			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
-			pFrom->pTab = pTab =
-				sql_ephemeral_table_new(pParse, name);
-			if (pTab == NULL)
+			struct space *space;
+			pFrom->space = space =
+			    sql_ephemeral_space_new(sqlite3ParseToplevel(pParse),
+						    name);
+
+			if (space == NULL)
 				return WRC_Abort;
 			/*
 			 * Rewrite old name with correct pointer.
 			 */
-			name = tt_sprintf("sqlite_sq_%llX", (void *)pTab);
-			sprintf(pTab->def->name, "%s", name);
-			pTab->nTabRef = 1;
+			name = tt_sprintf("sqlite_sq_%llX", (void *)space);
+			sprintf(space->def->name, "%s", name);
 			while (pSel->pPrior) {
 				pSel = pSel->pPrior;
 			}
-			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
-			if (sql_table_def_rebuild(db, pTab) != 0)
-				return WRC_Abort;
-			pTab->tuple_log_count = DEFAULT_TUPLE_LOG_COUNT;
-			assert(sqlite3LogEst(DEFAULT_TUPLE_COUNT) ==
-			       DEFAULT_TUPLE_LOG_COUNT);
+			sqlite3ColumnsFromExprList(pParse, pSel->pEList,
+			                           space->def);
 		} else {
 			/*
 			 * An ordinary table or view name in the
 			 * FROM clause.
 			 */
-			pTab = sql_lookup_table(pParse, pFrom);
-			if (pTab == NULL)
+			struct space *space = sql_lookup_space(pParse, pFrom);
+			if (space == NULL)
 				return WRC_Abort;
 			if (cannotBeFunction(pParse, pFrom))
 				return WRC_Abort;
-			if (pTab->def->opts.is_view) {
+			if (space->def->opts.is_view) {
 				struct Select *select =
-					sql_view_compile(db,
-							 pTab->def->opts.sql);
+					sql_view_compile(db, space->def->opts.sql);
 				if (select == NULL)
 					return WRC_Abort;
 				sqlite3SrcListAssignCursors(pParse,
@@ -4843,11 +4818,11 @@ selectExpander(Walker * pWalker, Select * p)
 				assert(pFrom->pSelect == 0);
 				pFrom->pSelect = select;
 				sqlite3SelectSetName(pFrom->pSelect,
-						     pTab->def->name);
-				int columns = pTab->def->field_count;
-				pTab->def->field_count = -1;
+						     space->def->name);
+				int columns = space->def->field_count;
+				space->def->field_count = -1;
 				sqlite3WalkSelect(pWalker, pFrom->pSelect);
-				pTab->def->field_count = columns;
+				space->def->field_count = columns;
 			}
 		}
 		/* Locate the index named by the INDEXED BY clause, if any. */
@@ -4929,12 +4904,11 @@ selectExpander(Walker * pWalker, Select * p)
 				}
 				for (i = 0, pFrom = pTabList->a;
 				     i < pTabList->nSrc; i++, pFrom++) {
-					Table *pTab = pFrom->pTab;
+					struct space *space = pFrom->space;
 					Select *pSub = pFrom->pSelect;
 					char *zTabName = pFrom->zAlias;
-					if (zTabName == 0) {
-						zTabName = pTab->def->name;
-					}
+					if (zTabName == 0)
+						zTabName = space->def->name;
 					if (db->mallocFailed)
 						break;
 					if (pSub == 0
@@ -4943,14 +4917,13 @@ selectExpander(Walker * pWalker, Select * p)
 					    0) {
 						pSub = 0;
 						if (zTName
-						    && strcmp(zTName,
-								      zTabName)
+						    && strcmp(zTName, zTabName)
 						    != 0) {
 							continue;
 						}
 					}
-					for (j = 0; j < (int)pTab->def->field_count; j++) {
-						char *zName = pTab->def->fields[j].name;
+					for (j = 0; j < (int)space->def->field_count; j++) {
+						char *zName = space->def->fields[j].name;
 						char *zColname;	/* The computed column name */
 						char *zToFree;	/* Malloced string that needs to be freed */
 						Token sColname;	/* Computed column name as a token */
@@ -5138,16 +5111,16 @@ selectAddSubqueryTypeInfo(Walker * pWalker, Select * p)
 	pParse = pWalker->pParse;
 	pTabList = p->pSrc;
 	for (i = 0, pFrom = pTabList->a; i < pTabList->nSrc; i++, pFrom++) {
-		Table *pTab = pFrom->pTab;
-		assert(pTab != 0);
-		if (pTab->def->id == 0) {
+		struct space *space = pFrom->space;
+		assert(space != 0);
+		if (space->def->id == 0) {
 			/* A sub-query in the FROM clause of a SELECT */
 			Select *pSel = pFrom->pSelect;
 			if (pSel) {
 				while (pSel->pPrior)
 					pSel = pSel->pPrior;
 				sqlite3SelectAddColumnTypeAndCollation(pParse,
-								       pTab,
+								       space,
 								       pSel);
 			}
 		}
@@ -5509,17 +5482,17 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		struct SrcList_item *pItem = &pTabList->a[i];
 		Select *pSub = pItem->pSelect;
 		int isAggSub;
-		Table *pTab = pItem->pTab;
+		struct space *space = pItem->space;
 		if (pSub == 0)
 			continue;
 
 		/* Catch mismatch in the declared columns of a view and the number of
 		 * columns in the SELECT on the RHS
 		 */
-		if ((int)pTab->def->field_count != pSub->pEList->nExpr) {
+		if ((int)space->def->field_count != pSub->pEList->nExpr) {
 			sqlite3ErrorMsg(pParse,
 					"expected %d columns for '%s' but got %d",
-					pTab->def->field_count, pTab->def->name,
+					space->def->field_count, space->def->name,
 					pSub->pEList->nExpr);
 			goto select_end;
 		}
@@ -5645,23 +5618,26 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 			pItem->regReturn = ++pParse->nMem;
 			sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn,
 					  0, addrTop);
-			VdbeComment((v, "%s", pItem->pTab->def->name));
+			VdbeComment((v, "%s", pItem->space->def->name));
 			pItem->addrFillSub = addrTop;
 			sqlite3SelectDestInit(&dest, SRT_Coroutine,
 					      pItem->regReturn, -1);
 			pItem->iSelectId = pParse->iNextSelectId;
 			sqlite3Select(pParse, pSub, &dest);
-			pItem->pTab->tuple_log_count = pSub->nSelectRow;
 			pItem->fg.viaCoroutine = 1;
 			pItem->regResult = dest.iSdst;
 			sqlite3VdbeEndCoroutine(v, pItem->regReturn);
 			sqlite3VdbeJumpHere(v, addrTop - 1);
 			sqlite3ClearTempRegCache(pParse);
 		} else {
-			/* Generate a subroutine that will fill an ephemeral table with
-			 * the content of this subquery.  pItem->addrFillSub will point
-			 * to the address of the generated subroutine.  pItem->regReturn
-			 * is a register allocated to hold the subroutine return address
+			/* Generate a subroutine that will fill
+			 * an ephemeral space with the content
+			 * of this subquery. pItem->addrFillSub
+			 * will point to the address of the
+			 * generated subroutine.
+			 * pItem->regReturn is a register
+			 * allocated to hold the subroutine
+			 * return address
 			 */
 			int topAddr;
 			int onceAddr = 0;
@@ -5673,28 +5649,29 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 					      pItem->regReturn);
 			pItem->addrFillSub = topAddr + 1;
 			if (pItem->fg.isCorrelated == 0) {
-				/* If the subquery is not correlated and if we are not inside of
-				 * a trigger, then we only need to compute the value of the subquery
-				 * once.
+				/* If the subquery is not
+				 * correlated and if we are not
+				 * inside of a trigger, then
+				 * we only need to compute the
+				 * value of the subquery once.
 				 */
 				onceAddr = sqlite3VdbeAddOp0(v, OP_Once);
 				VdbeCoverage(v);
 				VdbeComment((v, "materialize \"%s\"",
-					     pItem->pTab->def->name));
+					     pItem->space->def->name));
 			} else {
 				VdbeNoopComment((v, "materialize \"%s\"",
-						 pItem->pTab->def->name));
+						 pItem->space->def->name));
 			}
 			sqlite3SelectDestInit(&dest, SRT_EphemTab,
 					      pItem->iCursor, ++pParse->nMem);
 			pItem->iSelectId = pParse->iNextSelectId;
 			sqlite3Select(pParse, pSub, &dest);
-			pItem->pTab->tuple_log_count = pSub->nSelectRow;
 			if (onceAddr)
 				sqlite3VdbeJumpHere(v, onceAddr);
 			retAddr =
 			    sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
-			VdbeComment((v, "end %s", pItem->pTab->def->name));
+			VdbeComment((v, "end %s", pItem->space->def->name));
 			sqlite3VdbeChangeP1(v, topAddr, retAddr);
 			sqlite3ClearTempRegCache(pParse);
 		}
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index ee24e0337..6ace02253 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1418,7 +1418,6 @@ typedef struct SQLiteThread SQLiteThread;
 typedef struct SelectDest SelectDest;
 typedef struct SrcList SrcList;
 typedef struct StrAccum StrAccum;
-typedef struct Table Table;
 typedef struct Token Token;
 typedef struct TreeView TreeView;
 typedef struct TriggerPrg TriggerPrg;
@@ -1811,35 +1810,15 @@ struct Savepoint {
 #define SQLITE_NULLEQ       0x80	/* NULL=NULL */
 #define SQLITE_NOTNULL      0x90	/* Assert that operands are never NULL */
 
-/*
- * The schema for each SQL table and view is represented in memory
- * by an instance of the following structure.
- */
-struct Table {
-	u32 nTabRef;		/* Number of pointers to this Table */
-	/**
-	 * Estimated number of entries in table.
-	 * Used only when table represents temporary objects,
-	 * such as nested SELECTs or VIEWs. Otherwise, this stat
-	 * can be fetched from space struct.
-	 */
-	LogEst tuple_log_count;
-	Table *pNextZombie;	/* Next on the Parse.pZombieTab list */
-	/** Space definition with Tarantool metadata. */
-	struct space_def *def;
-	/** Surrogate space containing array of indexes. */
-	struct space *space;
-};
-
 /**
  * Return logarithm of tuple count in space.
  *
- * @param tab Table containing id of space to be examined.
+ * @param space Space to be examined.
  * @retval Logarithm of tuple count in space, or default values,
  *         if there is no corresponding space for given table.
  */
 LogEst
-sql_space_tuple_log_count(struct Table *tab);
+sql_space_tuple_log_count(struct space *space);
 
 /*
  * Each foreign key constraint is an instance of the following structure.
@@ -2070,7 +2049,7 @@ typedef int ynVar;
  * then iTable is the address of a subroutine that computes the subquery.
  *
  * If the Expr is of type OP_Column, and the table it is selecting from
- * is a disk table or the "old.*" pseudo-table, then pTab points to the
+ * is a disk table or the "old.*" pseudo-table, then space_def points to the
  * corresponding table definition.
  *
  * ALLOCATION NOTES:
@@ -2334,7 +2313,7 @@ struct SrcList {
 	struct SrcList_item {
 		char *zName;	/* Name of the table */
 		char *zAlias;	/* The "B" part of a "A AS B" phrase.  zName is the "A" */
-		Table *pTab;	/* An SQL table corresponding to zName */
+		struct space *space;	/* A space corresponding to zName */
 		Select *pSelect;	/* A SELECT statement used in place of a table name */
 		int addrFillSub;	/* Address of subroutine to manifest a subquery */
 		int regReturn;	/* Register holding return address of addrFillSub */
@@ -2352,7 +2331,7 @@ struct SrcList {
 		int iCursor;	/* The VDBE cursor number used to access this table */
 		Expr *pOn;	/* The ON clause of a join */
 		IdList *pUsing;	/* The USING clause of a join */
-		Bitmask colUsed;	/* Bit N (1<<N) set if column N of pTab is used */
+		Bitmask colUsed;	/* Bit N (1<<N) set if column N of space is used */
 		union {
 			char *zIndexedBy;	/* Identifier from "INDEXED BY <zIndex>" clause */
 			ExprList *pFuncArg;	/* Arguments to table-valued-function */
@@ -2733,7 +2712,7 @@ struct Parse {
 	int nSelect;		/* Number of SELECT statements seen */
 	int nSelectIndent;	/* How far to indent SELECTTRACE() output */
 	Parse *pToplevel;	/* Parse structure for main program (or NULL) */
-	Table *pTriggerTab;	/* Table triggers are being coded for */
+	struct space *trigger_space;	/* Space triggers are being coded for */
 	u32 nQueryLoop;		/* Est number of iterations of a query (10*log2(N)) */
 	u32 oldmask;		/* Mask of old.* columns referenced */
 	u32 newmask;		/* Mask of new.* columns referenced */
@@ -2776,8 +2755,7 @@ struct Parse {
 	VList *pVList;		/* Mapping between variable names and numbers */
 	Vdbe *pReprepare;	/* VM being reprepared (sqlite3Reprepare()) */
 	const char *zTail;	/* All SQL text past the last semicolon parsed */
-	Table *pNewTable;	/* A table being constructed by CREATE TABLE */
-	Table *pZombieTab;	/* List of Table objects to delete after code gen */
+	struct space *new_space;	/* A space being constructed by CREATE TABLE */
 	TriggerPrg *pTriggerPrg;	/* Linked list of coded triggers */
 	With *pWith;		/* Current WITH clause, or NULL */
 	With *pWithToFree;	/* Free this WITH object at the end of the parse */
@@ -3320,7 +3298,6 @@ u32 sqlite3ExprListFlags(const ExprList *);
 int sqlite3Init(sqlite3 *);
 
 void sqlite3Pragma(Parse *, Token *, Token *, Token *, int);
-void sqlite3DeleteColumnNames(sqlite3 *, Table *);
 
 /**
  * Return true if given column is part of primary key.
@@ -3343,20 +3320,21 @@ sql_space_column_is_in_pk(struct space *space, uint32_t);
  *
  * @param parse Parsing context.
  * @param expr_list  Expr list from which to derive column names.
- * @param table Destination table.
+ * @param space_def Destination space definition.
  * @retval SQLITE_OK on success.
  * @retval error codef on error.
  */
-int sqlite3ColumnsFromExprList(Parse *parse, ExprList *expr_list, Table *table);
+int sqlite3ColumnsFromExprList(Parse *parse, ExprList *expr_list,
+			       struct space_def *space_def);
 
-void sqlite3SelectAddColumnTypeAndCollation(Parse *, Table *, Select *);
-Table *sqlite3ResultSetOfSelect(Parse *, Select *);
+void sqlite3SelectAddColumnTypeAndCollation(Parse *, struct space *, Select *);
+struct space *sqlite3ResultSetOfSelect(Parse *, Select *);
 
 /**
  * Return the PRIMARY KEY index of a table.
  */
 struct index *
-sql_table_primary_key(const struct Table *tab);
+sql_table_primary_key(const struct space *space);
 
 void sqlite3StartTable(Parse *, Token *, int);
 void sqlite3AddColumn(Parse *, Token *, struct type_def *);
@@ -3474,7 +3452,7 @@ sql_store_select(struct Parse *parse_context, struct Select *select);
 
 void
 sql_drop_table(struct Parse *, struct SrcList *, bool, bool);
-void sqlite3DeleteTable(sqlite3 *, Table *);
+void sql_space_delete(sqlite3 *, struct space *);
 void sqlite3Insert(Parse *, SrcList *, Select *, IdList *,
 		   enum on_conflict_action);
 void *sqlite3ArrayAllocate(sqlite3 *, void *, int, int *, int *);
@@ -3542,22 +3520,22 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
  * subqueries (as in the FROM clause of a SELECT statement) in
  * this case it contains the name of a single table, as one might
  * find in an INSERT, DELETE, or UPDATE statement. Look up that
- * space in the cache and create Table wrapper around it.
+ * space in the cache.
  * Set an error message and return NULL if the table name is not
  * found or if space doesn't have format.
  *
  * The following fields are initialized appropriate in src_list:
  *
- *    src_list->a[0].pTab       Pointer to the Table object.
+ *    src_list->a[0].space      Pointer to the space object.
  *    src_list->a[0].pIndex     Pointer to the INDEXED BY index,
  *                              if there is one.
  *
  * @param parse Parsing context.
- * @param tbl_name Table element.
- * @retval Table object if found, NULL otherwise.
+ * @param space_name Space element.
+ * @retval Space object if found, NULL otherwise.
  */
-struct Table *
-sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
+struct space *
+sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name);
 
 /**
  * Generate code for a DELETE FROM statement.
@@ -3603,10 +3581,11 @@ int sqlite3WhereOkOnePass(WhereInfo *, int *);
 
 /**
  * Generate code that will extract the iColumn-th column from
- * table pTab and store the column value in a register.
+ * table defined by space_def and store the column value in a
+ * register.
  *
  * An effort is made to store the column value in register iReg.
- * This is not garanteeed for GetColumn() - the result can be
+ * This is not guaranteed for GetColumn() - the result can be
  * stored in any register.  But the result is guaranteed to land
  * in register iReg for GetColumnToReg().
  * @param pParse Parsing and code generating context.
@@ -3622,8 +3601,8 @@ sqlite3ExprCodeGetColumn(Parse *, struct space_def *, int, int, int, u8);
 
 /**
  * Generate code that will extract the iColumn-th column from
- * table pTab and store the column value in a register, copy the
- * result.
+ * table defined by space_def and store the column value in
+ * a register, copy the result.
  * @param pParse Parsing and code generating context.
  * @param space_def Space definition.
  * @param iColumn Index of the table column.
@@ -3749,7 +3728,7 @@ int sqlite3ExprNeedsNoAffinityChange(const Expr *, char);
  *   of cursor should be preserved instead.
  *
  * @param parse Parsing context.
- * @param table Table containing the row to be deleted.
+ * @param space Space containing the row to be deleted.
  * @param trigger_list List of triggers to (potentially) fire.
  * @param cursor Cursor from which column data is extracted/
  * @param reg_pk First memory cell containing the PRIMARY KEY.
@@ -3763,7 +3742,7 @@ int sqlite3ExprNeedsNoAffinityChange(const Expr *, char);
  *        to the index entry to be deleted.
  */
 void
-sql_generate_row_delete(struct Parse *parse, struct Table *table,
+sql_generate_row_delete(struct Parse *parse, struct space *space,
 			struct sql_trigger *trigger_list, int cursor,
 			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
@@ -3854,7 +3833,7 @@ sql_generate_index_key(struct Parse *parse, struct index *index, int cursor,
  *  CHECK       REPLACE    Illegal. Results in an exception.
  *
  * @param parse_context Current parsing context.
- * @param tab The table being inserted or updated.
+ * @param space The space being inserted or updated.
  * @param new_tuple_reg First register in a range holding values
  *                      to insert.
  * @param on_conflict On conflict error action of INSERT or
@@ -3865,7 +3844,7 @@ sql_generate_index_key(struct Parse *parse, struct index *index, int cursor,
  */
 void
 vdbe_emit_constraint_checks(struct Parse *parse_context,
-			    struct Table *tab, int new_tuple_reg,
+			    struct space *space, int new_tuple_reg,
 			    enum on_conflict_action on_conflict,
 			    int ignore_label, int *upd_cols);
 
@@ -3985,19 +3964,21 @@ vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
 		       bool account_changes);
 
 /**
- * Return a list of all triggers on table pTab if there exists at
- * least one trigger that must be fired when an operation of type
- * 'op' is performed on the table, and, if that operation is an
- * UPDATE, if at least one of the columns in changes_list is being
- * modified.
- *
- * @param table The table the contains the triggers.
+ * Return a list of all triggers on space (represented with
+ * space_def) if there exists at least one trigger that must be
+ * fired when an operation of type 'op' is performed on the
+ * table, and, if that operation is an UPDATE, if at least one
+ * of the columns in changes_list is being modified.
+ *
+ * @param space_def The definition of the space that contains
+ *        the triggers.
  * @param op operation one of TK_DELETE, TK_INSERT, TK_UPDATE.
  * @param changes_list Columns that change in an UPDATE statement.
  * @param[out] pMask Mask of TRIGGER_BEFORE|TRIGGER_AFTER
  */
 struct sql_trigger *
-sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
+sql_triggers_exist(struct space_def *space_def, int op,
+		   struct ExprList *changes_list,
 		   int *mask_ptr);
 
 /**
@@ -4020,13 +4001,13 @@ sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
  *   Register       Contains
  *   ------------------------------------------------------
  *   reg+0          OLD.PK
- *   reg+1          OLD.* value of left-most column of pTab
+ *   reg+1          OLD.* value of left-most column of space
  *   ...            ...
- *   reg+N          OLD.* value of right-most column of pTab
+ *   reg+N          OLD.* value of right-most column of space
  *   reg+N+1        NEW.PK
- *   reg+N+2        OLD.* value of left-most column of pTab
+ *   reg+N+2        OLD.* value of left-most column of space
  *   ...            ...
- *   reg+N+N+1      NEW.* value of right-most column of pTab
+ *   reg+N+N+1      NEW.* value of right-most column of space
  *
  * For ON DELETE triggers, the registers containing the NEW.*
  * values will never be accessed by the trigger program, so they
@@ -4048,7 +4029,7 @@ sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
  * @param op operation, one of TK_UPDATE, TK_INSERT, TK_DELETE.
  * @param changes_list Changes list for any UPDATE OF triggers.
  * @param tr_tm One of TRIGGER_BEFORE, TRIGGER_AFTER.
- * @param table The table to code triggers from.
+ * @param space The space to code triggers from.
  * @param reg The first in an array of registers.
  * @param orconf ON CONFLICT policy.
  * @param ignore_jump Instruction to jump to for RAISE(IGNORE).
@@ -4056,7 +4037,7 @@ sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 		      int op, struct ExprList *changes_list, int tr_tm,
-		      struct Table *table, int reg, int orconf, int ignore_jump);
+		      struct space *space, int reg, int orconf, int ignore_jump);
 
 /**
  * Generate code for the trigger program associated with trigger
@@ -4066,14 +4047,14 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
  *
  * @param parser Parse context.
  * @param trigger Trigger to code.
- * @param table The table to code triggers from.
+ * @param space The space to code triggers from.
  * @param reg Reg array containing OLD.* and NEW.* values.
  * @param orconf ON CONFLICT policy.
  * @param ignore_jump Instruction to jump to for RAISE(IGNORE).
  */
 void
 vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
-			     struct Table *table, int reg, int orconf,
+			     struct space *space, int reg, int orconf,
 			     int ignore_jump);
 
 void sqlite3DeleteTriggerStep(sqlite3 *, TriggerStep *);
@@ -4119,7 +4100,7 @@ TriggerStep *sqlite3TriggerDeleteStep(sqlite3 *, Token *, Expr *);
  * @param changes_list Changes list for any UPDATE OF triggers.
  * @param new  1 for new.* ref mask, 0 for old.* ref mask.
  * @param tr_tm Mask of TRIGGER_BEFORE|TRIGGER_AFTER.
- * @param table The table to code triggers from.
+ * @param space The space to code triggers from.
  * @param orconf Default ON CONFLICT policy for trigger steps.
  *
  * @retval mask value.
@@ -4127,7 +4108,7 @@ TriggerStep *sqlite3TriggerDeleteStep(sqlite3 *, Token *, Expr *);
 u32
 sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 		    ExprList *changes_list, int new, int tr_tm,
-		    Table *table, int orconf);
+		    struct space *space, int orconf);
 #define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
 #define sqlite3IsToplevel(p) ((p)->pToplevel==0)
 
@@ -4440,8 +4421,8 @@ int sqlite3ResolveOrderGroupBy(Parse *, Select *, ExprList *, const char *);
 /**
  * Generate code for default value.
  * The most recently coded instruction was an OP_Column to retrieve the
- * i-th column of table pTab. This routine sets the P4 parameter of the
- * OP_Column to the default value, if any.
+ * i-th column of table defined by space_def. This routine sets
+ * the P4 parameter of the OP_Column to the default value, if any.
  *
  * The default value of a column is specified by a DEFAULT clause in the
  * column definition. This was either supplied by the user when the table
@@ -4738,25 +4719,25 @@ void sqlite3WithPush(Parse *, With *, u8);
  * inserted using the INSERT convention.
  *
  * @param parser SQL parser.
- * @param tab Table from which the row is deleted.
+ * @param space Space from which the row is deleted.
  * @param reg_old Register with deleted row.
  * @param reg_new Register with inserted row.
  * @param changed_cols Array of updated columns. Can be NULL.
  */
 void
-fkey_emit_check(struct Parse *parser, struct Table *tab, int reg_old,
+fkey_emit_check(struct Parse *parser, struct space *space, int reg_old,
 		int reg_new, const int *changed_cols);
 
 /**
  * Emit VDBE code to do CASCADE, SET NULL or SET DEFAULT actions
  * when deleting or updating a row.
  * @param parser SQL parser.
- * @param tab Table being updated or deleted from.
+ * @param space Space being updated or deleted from.
  * @param reg_old Register of the old record.
  * param changes Array of numbers of changed columns.
  */
 void
-fkey_emit_actions(struct Parse *parser, struct Table *tab, int reg_old,
+fkey_emit_actions(struct Parse *parser, struct space *space, int reg_old,
 		  const int *changes);
 
 /**
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 0aa31bdd6..4db541115 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -131,19 +131,19 @@ tarantoolSqlite3IncrementMaxid(uint64_t *space_max_id);
 /**
  * Encode format as entry to be inserted to _space on @region.
  * @param region Region to use.
- * @param table Table to encode.
+ * @param table Space to encode.
  * @param[out] size Size of result allocation.
  *
  * @retval NULL Error.
  * @retval not NULL Pointer to msgpack on success.
  */
 char *
-sql_encode_table(struct region *region, struct Table *table, uint32_t *size);
+sql_encode_table(struct region *region, struct space *space, uint32_t *size);
 
 /**
  * Encode "opts" dictionary for _space entry on @region.
  * @param region Region to use.
- * @param table Table containing opts to encode.
+ * @param space Space containing opts to encode.
  * @param sql Source request to encode.
  * @param[out] size Size of result allocation.
  *
@@ -151,7 +151,7 @@ sql_encode_table(struct region *region, struct Table *table, uint32_t *size);
  * @retval not NULL Pointer to msgpack on success.
  */
 char *
-sql_encode_table_opts(struct region *region, struct Table *table,
+sql_encode_table_opts(struct region *region, struct space *space,
 		      const char *sql, uint32_t *size);
 
 /**
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 4eebfe527..5be4c57f6 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -449,7 +449,7 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		sqlite3OomFault(db);
 		return SQLITE_NOMEM_BKPT;
 	}
-	assert(pParse->pNewTable == 0);
+	assert(pParse->new_space == 0);
 	assert(pParse->parsed_ast.trigger == NULL);
 	assert(pParse->nVar == 0);
 	assert(pParse->pVList == 0);
@@ -529,16 +529,10 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 		sqlite3VdbeDelete(pParse->pVdbe);
 		pParse->pVdbe = 0;
 	}
-	sqlite3DeleteTable(db, pParse->pNewTable);
-
+	sql_space_delete(db, pParse->new_space);
 	if (pParse->pWithToFree)
 		sqlite3WithDelete(db, pParse->pWithToFree);
 	sqlite3DbFree(db, pParse->pVList);
-	while (pParse->pZombieTab) {
-		Table *p = pParse->pZombieTab;
-		pParse->pZombieTab = p->pNextZombie;
-		sqlite3DeleteTable(db, p);
-	}
 	assert(nErr == 0 || pParse->rc != SQLITE_OK);
 	return nErr;
 }
diff --git a/src/box/sql/treeview.c b/src/box/sql/treeview.c
index f6a1755f4..a97ffdded 100644
--- a/src/box/sql/treeview.c
+++ b/src/box/sql/treeview.c
@@ -222,9 +222,9 @@ sqlite3TreeViewSelect(TreeView * pView, const Select * p, u8 moreToFollow)
 				if (pItem->zName) {
 					sqlite3XPrintf(&x, " %s", pItem->zName);
 				}
-				if (pItem->pTab) {
+				if (pItem->space) {
 					sqlite3XPrintf(&x, " tabname=%Q",
-						       pItem->pTab->def->name);
+						       pItem->space->def->name);
 				}
 				if (pItem->zAlias) {
 					sqlite3XPrintf(&x, " (AS %s)",
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 7d5dc9e23..7f1d18342 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -558,14 +558,15 @@ checkColumnOverlap(IdList * pIdList, ExprList * pEList)
 }
 
 struct sql_trigger *
-sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
+sql_triggers_exist(struct space_def *space_def, int op,
+		   struct ExprList *changes_list,
 		   int *mask_ptr)
 {
 	int mask = 0;
 	struct sql_trigger *trigger_list = NULL;
 	struct session *user_session = current_session();
 	if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0)
-		trigger_list = space_trigger_list(table->def->id);
+		trigger_list = space_trigger_list(space_def->id);
 	for (struct sql_trigger *p = trigger_list; p != NULL; p = p->next) {
 		if (p->op == op && checkColumnOverlap(p->pColumns,
 						      changes_list) != 0)
@@ -614,7 +615,7 @@ codeTriggerProgram(Parse * pParse,	/* The parser context */
 	Vdbe *v = pParse->pVdbe;
 	sqlite3 *db = pParse->db;
 
-	assert(pParse->pTriggerTab && pParse->pToplevel);
+	assert(pParse->trigger_space && pParse->pToplevel);
 	assert(pStepList);
 	assert(v != 0);
 
@@ -748,7 +749,7 @@ transferParseError(Parse * pTo, Parse * pFrom)
  *
  * @param parser Current parse context.
  * @param trigger sql_trigger to code.
- * @param table trigger is attached to.
+ * @param space Trigger is attached to.
  * @param orconf ON CONFLICT policy to code trigger program with.
  *
  * @retval not NULL on success.
@@ -756,7 +757,7 @@ transferParseError(Parse * pTo, Parse * pFrom)
  */
 static TriggerPrg *
 sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
-			struct Table *table, int orconf)
+			struct space *space, int orconf)
 {
 	Parse *pTop = sqlite3ParseToplevel(parser);
 	/* Database handle. */
@@ -768,7 +769,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	Parse *pSubParse;	/* Parse context for sub-vdbe */
 	int iEndTrigger = 0;	/* Label to jump to if WHEN is false */
 
-	assert(trigger->zName == NULL || table->def->id == trigger->space_id);
+	assert(trigger->zName == NULL || space->def->id == trigger->space_id);
 	assert(pTop->pVdbe);
 
 	/* Allocate the TriggerPrg and SubProgram objects. To ensure that they
@@ -799,7 +800,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	sql_parser_create(pSubParse, db);
 	memset(&sNC, 0, sizeof(sNC));
 	sNC.pParse = pSubParse;
-	pSubParse->pTriggerTab = table;
+	pSubParse->trigger_space = space;
 	pSubParse->pToplevel = pTop;
 	pSubParse->eTriggerOp = trigger->op;
 	pSubParse->nQueryLoop = parser->nQueryLoop;
@@ -814,7 +815,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 			     (trigger->op == TK_UPDATE ? "UPDATE" : ""),
 			     (trigger->op == TK_INSERT ? "INSERT" : ""),
 			     (trigger->op == TK_DELETE ? "DELETE" : ""),
-			      table->def->name));
+			      space->def->name));
 #ifndef SQLITE_OMIT_TRACE
 		sqlite3VdbeChangeP4(v, -1,
 				    sqlite3MPrintf(db, "-- TRIGGER %s",
@@ -864,7 +865,6 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 		sqlite3VdbeDelete(v);
 	}
 
-	assert(!pSubParse->pZombieTab);
 	assert(!pSubParse->pTriggerPrg && !pSubParse->nMaxArg);
 	sql_parser_destroy(pSubParse);
 	sqlite3StackFree(db, pSubParse);
@@ -888,12 +888,12 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
  */
 static TriggerPrg *
 sql_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
-		struct Table *table, int orconf)
+		struct space *space, int orconf)
 {
 	Parse *pRoot = sqlite3ParseToplevel(parser);
 	TriggerPrg *pPrg;
 
-	assert(trigger->zName == NULL || table->def->id == trigger->space_id);
+	assert(trigger->zName == NULL || space->def->id == trigger->space_id);
 
 	/*
 	 * It may be that this trigger has already been coded (or
@@ -911,20 +911,20 @@ sql_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 	 * a new one.
 	 */
 	if (pPrg == NULL)
-		pPrg = sql_row_trigger_program(parser, trigger, table, orconf);
+		pPrg = sql_row_trigger_program(parser, trigger, space, orconf);
 
 	return pPrg;
 }
 
 void
 vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
-			     struct Table *table, int reg, int orconf,
+			     struct space *space, int reg, int orconf,
 			     int ignore_jump)
 {
 	/* Main VM. */
 	struct Vdbe *v = sqlite3GetVdbe(parser);
 
-	TriggerPrg *pPrg = sql_row_trigger(parser, trigger, table, orconf);
+	TriggerPrg *pPrg = sql_row_trigger(parser, trigger, space, orconf);
 	assert(pPrg != NULL || parser->nErr != 0 ||
 	       parser->db->mallocFailed != 0);
 
@@ -962,7 +962,7 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 		      int op, struct ExprList *changes_list, int tr_tm,
-		      struct Table *table, int reg, int orconf, int ignore_jump)
+		      struct space *space, int reg, int orconf, int ignore_jump)
 {
 	assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
 	assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);
@@ -972,7 +972,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 		/* Determine whether we should code trigger. */
 		if (p->op == op && p->tr_tm == tr_tm &&
 		    checkColumnOverlap(p->pColumns, changes_list)) {
-			vdbe_code_row_trigger_direct(parser, p, table, reg,
+			vdbe_code_row_trigger_direct(parser, p, space, reg,
 						     orconf, ignore_jump);
 		}
 	}
@@ -981,7 +981,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 u32
 sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 		    ExprList *changes_list, int new, int tr_tm,
-		    Table *table, int orconf)
+		    struct space *space, int orconf)
 {
 	const int op = changes_list != NULL ? TK_UPDATE : TK_DELETE;
 	u32 mask = 0;
@@ -991,7 +991,7 @@ sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 		if (p->op == op && (tr_tm & p->tr_tm)
 		    && checkColumnOverlap(p->pColumns, changes_list)) {
 			TriggerPrg *prg =
-				sql_row_trigger(parser, p, table, orconf);
+				sql_row_trigger(parser, p, space, orconf);
 			if (prg != NULL)
 				mask |= prg->aColmask[new];
 		}
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index dbef22fd8..3b7122360 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -81,7 +81,6 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	      enum on_conflict_action on_error)
 {
 	int i, j;		/* Loop counters */
-	Table *pTab;		/* The table to be updated */
 	int addrTop = 0;	/* VDBE instruction address of the start of the loop */
 	WhereInfo *pWInfo;	/* Information about the WHERE clause */
 	Vdbe *v;		/* The virtual database engine */
@@ -124,32 +123,32 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 
 	/* Locate the table which we want to update.
 	 */
-	pTab = sql_lookup_table(pParse, pTabList->a);
-	if (pTab == NULL)
+	struct space *space = sql_lookup_space(pParse, pTabList->a);
+	if (space == NULL)
 		goto update_cleanup;
 
 	/* Figure out if we have any triggers and if the table being
 	 * updated is a view.
 	 */
-	trigger = sql_triggers_exist(pTab, TK_UPDATE, pChanges, &tmask);
-	is_view = pTab->def->opts.is_view;
+	trigger = sql_triggers_exist(space->def, TK_UPDATE, pChanges, &tmask);
+	is_view = space->def->opts.is_view;
 	assert(trigger != NULL || tmask == 0);
 
 	if (is_view &&
-	    sql_view_assign_cursors(pParse,pTab->def->opts.sql) != 0) {
+	    sql_view_assign_cursors(pParse, space->def->opts.sql) != 0) {
 		goto update_cleanup;
 	}
 	if (is_view && tmask == 0) {
 		sqlite3ErrorMsg(pParse, "cannot modify %s because it is a view",
-				pTab->def->name);
+				space->def->name);
 		goto update_cleanup;
 	}
 
-	struct space_def *def = pTab->def;
+	struct space_def *def = space->def;
 	/* Allocate cursor on primary index. */
 	int pk_cursor = pParse->nTab++;
 	pTabList->a[0].iCursor = pk_cursor;
-	struct index *pPk = space_index(pTab->space, 0);
+	struct index *pPk = space_index(space, 0);
 	i = sizeof(int) * def->field_count;
 	aXRef = (int *) region_alloc(&pParse->region, i);
 	if (aXRef == NULL) {
@@ -176,7 +175,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 			if (strcmp(def->fields[j].name,
 				   pChanges->a[i].zName) == 0) {
 				if (pPk &&
-				    sql_space_column_is_in_pk(pTab->space, j))
+				    sql_space_column_is_in_pk(space, j))
 					is_pk_modified = true;
 				if (aXRef[j] != -1) {
 					sqlite3ErrorMsg(pParse,
@@ -202,7 +201,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 */
 	pTabList->a[0].colUsed = 0;
 
-	hasFK = fkey_is_required(pTab->def->id, aXRef);
+	hasFK = fkey_is_required(space->def->id, aXRef);
 
 	/* Begin generating code. */
 	v = sqlite3GetVdbe(pParse);
@@ -226,13 +225,11 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * an ephemeral table.
 	 */
 	uint32_t pk_part_count;
-	struct space *space;
 	if (is_view) {
 		sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
 		/* Number of columns from SELECT plus ID.*/
 		pk_part_count = nKey = def->field_count + 1;
 	} else {
-		space = pTab->space;
 		assert(space != NULL);
 		vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
 		pk_part_count = pPk->def->key_def->part_count;
@@ -298,7 +295,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	/* Initialize the count of updated rows
 	 */
 	if ((user_session->sql_flags & SQLITE_CountRows)
-	    && !pParse->pTriggerTab) {
+	    && !pParse->trigger_space) {
 		regRowCount = ++pParse->nMem;
 		sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
 	}
@@ -333,16 +330,15 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * information is needed
 	 */
 	if (is_pk_modified || hasFK != 0 || trigger != NULL) {
-		struct space *space = space_by_id(pTab->def->id);
 		assert(space != NULL);
 		u32 oldmask = hasFK ? space->fkey_mask : 0;
 		oldmask |= sql_trigger_colmask(pParse, trigger, pChanges, 0,
 					       TRIGGER_BEFORE | TRIGGER_AFTER,
-					       pTab, on_error);
+					       space, on_error);
 		for (i = 0; i < (int)def->field_count; i++) {
 			if (oldmask == 0xffffffff
 			    || (i < 32 && (oldmask & MASKBIT32(i)) != 0) ||
-				sql_space_column_is_in_pk(pTab->space, i)) {
+				sql_space_column_is_in_pk(space, i)) {
 				testcase(oldmask != 0xffffffff && i == 31);
 				sqlite3ExprCodeGetColumnOfTable(v, def,
 								pk_cursor, i,
@@ -368,7 +364,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * be used eliminates some redundant opcodes.
 	 */
 	newmask = sql_trigger_colmask(pParse, trigger, pChanges, 1,
-				      TRIGGER_BEFORE, pTab, on_error);
+				      TRIGGER_BEFORE, space, on_error);
 	for (i = 0; i < (int)def->field_count; i++) {
 		j = aXRef[i];
 		if (j >= 0) {
@@ -394,9 +390,9 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * verified. One could argue that this is wrong.
 	 */
 	if (tmask & TRIGGER_BEFORE) {
-		sql_emit_table_affinity(v, pTab->def, regNew);
+		sql_emit_table_affinity(v, space->def, regNew);
 		vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
-				      TRIGGER_BEFORE, pTab, regOldPk,
+				      TRIGGER_BEFORE, space, regOldPk,
 				      on_error, labelContinue);
 
 		/* The row-trigger may have deleted the row being updated. In this
@@ -431,11 +427,11 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 
 	if (!is_view) {
 		assert(regOldPk > 0);
-		vdbe_emit_constraint_checks(pParse, pTab, regNewPk + 1,
+		vdbe_emit_constraint_checks(pParse, space, regNewPk + 1,
 					    on_error, labelContinue, aXRef);
 		/* Do FK constraint checks. */
 		if (hasFK) {
-			fkey_emit_check(pParse, pTab, regOldPk, 0, aXRef);
+			fkey_emit_check(pParse, space, regOldPk, 0, aXRef);
 		}
 		if (on_error == ON_CONFLICT_ACTION_REPLACE) {
 			/*
@@ -451,11 +447,11 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 			sqlite3VdbeJumpHere(v, not_found_lbl);
 		}
 		if (hasFK) {
-			fkey_emit_check(pParse, pTab, 0, regNewPk, aXRef);
+			fkey_emit_check(pParse, space, 0, regNewPk, aXRef);
 		}
 		if (on_error == ON_CONFLICT_ACTION_REPLACE) {
 			 vdbe_emit_insertion_completion(v, space, regNew,
-							pTab->def->field_count,
+							space->def->field_count,
 							on_error);
 
 		} else {
@@ -501,18 +497,18 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		 * via a foreign key to the row just updated.
 		 */
 		if (hasFK)
-			fkey_emit_actions(pParse, pTab, regOldPk, aXRef);
+			fkey_emit_actions(pParse, space, regOldPk, aXRef);
 	}
 
 	/* Increment the row counter
 	 */
 	if ((user_session->sql_flags & SQLITE_CountRows)
-	    && !pParse->pTriggerTab) {
+	    && !pParse->trigger_space) {
 		sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
 	vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
-			      TRIGGER_AFTER, pTab, regOldPk, on_error,
+			      TRIGGER_AFTER, space, regOldPk, on_error,
 			      labelContinue);
 
 	/* Repeat the above with the next record to be updated, until
@@ -529,7 +525,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 
 	/* Return the number of rows that were changed. */
 	if (user_session->sql_flags & SQLITE_CountRows &&
-	    pParse->pTriggerTab == NULL) {
+	    pParse->trigger_space == NULL) {
 		sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
 		sqlite3VdbeSetNumCols(v, 1);
 		sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated",
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 571b5af78..c26661a20 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -580,7 +580,6 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
 		    WhereClause * pWC,		/* The WHERE clause */
 		    ExprList * pDistinct)	/* The result set that needs to be DISTINCT */
 {
-	Table *pTab;
 	int iBase;
 
 	/* If there is more than one table or sub-select in the FROM clause of
@@ -590,7 +589,7 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
 	if (pTabList->nSrc != 1)
 		return 0;
 	iBase = pTabList->a[0].iCursor;
-	pTab = pTabList->a[0].pTab;
+	struct space *space = pTabList->a[0].space;
 
 	/* If any of the expressions is an IPK column on table iBase, then return
 	 * true. Note: The (p->iTable==iBase) part of this test may be false if the
@@ -601,7 +600,7 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
 		if (p->op == TK_COLUMN && p->iTable == iBase && p->iColumn < 0)
 			return 1;
 	}
-	if (pTab->space == NULL)
+	if (space == NULL)
 		return 0;
 	/* Loop through all indices on the table, checking each to see if it makes
 	 * the DISTINCT qualifier redundant. It does so if:
@@ -616,8 +615,8 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
 	 *   3. All of those index columns for which the WHERE clause does not
 	 *      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_def *def = pTab->space->index[j]->def;
+	for (uint32_t j = 0; j < space->index_count; ++j) {
+		struct index_def *def = space->index[j]->def;
 		if (!def->opts.is_unique)
 			continue;
 		uint32_t col_count = def->key_def->part_count;
@@ -629,7 +628,7 @@ isDistinctRedundant(Parse * pParse,		/* Parsing context */
 						 i) < 0)
 					break;
 				uint32_t x = def->key_def->parts[i].fieldno;
-				if (pTab->def->fields[x].is_nullable)
+				if (space->def->fields[x].is_nullable)
 					break;
 			}
 		}
@@ -1685,13 +1684,13 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)
 	WhereInfo *pWInfo = pWC->pWInfo;
 	int nb = 1 + (pWInfo->pTabList->nSrc + 3) / 4;
 	struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
-	Table *pTab = pItem->pTab;
+	struct space_def *space_def = pItem->space->def;
 	Bitmask mAll = (((Bitmask) 1) << (nb * 4)) - 1;
 #ifdef SQLITE_DEBUG
 	sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
 			   p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
 	sqlite3DebugPrintf(" %12s",
-			   pItem->zAlias ? pItem->zAlias : pTab->def->name);
+			   pItem->zAlias ? pItem->zAlias : space_def->name);
 #endif
 	const char *zName;
 	if (p->index_def != NULL && (zName = p->index_def->name) != NULL) {
@@ -2778,20 +2777,19 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 	int b;			/* A boolean value */
 	LogEst rSize;		/* number of rows in the table */
 	WhereClause *pWC;	/* The parsed WHERE clause */
-	Table *pTab;		/* Table being queried */
 
 	pNew = pBuilder->pNew;
 	pWInfo = pBuilder->pWInfo;
 	pTabList = pWInfo->pTabList;
 	pSrc = pTabList->a + pNew->iTab;
-	pTab = pSrc->pTab;
 	pWC = pBuilder->pWC;
 
+	struct space *space = pSrc->space;
 	if (pSrc->pIBIndex) {
 		/* An INDEXED BY clause specifies a particular index to use */
 		probe = pSrc->pIBIndex;
-	} else if (pTab->space->index_count != 0) {
-		probe = pTab->space->index[0]->def;
+	} else if (space->index_count != 0) {
+		probe = space->index[0]->def;
 	} else {
 		/* There is no INDEXED BY clause.  Create a fake Index object in local
 		 * variable fake_index to represent the primary key index.  Make this
@@ -2802,7 +2800,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder,	/* WHERE clause information */
 
 		struct key_part_def part;
 		part.fieldno = 0;
-		part.type = pTab->def->fields[0].type;
+		part.type = space->def->fields[0].type;
 		part.nullable_action = ON_CONFLICT_ACTION_ABORT;
 		part.is_nullable = false;
 		part.sort_order = SORT_ORDER_ASC;
@@ -2818,7 +2816,7 @@ tnt_error:
 
 		struct index_opts opts;
 		index_opts_create(&opts);
-		fake_index = index_def_new(pTab->def->id, 0,"fake_autoindex",
+		fake_index = index_def_new(space->def->id, 0,"fake_autoindex",
 					   sizeof("fake_autoindex") - 1,
 					   TREE, &opts, key_def, NULL);
 		key_def_delete(key_def);
@@ -2834,7 +2832,7 @@ tnt_error:
 			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[0] = sql_space_tuple_log_count(pSrc->space);
 		stat->tuple_log_est[1] = 0;
 		fake_index->opts.stat = stat;
 
@@ -2901,10 +2899,10 @@ tnt_error:
 	 * index is considered.
 	 */
 	uint32_t idx_count = fake_index == NULL || pSrc->pIBIndex != NULL ?
-			     pTab->space->index_count : 1;
+			     space->index_count : 1;
 	for (uint32_t i = 0; i < idx_count; iSortIdx++, i++) {
 		if (i > 0)
-			probe = pTab->space->index[i]->def;
+			probe = space->index[i]->def;
 		rSize = index_field_tuple_est(probe, 0);
 		pNew->nEq = 0;
 		pNew->nBtm = 0;
@@ -4083,7 +4081,7 @@ where_loop_builder_shortcut(struct WhereLoopBuilder *builder)
 		return 0;
 	assert(where_info->pTabList->nSrc >= 1);
 	struct SrcList_item *item = where_info->pTabList->a;
-	struct space_def *space_def = item->pTab->def;
+	struct space_def *space_def = item->space->def;
 	assert(space_def != NULL);
 	if (item->fg.isIndexedBy)
 		return 0;
@@ -4559,10 +4557,10 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 	 */
 	for (ii = 0, pLevel = pWInfo->a; ii < nTabList; ii++, pLevel++) {
 		struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
-		Table *pTab = pTabItem->pTab;
+		struct space_def *space_def = pTabItem->space->def;
 		pLoop = pLevel->pWLoop;
-		struct space *space = space_cache_find(pTabItem->pTab->def->id);
-		if (pTab->def->id == 0 || pTab->def->opts.is_view) {
+		struct space *space = space_cache_find(space_def->id);
+		if (space_def->id == 0 || space_def->opts.is_view) {
 			/* Do nothing */
 		} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
 			   (wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) {
@@ -4573,10 +4571,6 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 					      space);
 			VdbeComment((v, "%s", space->def->name));
 			assert(pTabItem->iCursor == pLevel->iTabCur);
-			testcase(pWInfo->eOnePass == ONEPASS_OFF
-				 && pTab->nCol == BMS - 1);
-			testcase(pWInfo->eOnePass == ONEPASS_OFF
-				 && pTab->nCol == BMS);
 			sqlite3VdbeChangeP5(v, bFordelete);
 #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
 			sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
@@ -4612,15 +4606,16 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 				op = 0;
 			} else if (pWInfo->eOnePass != ONEPASS_OFF) {
 				iIndexCur = iAuxArg;
-				if (pTabItem->pTab->space->index_count != 0) {
+				if (pTabItem->space->index_count != 0) {
 					uint32_t iid = 0;
-					struct index *pJ = pTabItem->pTab->space->index[iid];
+					struct index *pJ =
+						pTabItem->space->index[iid];
 					assert(wctrlFlags &
 					       WHERE_ONEPASS_DESIRED);
 					while (pJ->def->iid != idx_def->iid) {
 						iIndexCur++;
 						iid++;
-						pJ = pTabItem->pTab->space->index[iid];
+						pJ = pTabItem->space->index[iid];
 					}
 				} else {
 					for(uint32_t i = 0;
@@ -4825,7 +4820,7 @@ sqlite3WhereEnd(WhereInfo * pWInfo)
 			sqlite3VdbeJumpHere(v, addr);
 		}
 		VdbeModuleComment((v, "End WHERE-loop%d: %s", i,
-				   pWInfo->pTabList->a[pLevel->iFrom].pTab->
+				   pWInfo->pTabList->a[pLevel->iFrom].space->
 				   def->name));
 	}
 
@@ -4839,8 +4834,7 @@ sqlite3WhereEnd(WhereInfo * pWInfo)
 		int k, last;
 		VdbeOp *pOp;
 		struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
-		Table *pTab MAYBE_UNUSED = pTabItem->pTab;
-		assert(pTab != 0);
+		assert(pTabItem->space != 0);
 		pLoop = pLevel->pWLoop;
 
 		/* For a co-routine, change all OP_Column references to the table of
@@ -4880,7 +4874,8 @@ sqlite3WhereEnd(WhereInfo * pWInfo)
 				if (pOp->opcode == OP_Column) {
 					int x = pOp->p2;
 					assert(def == NULL ||
-					       def->space_id == pTab->def->id);
+					       def->space_id ==
+					       pTabItem->space->def->id);
 					if (x >= 0) {
 						pOp->p2 = x;
 						pOp->p1 = pLevel->iIdxCur;
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index b124a1d53..631e40b02 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -934,7 +934,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 				  pTabItem->addrFillSub);
 		pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
 		VdbeCoverage(v);
-		VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->def->name));
+		VdbeComment((v, "next row of \"%s\"", pTabItem->space->def->name));
 		pLevel->op = OP_Goto;
 	} else if (pLoop->wsFlags & WHERE_INDEXED) {
 		/* Case 4: A scan using an index.
@@ -1353,9 +1353,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 		int ii;		/* Loop counter */
 		u16 wctrlFlags;	/* Flags for sub-WHERE clause */
 		Expr *pAndExpr = 0;	/* An ".. AND (...)" expression */
-		Table *pTab = pTabItem->pTab;
+		struct space *space = pTabItem->space;
 		struct key_def *pk_key_def =
-			space_index(pTab->space, 0)->def->key_def;
+			space_index(space, 0)->def->key_def;
 		uint32_t pk_part_count = pk_key_def->part_count;
 
 		pTerm = pLoop->aLTerm[0];
@@ -1520,7 +1520,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 								fieldno;
 							sqlite3ExprCodeGetColumnToReg
 								(pParse,
-								 pTab->def,
+								 space->def,
 								 fieldno,
 								 iCur,
 								 r + iPk);
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 342064ec8..b1efff558 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1477,26 +1477,23 @@ sqlite3WhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 			WhereClause * pWC	/* Xfer function arguments to here */
     )
 {
-	Table *pTab;
 	int j, k;
 	ExprList *pArgs;
 	Expr *pColRef;
 	Expr *pTerm;
 	if (pItem->fg.isTabFunc == 0)
 		return;
-	pTab = pItem->pTab;
-	assert(pTab != 0);
+	struct space_def *space_def = pItem->space->def;
 	pArgs = pItem->u1.pFuncArg;
 	if (pArgs == 0)
 		return;
 	for (j = k = 0; j < pArgs->nExpr; j++) {
-		while (k < (int)pTab->def->field_count) {
+		while (k < (int)space_def->field_count)
 			k++;
-		}
-		if (k >= (int)pTab->def->field_count) {
+		if (k >= (int)space_def->field_count) {
 			sqlite3ErrorMsg(pParse,
 					"too many arguments on %s() - max %d",
-					pTab->def->name, j);
+					space_def->name, j);
 			return;
 		}
 		pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
@@ -1504,7 +1501,7 @@ sqlite3WhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 			return;
 		pColRef->iTable = pItem->iCursor;
 		pColRef->iColumn = k++;
-		pColRef->space_def = pTab->def;
+		pColRef->space_def = space_def;
 		pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
 				     sqlite3ExprDup(pParse->db,
 						    pArgs->a[j].pExpr, 0));
-- 
2.14.3 (Apple Git-98)