From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 48BA1246B4 for ; Mon, 28 Jan 2019 04:54:46 -0500 (EST) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id MXKrTFyCndZi for ; Mon, 28 Jan 2019 04:54:46 -0500 (EST) Received: from smtpng3.m.smailru.net (smtpng3.m.smailru.net [94.100.177.149]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id EC12C222F7 for ; Mon, 28 Jan 2019 04:54:44 -0500 (EST) Received: by smtpng3.m.smailru.net with esmtpa (envelope-from ) id 1go3co-0006mg-UU for tarantool-patches@freelists.org; Mon, 28 Jan 2019 12:54:43 +0300 From: Ivan Koptelov Subject: [tarantool-patches] [PATCH] sql: remove struct Table Message-ID: <4c2b87b5-9e83-f96f-4350-edf7a12e5593@tarantool.org> Date: Mon, 28 Jan 2019 12:54:42 +0300 MIME-Version: 1.0 Content-Type: text/html; charset=utf-8 Content-Language: en-GB Content-Transfer-Encoding: 7bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org
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)