<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <pre>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: <a class="moz-txt-link-freetext" href="https://github.com/tarantool/tarantool/tree/sudobobo/gh-3235-repl-Table-w-space" moz-do-not-send="true">https://github.com/tarantool/tarantool/tree/sudobobo/gh-3235-repl-Table-w-space</a>
Issue: <a class="moz-txt-link-freetext" href="https://github.com/tarantool/tarantool/issues/3235" moz-do-not-send="true">https://github.com/tarantool/tarantool/issues/3235</a>

 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)


</pre>
  </body>
</html>