Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org
Cc: v.shpilevoy@tarantool.org,
	Kirill Shcherbatov <kshcherbatov@tarantool.org>
Subject: [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column
Date: Fri, 11 May 2018 11:49:46 +0300	[thread overview]
Message-ID: <b0e872bbbb844bf9527aaa481f7c76894f195332.1526028449.git.kshcherbatov@tarantool.org> (raw)
In-Reply-To: <cover.1526028449.git.kshcherbatov@tarantool.org>
In-Reply-To: <cover.1526028449.git.kshcherbatov@tarantool.org>

1. Removed zName, is_nullable, collation, type
from SQL Column.
2. Removed zColumns, zName from SQL Table.
3. Refactored Parser to use def_expression directly.
4. Introduced is_view flag.
4. Introduced sql_table_def_rebuild intended for collect
fragmented with sql_field_retrieve space_def into memory
located in one allocation.

Part of #3272, #3218.
---
 src/box/field_def.h     |   6 +
 src/box/space_def.c     |  29 +++--
 src/box/sql.c           | 128 +++++++++++++++----
 src/box/sql.h           |  33 +++++
 src/box/sql/alter.c     |  55 +++++---
 src/box/sql/analyze.c   |  16 ++-
 src/box/sql/build.c     | 334 ++++++++++++++++++++++++++----------------------
 src/box/sql/delete.c    |  21 +--
 src/box/sql/expr.c      |  14 +-
 src/box/sql/fkey.c      |  38 +++---
 src/box/sql/hash.c      |   5 +-
 src/box/sql/hash.h      |   2 +-
 src/box/sql/insert.c    |  67 +++++-----
 src/box/sql/pragma.c    |  34 +++--
 src/box/sql/prepare.c   |  45 +++----
 src/box/sql/resolve.c   |  22 ++--
 src/box/sql/select.c    | 159 +++++++++++++----------
 src/box/sql/sqliteInt.h |  37 ++++--
 src/box/sql/tokenize.c  |   5 +-
 src/box/sql/treeview.c  |   2 +-
 src/box/sql/trigger.c   |   7 +-
 src/box/sql/update.c    |  33 ++---
 src/box/sql/util.c      |   9 --
 src/box/sql/vdbe.c      |   2 +-
 src/box/sql/vdbeaux.c   |   9 +-
 src/box/sql/where.c     |  12 +-
 src/box/sql/wherecode.c |   4 +-
 src/box/sql/whereexpr.c |   6 +-
 28 files changed, 675 insertions(+), 459 deletions(-)

diff --git a/src/box/field_def.h b/src/box/field_def.h
index dfc1950..a42beab 100644
--- a/src/box/field_def.h
+++ b/src/box/field_def.h
@@ -116,6 +116,12 @@ struct field_def {
 	struct Expr *default_value_expr;
 };
 
+static inline bool
+nullable_action_to_is_nullable(enum on_conflict_action nullable_action)
+{
+	return nullable_action == ON_CONFLICT_ACTION_NONE;
+}
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/space_def.c b/src/box/space_def.c
index 22bd3ca..77c0e02 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -70,11 +70,12 @@ space_def_sizeof(uint32_t name_len, const struct field_def *fields,
 	for (uint32_t i = 0; i < field_count; ++i) {
 		field_strs_size += strlen(fields[i].name) + 1;
 		if (fields[i].default_value != NULL) {
-			assert(fields[i].default_value_expr != NULL);
 			int len = strlen(fields[i].default_value);
 			field_strs_size += len + 1;
-			struct Expr *e = fields[i].default_value_expr;
-			def_exprs_size += sql_expr_sizeof(e, 0);
+			if (fields[i].default_value_expr != NULL) {
+				struct Expr *e = fields[i].default_value_expr;
+				def_exprs_size += sql_expr_sizeof(e, 0);
+			}
 		}
 	}
 
@@ -116,12 +117,13 @@ space_def_dup(const struct space_def *src)
 			if (src->fields[i].default_value != NULL) {
 				ret->fields[i].default_value = strs_pos;
 				strs_pos += strlen(strs_pos) + 1;
-
-				struct Expr *e =
-					src->fields[i].default_value_expr;
-				assert(e != NULL);
+			}
+			struct Expr *e =
+				src->fields[i].default_value_expr;
+			if (e != NULL) {
 				char *expr_pos_old = expr_pos;
-				e = sql_expr_dup(sql_get(), e, 0, &expr_pos);
+				e = sql_expr_dup(sql_get(), e, 0,
+						 &expr_pos);
 				assert(e != NULL);
 				/* Note: due to SQL legacy
 				 * duplicactor pointer is not
@@ -201,12 +203,13 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
 				       fields[i].default_value, len);
 				def->fields[i].default_value[len] = 0;
 				strs_pos += len + 1;
-
-				struct Expr *e =
-					fields[i].default_value_expr;
-				assert(e != NULL);
+			}
+			struct Expr *e =
+				fields[i].default_value_expr;
+			if (e != NULL) {
 				char *expr_pos_old = expr_pos;
-				e = sql_expr_dup(sql_get(), e, 0, &expr_pos);
+				e = sql_expr_dup(sql_get(), e, 0,
+						 &expr_pos);
 				assert(e != NULL);
 				/* Note: due to SQL legacy
 				 * duplicactor pointer is
diff --git a/src/box/sql.c b/src/box/sql.c
index 166bb71..7d48cdc 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1430,10 +1430,12 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 {
 	struct Column *aCol = pTable->aCol;
 	const struct Enc *enc = get_enc(buf);
+	const struct space_def *def = pTable->def;
+	assert(def != NULL);
 	struct SqliteIndex *pk_idx = sqlite3PrimaryKeyIndex(pTable);
 	int pk_forced_int = -1;
 	char *base = buf, *p;
-	int i, n = pTable->nCol;
+	int i, n = def->field_count;
 
 	p = enc->encode_array(base, n);
 
@@ -1441,47 +1443,53 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 	 * treat it as strict type, not affinity.  */
 	if (pk_idx && pk_idx->nColumn == 1) {
 		int pk = pk_idx->aiColumn[0];
-		if (pTable->aCol[pk].type == FIELD_TYPE_INTEGER)
+		if (def->fields[pk].type == FIELD_TYPE_INTEGER)
 			pk_forced_int = pk;
 	}
 
 	for (i = 0; i < n; i++) {
 		const char *t;
-		struct coll *coll = aCol[i].coll;
-		struct field_def *field = &pTable->def->fields[i];
-		struct Expr *def = field->default_value_expr;
+		struct coll *coll =
+			coll_by_id(pTable->def->fields[i].coll_id);
+
+		struct field_def *field = &def->fields[i];
+		const char *zToken = field->default_value;
 		int base_len = 4;
 		if (coll != NULL)
 			base_len += 1;
-		if (def != NULL)
+		if (zToken != NULL)
 			base_len += 1;
 		p = enc->encode_map(p, base_len);
 		p = enc->encode_str(p, "name", 4);
-		p = enc->encode_str(p, aCol[i].zName, strlen(aCol[i].zName));
+		p = enc->encode_str(p, field->name, strlen(field->name));
 		p = enc->encode_str(p, "type", 4);
+
+		assert(def->fields[i].is_nullable ==
+			nullable_action_to_is_nullable(
+				def->fields[i].nullable_action));
+
 		if (i == pk_forced_int) {
 			t = "integer";
 		} else {
 			t = aCol[i].affinity == SQLITE_AFF_BLOB ? "scalar" :
-				convertSqliteAffinity(aCol[i].affinity, aCol[i].notNull == 0);
+				convertSqliteAffinity(aCol[i].affinity,
+						      def->fields[i].is_nullable);
 		}
 		p = enc->encode_str(p, t, strlen(t));
 		p = enc->encode_str(p, "is_nullable", 11);
-		p = enc->encode_bool(p, aCol[i].notNull ==
-				     ON_CONFLICT_ACTION_NONE);
+		p = enc->encode_bool(p, def->fields[i].is_nullable);
 		p = enc->encode_str(p, "nullable_action", 15);
-		assert(aCol[i].notNull < on_conflict_action_MAX);
-		const char *action = on_conflict_action_strs[aCol[i].notNull];
+		assert(def->fields[i].nullable_action < on_conflict_action_MAX);
+		const char *action =
+			on_conflict_action_strs[def->fields[i].nullable_action];
 		p = enc->encode_str(p, action, strlen(action));
 		if (coll != NULL) {
 			p = enc->encode_str(p, "collation", strlen("collation"));
 			p = enc->encode_uint(p, coll->id);
 		}
-		if (def != NULL) {
-		        assert((def->flags & EP_IntValue) == 0);
-			assert(def->u.zToken != NULL);
-			p = enc->encode_str(p, "default", strlen("default"));
-			p = enc->encode_str(p, def->u.zToken, strlen(def->u.zToken));
+		if (zToken != NULL) {
+		        p = enc->encode_str(p, "default", strlen("default"));
+			p = enc->encode_str(p, zToken, strlen(zToken));
 		}
 	}
 	return (int)(p - base);
@@ -1496,13 +1504,12 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
  */
 int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
 {
-	(void)pTable;
 	const struct Enc *enc = get_enc(buf);
 	char *base = buf, *p;
 
 	bool is_view = false;
 	if (pTable != NULL)
-		is_view = pTable->pSelect != NULL;
+		is_view = pTable->def->opts.is_view;
 	p = enc->encode_map(base, is_view ? 2 : 1);
 	p = enc->encode_str(p, "sql", 3);
 	p = enc->encode_str(p, zSql, strlen(zSql));
@@ -1523,6 +1530,9 @@ int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
 int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 {
 	struct Column *aCol = pIndex->pTable->aCol;
+	struct space_def *def = pIndex->pTable->def;
+	assert(def != NULL);
+
 	const struct Enc *enc = get_enc(buf);
 	struct SqliteIndex *primary_index;
 	char *base = buf, *p;
@@ -1534,7 +1544,7 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 	 * treat it as strict type, not affinity.  */
 	if (primary_index->nColumn == 1) {
 		int pk = primary_index->aiColumn[0];
-		if (aCol[pk].type == FIELD_TYPE_INTEGER)
+		if (def->fields[pk].type == FIELD_TYPE_INTEGER)
 			pk_forced_int = pk;
 	}
 
@@ -1549,11 +1559,16 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 	p = enc->encode_array(base, n);
 	for (i = 0; i < n; i++) {
 		int col = pIndex->aiColumn[i];
+		assert(def->fields[col].is_nullable ==
+			nullable_action_to_is_nullable(
+				def->fields[col].nullable_action));
 		const char *t;
-		if (pk_forced_int == col)
+		if (pk_forced_int == col) {
 			t = "integer";
-		else
-			t = convertSqliteAffinity(aCol[col].affinity, aCol[col].notNull == 0);
+		} else {
+			t = convertSqliteAffinity(aCol[col].affinity,
+						  def->fields[col].is_nullable);
+		}
 		/* do not decode default collation */
 		p = enc->encode_map(p, pIndex->coll_array[i] == NULL ? 4 : 5);
 		p = enc->encode_str(p, "type", sizeof("type")-1);
@@ -1565,9 +1580,10 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 			p = enc->encode_uint(p, pIndex->coll_array[i]->id);
 		}
 		p = enc->encode_str(p, "is_nullable", 11);
-		p = enc->encode_bool(p, aCol[col].notNull == ON_CONFLICT_ACTION_NONE);
+		p = enc->encode_bool(p, def->fields[col].is_nullable);
 		p = enc->encode_str(p, "nullable_action", 15);
-		const char *action_str = on_conflict_action_strs[aCol[col].notNull];
+		const char *action_str =
+			on_conflict_action_strs[def->fields[col].nullable_action];
 		p = enc->encode_str(p, action_str, strlen(action_str));
 	}
 	return (int)(p - base);
@@ -1681,3 +1697,65 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
 
 	return space->def->fields[fieldno].default_value_expr;
 }
+
+struct space_def *
+sql_ephemeral_space_def_new(Parse *parser, const char *name)
+{
+	struct space_def *def = NULL;
+	struct region *region = &fiber()->gc;
+	size_t name_len = name != NULL ? strlen(name) : 0;
+	size_t size = sizeof(struct space_def) + name_len + 1;
+	def = (struct space_def *)region_alloc(region, size);
+	if (def == NULL) {
+		diag_set(OutOfMemory, sizeof(struct tuple_dictionary),
+			"region_alloc", "sql_ephemeral_space_def_new");
+		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->nErr++;
+		return NULL;
+	} else {
+		memset(def, 0, size);
+	}
+	memcpy(def->name, name, name_len);
+	def->name[name_len] = '\0';
+	def->opts.temporary = true;
+	return def;
+}
+
+Table *
+sql_ephemeral_table_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->def = def;
+	return table;
+}
+
+int
+sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
+{
+	assert(pTable->def->opts.temporary);
+
+	/* All allocations are on region. */
+	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;
+	}
+	pTable->def = new_def;
+	pTable->def->opts.temporary = false;
+	return 0;
+}
diff --git a/src/box/sql.h b/src/box/sql.h
index db92d80..ac8d07a 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -65,6 +65,7 @@ sql_get();
 struct Expr;
 struct Parse;
 struct Select;
+struct Table;
 
 /**
  * Perform parsing of provided expression. This is done by
@@ -143,6 +144,38 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer);
 void
 sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
 
+/**
+ * Create and initialize a new ephemeric SQL Table object.
+ * @param parser SQL Parser object.
+ * @param name Table 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);
+
+/**
+ * Create and initialize a new ephemeric space_def object.
+ * @param parser SQL Parser object.
+ * @param name Table 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. Fields and strings are expected to be allocated with
+ * sqlite3DbMalloc.
+ * @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);
+
 #if defined(__cplusplus)
 } /* extern "C" { */
 #endif
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index 24f0965..85404ac 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -109,7 +109,7 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
 #ifndef SQLITE_OMIT_VIEW
 	if (space_is_view(pTab)) {
 		sqlite3ErrorMsg(pParse, "view %s may not be altered",
-				pTab->zName);
+				pTab->def->name);
 		goto exit_rename_table;
 	}
 #endif
@@ -144,6 +144,9 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
 void
 sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 {
+	/* This function is not implemented yet #3075. */
+	unreachable();
+
 	Table *pNew;		/* Copy of pParse->pNewTable */
 	Table *pTab;		/* Table being altered */
 	const char *zTab;	/* Table name */
@@ -160,11 +163,12 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 	pNew = pParse->pNewTable;
 	assert(pNew);
 
-	zTab = &pNew->zName[16];	/* Skip the "sqlite_altertab_" prefix on the name */
-	pCol = &pNew->aCol[pNew->nCol - 1];
+	/* Skip the "sqlite_altertab_" prefix on the name. */
+	zTab = &pNew->def->name[16];
+	pCol = &pNew->aCol[pNew->def->field_count - 1];
 	assert(pNew->def != NULL);
 	pDflt = space_column_default_expr(SQLITE_PAGENO_TO_SPACEID(pNew->tnum),
-					  pNew->nCol - 1);
+					  pNew->def->field_count - 1);
 	pTab = sqlite3HashFind(&db->pSchema->tblHash, zTab);;
 	assert(pTab);
 
@@ -195,7 +199,12 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 				"Cannot add a REFERENCES column with non-NULL default value");
 		return;
 	}
-	if (pCol->notNull && !pDflt) {
+	assert(pNew->def->fields[pNew->def->field_count - 1].is_nullable ==
+	       nullable_action_to_is_nullable(
+		pNew->def->fields[pNew->def->field_count - 1].nullable_action));
+
+	if (pNew->def->fields[pNew->def->field_count - 1].nullable_action
+	    && !pDflt) {
 		sqlite3ErrorMsg(pParse,
 				"Cannot add a NOT NULL column with default value NULL");
 		return;
@@ -227,7 +236,7 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 	(void)pColDef;
 
 	/* Reload the schema of the modified table. */
-	reloadTableSchema(pParse, pTab, pTab->zName);
+	reloadTableSchema(pParse, pTab, pTab->def->name);
 }
 
 /*
@@ -248,10 +257,13 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 void
 sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 {
+	/* This function is not implemented yet #3075. */
+	unreachable();
+
 	Table *pNew;
 	Table *pTab;
 	Vdbe *v;
-	int i;
+	uint32_t i;
 	int nAlloc;
 	sqlite3 *db = pParse->db;
 
@@ -281,25 +293,30 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 	pNew = (Table *) sqlite3DbMallocZero(db, sizeof(Table));
 	if (!pNew)
 		goto exit_begin_add_column;
+	pNew->def = space_def_dup(pTab->def);
+	if (pNew->def == NULL) {
+		sqlite3DbFree(db, pNew);
+		goto exit_begin_add_column;
+	}
 	pParse->pNewTable = pNew;
 	pNew->nTabRef = 1;
-	pNew->nCol = pTab->nCol;
-	assert(pNew->nCol > 0);
-	nAlloc = (((pNew->nCol - 1) / 8) * 8) + 8;
-	assert(nAlloc >= pNew->nCol && nAlloc % 8 == 0
-	       && nAlloc - pNew->nCol < 8);
+	assert(pNew->def->field_count > 0);
+	nAlloc = (((pNew->def->field_count - 1) / 8) * 8) + 8;
+	assert((uint32_t)nAlloc >= pNew->def->field_count && nAlloc % 8 == 0
+	       && nAlloc - pNew->def->field_count < 8);
 	pNew->aCol =
 	    (Column *) sqlite3DbMallocZero(db, sizeof(Column) * nAlloc);
-	pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
-	if (!pNew->aCol || !pNew->zName) {
+	/* FIXME: pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); */
+	/* FIXME: if (!pNew->aCol || !pNew->zName) { */
+	if (!pNew->aCol) {
 		assert(db->mallocFailed);
 		goto exit_begin_add_column;
 	}
-	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->nCol);
-	for (i = 0; i < pNew->nCol; i++) {
-		Column *pCol = &pNew->aCol[i];
-		pCol->zName = sqlite3DbStrDup(db, pCol->zName);
-		pCol->coll = NULL;
+	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
+	for (i = 0; i < pNew->def->field_count; i++) {
+		/* FIXME: Column *pCol = &pNew->aCol[i]; */
+		/* FIXME: pCol->zName = sqlite3DbStrDup(db, pCol->zName); */
+		/* FIXME: pCol->coll = NULL; */
 	}
 	pNew->pSchema = db->pSchema;
 	pNew->addColOffset = pTab->addColOffset;
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index f0054c5..2722c08 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -828,7 +828,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 		return;
 	}
 	assert(pTab->tnum != 0);
-	if (sqlite3_strlike("\\_%", pTab->zName, '\\') == 0) {
+	if (sqlite3_strlike("\\_%", pTab->def->name, '\\') == 0) {
 		/* Do not gather statistics on system tables */
 		return;
 	}
@@ -842,7 +842,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 	iIdxCur = iTab++;
 	pParse->nTab = MAX(pParse->nTab, iTab);
 	sqlite3OpenTable(pParse, iTabCur, pTab, OP_OpenRead);
-	sqlite3VdbeLoadString(v, regTabname, pTab->zName);
+	sqlite3VdbeLoadString(v, regTabname, pTab->def->name);
 
 	for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
 		int addrRewind;	/* Address of "OP_Rewind iIdxCur" */
@@ -857,7 +857,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 		 * instead more familiar table name.
 		 */
 		if (IsPrimaryKeyIndex(pIdx)) {
-			zIdxName = pTab->zName;
+			zIdxName = pTab->def->name;
 		} else {
 			zIdxName = pIdx->zName;
 		}
@@ -865,7 +865,8 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 
 		/* Populate the register containing the index name. */
 		sqlite3VdbeLoadString(v, regIdxname, zIdxName);
-		VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName));
+		VdbeComment((v, "Analysis for %s.%s", pTab->def->name,
+			    zIdxName));
 
 		/*
 		 * Pseudo-code for loop that calls stat_push():
@@ -1023,9 +1024,10 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 		regKeyStat = sqlite3GetTempRange(pParse, nPkColumn);
 		for (j = 0; j < nPkColumn; j++) {
 			k = pPk->aiColumn[j];
-			assert(k >= 0 && k < pTab->nCol);
+			assert(k >= 0 && k < (int)pTab->def->field_count);
 			sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKeyStat + j);
-			VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
+			VdbeComment((v, "%s",
+				    pTab->def->fields[pPk->aiColumn[j]].name));
 		}
 		sqlite3VdbeAddOp3(v, OP_MakeRecord, regKeyStat,
 				  nPkColumn, regKey);
@@ -1145,7 +1147,7 @@ analyzeTable(Parse * pParse, Table * pTab, Index * pOnlyIdx)
 	if (pOnlyIdx) {
 		openStatTable(pParse, iStatCur, pOnlyIdx->zName, "idx");
 	} else {
-		openStatTable(pParse, iStatCur, pTab->zName, "tbl");
+		openStatTable(pParse, iStatCur, pTab->def->name, "tbl");
 	}
 	analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem + 1,
 			pParse->nTab);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a2b712a..a02fe89 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -291,15 +291,8 @@ sqlite3CommitInternalChanges()
 void
 sqlite3DeleteColumnNames(sqlite3 * db, Table * pTable)
 {
-	int i;
-	Column *pCol;
 	assert(pTable != 0);
-	if ((pCol = pTable->aCol) != 0) {
-		for (i = 0; i < pTable->nCol; i++, pCol++) {
-			sqlite3DbFree(db, pCol->zName);
-		}
-		sqlite3DbFree(db, pTable->aCol);
-	}
+	sqlite3DbFree(db, pTable->aCol);
 }
 
 /*
@@ -389,16 +382,13 @@ deleteTable(sqlite3 * db, Table * pTable)
 	 */
 	sqlite3HashClear(&pTable->idxHash);
 	sqlite3DeleteColumnNames(db, pTable);
-	sqlite3DbFree(db, pTable->zName);
 	sqlite3DbFree(db, pTable->zColAff);
 	sqlite3SelectDelete(db, pTable->pSelect);
 	sqlite3ExprListDelete(db, pTable->pCheck);
-	if (pTable->def != NULL) {
-		/* Fields has been allocated independently. */
-		struct field_def *fields = pTable->def->fields;
+	/* Do not delete pTable->def allocated not on region. */
+	assert(pTable->def != NULL);
+	if (!pTable->def->opts.temporary)
 		space_def_delete(pTable->def);
-		sqlite3DbFree(db, fields);
-	}
 	sqlite3DbFree(db, pTable);
 
 	/* Verify that no lookaside memory was used by schema tables */
@@ -494,31 +484,19 @@ sqlite3PrimaryKeyIndex(Table * pTab)
 
 /**
  * Create and initialize a new SQL Table object.
+ * All memory is allocated on region.
  * @param parser SQL Parser object.
  * @param name Table to create name.
- * @retval NULL on memory allocation error, Parser state is
- *         changed.
+ * @retval NULL on memory allocation error.
  * @retval not NULL on success.
  */
 static Table *
 sql_table_new(Parse *parser, char *name)
 {
 	sqlite3 *db = parser->db;
-
-	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
-	struct space_def *def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
-					      &space_opts_default, NULL, 0);
-	if (table == NULL || def == NULL) {
-		if (def != NULL)
-			space_def_delete(def);
-		sqlite3DbFree(db, table);
-		parser->rc = SQLITE_NOMEM_BKPT;
-		parser->nErr++;
+	struct Table *table = sql_ephemeral_table_new(parser, name);
+	if (table == NULL)
 		return NULL;
-	}
-
-	table->def = def;
-	table->zName = name;
 	table->iPKey = -1;
 	table->iAutoIncPKey = -1;
 	table->pSchema = db->pSchema;
@@ -568,10 +546,11 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 	}
 
 	zName = sqlite3NameFromToken(db, pName);
-
 	pParse->sNameToken = *pName;
 	if (zName == 0)
 		return;
+	if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK)
+		goto begin_table_error;
 
 	assert(db->pSchema != NULL);
 	pTable = sqlite3HashFind(&db->pSchema->tblHash, zName);
@@ -626,30 +605,35 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 static struct field_def *
 sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
 {
-	sqlite3 *db = parser->db;
 	struct field_def *field;
 	assert(table->def != NULL);
-	assert(table->def->exact_field_count >= (uint32_t)table->nCol);
 	assert(id < SQLITE_MAX_COLUMN);
 
 	if (id >= table->def->exact_field_count) {
-		uint32_t columns = table->def->exact_field_count;
-		columns = (columns > 0) ? 2 * columns : 1;
-		field = sqlite3DbRealloc(db, table->def->fields,
-					 columns * sizeof(table->def->fields[0]));
+		uint32_t columns_new = table->def->exact_field_count;
+		columns_new = (columns_new > 0) ? 2 * columns_new : 1;
+		struct region *region = &fiber()->gc;
+		field = region_alloc(region, columns_new *
+				     sizeof(table->def->fields[0]));
 		if (field == NULL) {
-			parser->rc = SQLITE_NOMEM_BKPT;
+			diag_set(OutOfMemory, columns_new * sizeof(table->def->fields[0]),
+				"region_alloc", "sql_field_retrieve");
+			parser->rc = SQL_TARANTOOL_ERROR;
 			parser->nErr++;
 			return NULL;
 		}
 
-		for (uint32_t i = columns / 2; i < columns; i++) {
+		for (uint32_t i = 0; i < table->def->exact_field_count; i++) {
+			memcpy(&field[i], &table->def->fields[i],
+			       sizeof(struct field_def));
+		}
+		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;
+		table->def->exact_field_count = columns_new;
 	}
 
 	field = &table->def->fields[id];
@@ -675,42 +659,44 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	sqlite3 *db = pParse->db;
 	if ((p = pParse->pNewTable) == 0)
 		return;
+	assert(p->def->opts.temporary);
 #if SQLITE_MAX_COLUMN
-	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
-		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
+	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
+		sqlite3ErrorMsg(pParse, "too many columns on %s",
+				p->def->name);
 		return;
 	}
 #endif
-	if (sql_field_retrieve(pParse, p, (uint32_t) p->nCol) == NULL)
+	if (sql_field_retrieve(pParse, p, (uint32_t) p->def->field_count) == NULL)
 		return;
-	z = sqlite3DbMallocRaw(db, pName->n + 1);
+	struct region *region = &fiber()->gc;
+	z = region_alloc(region, pName->n + 1);
 	if (z == 0)
 		return;
 	memcpy(z, pName->z, pName->n);
 	z[pName->n] = 0;
 	sqlite3NormalizeName(z);
-	for (i = 0; i < p->nCol; i++) {
-		if (strcmp(z, p->aCol[i].zName) == 0) {
+	for (i = 0; i < (int)p->def->field_count; i++) {
+		if (strcmp(z, p->def->fields[i].name) == 0) {
 			sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
-			sqlite3DbFree(db, z);
 			return;
 		}
 	}
-	if ((p->nCol & 0x7) == 0) {
+	if ((p->def->field_count & 0x7) == 0) {
 		Column *aNew;
 		aNew =
 		    sqlite3DbRealloc(db, p->aCol,
-				     (p->nCol + 8) * sizeof(p->aCol[0]));
-		if (aNew == 0) {
-			sqlite3DbFree(db, z);
+				     (p->def->field_count + 8) * sizeof(p->aCol[0]));
+		if (aNew == NULL)
 			return;
-		}
 		p->aCol = aNew;
 	}
-	pCol = &p->aCol[p->nCol];
+	pCol = &p->aCol[p->def->field_count];
 	memset(pCol, 0, sizeof(p->aCol[0]));
-	pCol->zName = z;
-
+	struct field_def *column_def = &p->def->fields[p->def->field_count];
+	column_def->name = z;
+	column_def->is_nullable = true;
+	column_def->nullable_action = ON_CONFLICT_ACTION_NONE;
 	if (pType->n == 0) {
 		/* If there is no type specified, columns have the default affinity
 		 * 'BLOB' and type SCALAR.
@@ -718,8 +704,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		 * specified type, the code below should emit an error.
 		 */
 		pCol->affinity = SQLITE_AFF_BLOB;
-		pCol->type = FIELD_TYPE_SCALAR;
 		pCol->szEst = 1;
+		column_def->type = FIELD_TYPE_SCALAR;
 	} else {
 		/* TODO: convert string of type into runtime
 		 * FIELD_TYPE value for other types.
@@ -728,19 +714,18 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		     pType->n == 7) ||
 		    (sqlite3StrNICmp(pType->z, "INT", 3) == 0 &&
 		     pType->n == 3)) {
-			pCol->type = FIELD_TYPE_INTEGER;
 			pCol->affinity = SQLITE_AFF_INTEGER;
+			column_def->type = FIELD_TYPE_INTEGER;
 		} else {
 			zType = sqlite3_malloc(pType->n + 1);
 			memcpy(zType, pType->z, pType->n);
 			zType[pType->n] = 0;
 			sqlite3Dequote(zType);
 			pCol->affinity = sqlite3AffinityType(zType, 0);
-			pCol->type = FIELD_TYPE_SCALAR;
 			sqlite3_free(zType);
+			column_def->type = FIELD_TYPE_SCALAR;
 		}
 	}
-	p->nCol++;
 	p->def->field_count++;
 	pParse->constraintName.n = 0;
 }
@@ -756,9 +741,11 @@ sqlite3AddNotNull(Parse * pParse, int onError)
 {
 	Table *p;
 	p = pParse->pNewTable;
-	if (p == 0 || NEVER(p->nCol < 1))
+	if (p == 0 || NEVER(p->def->field_count < 1))
 		return;
-	p->aCol[p->nCol - 1].notNull = (u8) onError;
+	p->def->fields[p->def->field_count - 1].nullable_action = (u8)onError;
+	p->def->fields[p->def->field_count - 1].is_nullable =
+		(onError == ON_CONFLICT_ACTION_NONE);
 }
 
 /*
@@ -871,38 +858,31 @@ void
 sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 {
 	Table *p;
-	Column *pCol;
 	sqlite3 *db = pParse->db;
 	p = pParse->pNewTable;
+	assert(p->def->opts.temporary);
 	if (p != 0) {
-		pCol = &(p->aCol[p->nCol - 1]);
 		if (!sqlite3ExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
 			sqlite3ErrorMsg(pParse,
 					"default value of column [%s] is not constant",
-					pCol->zName);
+					p->def->fields[p->def->field_count - 1].name);
 		} else {
-			/* A copy of pExpr is used instead of the original, as pExpr contains
-			 * tokens that point to volatile memory. The 'span' of the expression
-			 * is required by pragma table_info.
-			 */
-			Expr x;
 			assert(p->def != NULL);
 			struct field_def *field =
-				&p->def->fields[p->nCol - 1];
-			sql_expr_free(db, field->default_value_expr, false);
-
-			memset(&x, 0, sizeof(x));
-			x.op = TK_SPAN;
-			x.u.zToken = sqlite3DbStrNDup(db, (char *)pSpan->zStart,
-						      (int)(pSpan->zEnd -
-							    pSpan->zStart));
-			x.pLeft = pSpan->pExpr;
-			x.flags = EP_Skip;
-
-			field->default_value_expr =
-				sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
-			sqlite3DbFree(db, x.u.zToken);
+				&p->def->fields[p->def->field_count - 1];
+			struct region *region = &fiber()->gc;
+			uint32_t default_length = (int)(pSpan->zEnd - pSpan->zStart);
+			field->default_value = region_alloc(region,
+							    default_length + 1);
+			if (field->default_value == NULL) {
+				pParse->rc = SQLITE_NOMEM_BKPT;
+				pParse->nErr++;
+				return;
+			}
+			strncpy(field->default_value, (char *)pSpan->zStart,
+				default_length);
+			field->default_value[default_length] = '\0';
 		}
 	}
 	sql_expr_free(db, pSpan->pExpr, false);
@@ -942,12 +922,12 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 	if (pTab->tabFlags & TF_HasPrimaryKey) {
 		sqlite3ErrorMsg(pParse,
 				"table \"%s\" has more than one primary key",
-				pTab->zName);
+				pTab->def->name);
 		goto primary_key_exit;
 	}
 	pTab->tabFlags |= TF_HasPrimaryKey;
 	if (pList == 0) {
-		iCol = pTab->nCol - 1;
+		iCol = pTab->def->field_count - 1;
 		pCol = &pTab->aCol[iCol];
 		pCol->is_primkey = 1;
 		nTerm = 1;
@@ -962,10 +942,10 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 				goto primary_key_exit;
 			}
 			const char *zCName = pCExpr->u.zToken;
-			for (iCol = 0; iCol < pTab->nCol; iCol++) {
+			for (iCol = 0; iCol < (int)pTab->def->field_count; iCol++) {
 				if (strcmp
 				    (zCName,
-				     pTab->aCol[iCol].zName) == 0) {
+				     pTab->def->fields[iCol].name) == 0) {
 					pCol = &pTab->aCol[iCol];
 					pCol->is_primkey = 1;
 					break;
@@ -973,9 +953,10 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 			}
 		}
 	}
+	assert(pCol == &pTab->aCol[iCol]);
 	if (nTerm == 1
 	    && pCol
-	    && (sqlite3ColumnType(pCol) == FIELD_TYPE_INTEGER)
+	    && (pTab->def->fields[iCol].type == FIELD_TYPE_INTEGER)
 	    && sortOrder != SQLITE_SO_DESC) {
 		assert(autoInc == 0 || autoInc == 1);
 		pTab->iPKey = iCol;
@@ -1036,16 +1017,16 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 
 	if ((p = pParse->pNewTable) == 0)
 		return;
-	i = p->nCol - 1;
+	i = p->def->field_count - 1;
 	db = pParse->db;
 	zColl = sqlite3NameFromToken(db, pToken);
 	if (!zColl)
 		return;
 
-	struct coll *coll = sqlite3LocateCollSeq(pParse, db, zColl);
+	struct coll *coll = coll_by_name(zColl, strlen(zColl));
 	if (coll != NULL) {
 		Index *pIdx;
-		p->aCol[i].coll = coll;
+		p->def->fields[i].coll_id = coll->id;
 
 		/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
 		 * then an index may have been created on this column before the
@@ -1056,9 +1037,8 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 			if (pIdx->aiColumn[0] == i)
 				pIdx->coll_array[0] = sql_column_collation(p, i);
 		}
-	} else {
-		sqlite3DbFree(db, zColl);
 	}
+	sqlite3DbFree(db, zColl);
 }
 
 /**
@@ -1087,8 +1067,10 @@ sql_column_collation(Table *table, uint32_t column)
 	 * SQL specific structures.
 	 */
 	if (space == NULL || space_index(space, 0) == NULL) {
-		assert(column < (uint32_t)table->nCol);
-		return table->aCol[column].coll;
+		assert(column < (uint32_t)table->def->field_count);
+		struct coll *coll =
+			coll_by_id(table->def->fields[column].coll_id);
+		return coll;
 	}
 
 	return space->format->fields[column].coll;
@@ -1306,10 +1288,9 @@ createTableStmt(sqlite3 * db, Table * p)
 	char *zSep, *zSep2, *zEnd;
 	Column *pCol;
 	n = 0;
-	for (pCol = p->aCol, i = 0; i < p->nCol; i++, pCol++) {
-		n += identLength(pCol->zName) + 5;
-	}
-	n += identLength(p->zName);
+	for (i = 0; i < (int)p->def->field_count; i++)
+		n += identLength(p->def->fields[i].name) + 5;
+	n += identLength(p->def->name);
 	if (n < 50) {
 		zSep = "";
 		zSep2 = ",";
@@ -1319,7 +1300,7 @@ createTableStmt(sqlite3 * db, Table * p)
 		zSep2 = ",\n  ";
 		zEnd = "\n)";
 	}
-	n += 35 + 6 * p->nCol;
+	n += 35 + 6 * p->def->field_count;
 	zStmt = sqlite3DbMallocRaw(0, n);
 	if (zStmt == 0) {
 		sqlite3OomFault(db);
@@ -1327,9 +1308,9 @@ createTableStmt(sqlite3 * db, Table * p)
 	}
 	sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
 	k = sqlite3Strlen30(zStmt);
-	identPut(zStmt, &k, p->zName);
+	identPut(zStmt, &k, p->def->name);
 	zStmt[k++] = '(';
-	for (pCol = p->aCol, i = 0; i < p->nCol; i++, pCol++) {
+	for (pCol = p->aCol, i = 0; i < (int)p->def->field_count; i++, pCol++) {
 		static const char *const azType[] = {
 			/* SQLITE_AFF_BLOB    */ "",
 			/* SQLITE_AFF_TEXT    */ " TEXT",
@@ -1343,7 +1324,7 @@ createTableStmt(sqlite3 * db, Table * p)
 		sqlite3_snprintf(n - k, &zStmt[k], zSep);
 		k += sqlite3Strlen30(&zStmt[k]);
 		zSep = zSep2;
-		identPut(zStmt, &k, pCol->zName);
+		identPut(zStmt, &k, p->def->fields[i].name);
 		assert(pCol->affinity - SQLITE_AFF_BLOB >= 0);
 		assert(pCol->affinity - SQLITE_AFF_BLOB < ArraySize(azType));
 		testcase(pCol->affinity == SQLITE_AFF_BLOB);
@@ -1373,7 +1354,8 @@ estimateTableWidth(Table * pTab)
 	unsigned wTable = 0;
 	const Column *pTabCol;
 	int i;
-	for (i = pTab->nCol, pTabCol = pTab->aCol; i > 0; i--, pTabCol++) {
+	for (i = pTab->def->field_count,
+		     pTabCol = pTab->aCol; i > 0; i--, pTabCol++) {
 		wTable += pTabCol->szEst;
 	}
 	if (pTab->iPKey < 0)
@@ -1392,7 +1374,7 @@ estimateIndexWidth(Index * pIdx)
 	const Column *aCol = pIdx->pTable->aCol;
 	for (i = 0; i < pIdx->nColumn; i++) {
 		i16 x = pIdx->aiColumn[i];
-		assert(x < pIdx->pTable->nCol);
+		assert(x < (int)pIdx->pTable->def->field_count);
 		wIndex += x < 0 ? 1 : aCol[pIdx->aiColumn[i]].szEst;
 	}
 	pIdx->szIdxRow = sqlite3LogEst(wIndex * 4);
@@ -1433,9 +1415,11 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)
 	/* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
 	 */
 	if (!db->init.imposterTable) {
-		for (i = 0; i < pTab->nCol; i++) {
+		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			if (pTab->aCol[i].is_primkey) {
-				pTab->aCol[i].notNull = ON_CONFLICT_ACTION_ABORT;
+				pTab->def->fields[i].nullable_action
+					= ON_CONFLICT_ACTION_ABORT;
+				pTab->def->fields[i].is_nullable = false;
 			}
 		}
 	}
@@ -1446,7 +1430,7 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)
 	if (pTab->iPKey >= 0) {
 		ExprList *pList;
 		Token ipkToken;
-		sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName);
+		sqlite3TokenInit(&ipkToken, pTab->def->fields[pTab->iPKey].name);
 		pList = sqlite3ExprListAppend(pParse, 0,
 					      sqlite3ExprAlloc(db, TK_ID,
 							       &ipkToken, 0));
@@ -1660,10 +1644,10 @@ 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, p->zName), P4_DYNAMIC);
+			  sqlite3DbStrDup(pParse->db, p->def->name), P4_DYNAMIC);
 	sqlite3VdbeAddOp4(v, OP_String8, 0, iFirstCol + 3 /* engine */ , 0,
 			  "memtx", P4_STATIC);
-	sqlite3VdbeAddOp2(v, OP_Integer, p->nCol,
+	sqlite3VdbeAddOp2(v, OP_Integer, p->def->field_count,
 			  iFirstCol + 4 /* field_count */ );
 	sqlite3VdbeAddOp4(v, OP_Blob, zOptsSz, iFirstCol + 5, MSGPACK_SUBTYPE,
 			  zOpts, P4_DYNAMIC);
@@ -1726,9 +1710,9 @@ parseTableSchemaRecord(Parse * pParse, int iSpaceId, char *zStmt)
 	int i, iTop = pParse->nMem + 1;
 	pParse->nMem += 4;
 
-	sqlite3VdbeAddOp4(v,
-			  OP_String8, 0, iTop, 0,
-			  sqlite3DbStrDup(pParse->db, p->zName), P4_DYNAMIC);
+	sqlite3VdbeAddOp4(v, OP_String8, 0, iTop, 0,
+			  sqlite3DbStrDup(pParse->db, p->def->name),
+					  P4_DYNAMIC);
 	sqlite3VdbeAddOp2(v, OP_SCopy, iSpaceId, iTop + 1);
 	sqlite3VdbeAddOp2(v, OP_Integer, 0, iTop + 2);
 	sqlite3VdbeAddOp4(v, OP_String8, 0, iTop + 3, 0, zStmt, P4_DYNAMIC);
@@ -1853,7 +1837,6 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	p = pParse->pNewTable;
 	if (p == 0)
 		return;
-
 	assert(!db->init.busy || !pSelect);
 
 	/* If db->init.busy == 1, then we're called from
@@ -1863,17 +1846,21 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (db->init.busy)
 		p->tnum = db->init.newTnum;
 
-	if (!p->pSelect) {
+	assert(p->def->opts.is_view == (p->pSelect != NULL));
+	if (!p->def->opts.is_view) {
 		if ((p->tabFlags & TF_HasPrimaryKey) == 0) {
 			sqlite3ErrorMsg(pParse,
 					"PRIMARY KEY missing on table %s",
-					p->zName);
+					p->def->name);
 			return;
 		} else {
 			convertToWithoutRowidTable(pParse, p);
 		}
 	}
 
+	if (sql_table_def_rebuild(db, p) != 0)
+		return;
+
 #ifndef SQLITE_OMIT_CHECK
 	/* Resolve names in all CHECK constraint expressions.
 	 */
@@ -1908,7 +1895,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		/*
 		 * Initialize zType for the new view or table.
 		 */
-		if (p->pSelect == 0) {
+		assert(p->def->opts.is_view == (p->pSelect != NULL));
+		if (!p->def->opts.is_view) {
 			/* A regular table */
 			zType = "TABLE";
 #ifndef SQLITE_OMIT_VIEW
@@ -1937,7 +1925,9 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		if (pSelect) {
 			zStmt = createTableStmt(db, p);
 		} else {
-			Token *pEnd2 = p->pSelect ? &pParse->sLastToken : pEnd;
+			assert(p->def->opts.is_view == (p->pSelect != NULL));
+			Token *pEnd2 = p->def->opts.is_view ?
+				       &pParse->sLastToken : pEnd;
 			n = (int)(pEnd2->z - pParse->sNameToken.z);
 			if (pEnd2->z[0] != ';')
 				n += pEnd2->n;
@@ -1949,9 +1939,9 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		iSpaceId = getNewSpaceId(pParse);
 		createSpace(pParse, iSpaceId, zStmt);
 		/* Indexes aren't required for VIEW's. */
-		if (p->pSelect == NULL) {
+		assert(p->def->opts.is_view == (p->pSelect != NULL));
+		if (!p->def->opts.is_view)
 			createImplicitIndices(pParse, iSpaceId);
-		}
 
 		/* Check to see if we need to create an _sequence table for
 		 * keeping track of autoincrement keys.
@@ -1966,7 +1956,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 
 			reg_seq_record = emitNewSysSequenceRecord(pParse,
 								  reg_seq_id,
-								  p->zName);
+								  p->def->name);
 			sqlite3VdbeAddOp2(v, OP_SInsert, BOX_SEQUENCE_ID,
 					  reg_seq_record);
 			/* Do an insertion into _space_sequence. */
@@ -1986,7 +1976,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (db->init.busy) {
 		Table *pOld;
 		Schema *pSchema = p->pSchema;
-		pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
+		pOld = sqlite3HashInsert(&pSchema->tblHash, p->def->name, p);
 		if (pOld) {
 			assert(p == pOld);	/* Malloc must have failed inside HashInsert() */
 			sqlite3OomFault(db);
@@ -1996,7 +1986,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		current_session()->sql_flags |= SQLITE_InternChanges;
 
 #ifndef SQLITE_OMIT_ALTERTABLE
-		if (!p->pSelect) {
+		assert(p->def->opts.is_view == (p->pSelect != NULL));
+		if (!p->def->opts.is_view) {
 			const char *zName = (const char *)pParse->sNameToken.z;
 			int nName;
 			assert(!pSelect && pCons && pEnd);
@@ -2048,6 +2039,7 @@ sqlite3CreateView(Parse * pParse,	/* The parsing context */
 	 * they will persist after the current sqlite3_exec() call returns.
 	 */
 	p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
+	p->def->opts.is_view = true;
 	p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
 	if (db->mallocFailed)
 		goto create_view_fail;
@@ -2101,7 +2093,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	/* A positive nCol means the columns names for this view are
 	 * already known.
 	 */
-	if (pTable->nCol > 0)
+	if (pTable->def->field_count > 0)
 		return 0;
 
 	/* A negative nCol is a special marker meaning that we are currently
@@ -2119,12 +2111,12 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	 *     CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
 	 *     SELECT * FROM temp.ex1;
 	 */
-	if (pTable->nCol < 0) {
+	if ((int)pTable->def->field_count < 0) {
 		sqlite3ErrorMsg(pParse, "view %s is circularly defined",
-				pTable->zName);
+				pTable->def->name);
 		return 1;
 	}
-	assert(pTable->nCol >= 0);
+	assert((int)pTable->def->field_count >= 0);
 
 	/* If we get this far, it means we need to compute the table names.
 	 * Note that the call to sqlite3ResultSetOfSelect() will expand any
@@ -2134,11 +2126,12 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	 * statement that defines the view.
 	 */
 	assert(pTable->pSelect);
+	assert(pTable->def->opts.is_view == (pTable->pSelect != NULL));
 	pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
 	if (pSel) {
 		n = pParse->nTab;
 		sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
-		pTable->nCol = -1;
+		pTable->def->field_count = -1;
 		db->lookaside.bDisable++;
 		pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
 		pParse->nTab = n;
@@ -2149,11 +2142,16 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * normally holds CHECK constraints on an ordinary table, but for
 			 * a VIEW it holds the list of column names.
 			 */
-			sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
-						   &pTable->nCol,
-						   &pTable->aCol);
+			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
+			struct space_def *old_def = pTable->def;
+			/* Delete it manually. */
+			old_def->opts.temporary = true;
+			if (sql_table_def_rebuild(db, pTable) != 0)
+				nErr++;
+			space_def_delete(old_def);
 			if (db->mallocFailed == 0 && pParse->nErr == 0
-			    && pTable->nCol == pSel->pEList->nExpr) {
+			    && (int)pTable->def->field_count ==
+			       pSel->pEList->nExpr) {
 				sqlite3SelectAddColumnTypeAndCollation(pParse,
 								       pTable,
 								       pSel);
@@ -2163,12 +2161,31 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * the column names from the SELECT statement that defines the view.
 			 */
 			assert(pTable->aCol == 0);
-			pTable->nCol = pSelTab->nCol;
+			assert((int)pTable->def->field_count == -1);
+			assert(pSelTab->def->opts.temporary);
+
+			struct space_def *old_def = pTable->def;
+			struct space_def *new_def = NULL;
+			new_def = sql_ephemeral_space_def_new(pParse, old_def->name);
+			if (new_def == NULL) {
+				nErr++;
+			} else {
+				memcpy(new_def, old_def, sizeof(struct space_def));
+				new_def->dict = NULL;
+				new_def->opts.temporary = true;
+				new_def->fields = pSelTab->def->fields;
+				new_def->field_count = pSelTab->def->field_count;
+				pTable->def = new_def;
+				if (sql_table_def_rebuild(db, pTable) != 0)
+					nErr++;
+			}
 			pTable->aCol = pSelTab->aCol;
-			pSelTab->nCol = 0;
-			pSelTab->aCol = 0;
+			pSelTab->aCol = NULL;
+			pSelTab->def = old_def;
+			pSelTab->def->fields = NULL;
+			pSelTab->def->field_count = 0;
 		} else {
-			pTable->nCol = 0;
+			pTable->def->field_count = 0;
 			nErr++;
 		}
 		sqlite3DeleteTable(db, pSelTab);
@@ -2193,10 +2210,22 @@ sqliteViewResetAll(sqlite3 * db)
 	for (i = sqliteHashFirst(&db->pSchema->tblHash); i;
 	     i = sqliteHashNext(i)) {
 		Table *pTab = sqliteHashData(i);
-		if (pTab->pSelect) {
+		assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+		if (pTab->def->opts.is_view) {
 			sqlite3DeleteColumnNames(db, pTab);
+			struct space_def *old_def = pTab->def;
+			assert(old_def->opts.temporary == false);
+			/* Ignore fields allocated on region */
+			pTab->def = space_def_new(old_def->id, old_def->uid,
+						  0, old_def->name,
+						  strlen(old_def->name),
+						  old_def->engine_name,
+						  strlen(old_def->engine_name),
+						  &old_def->opts,
+						  NULL, 0);
+			assert(pTab->def);
+			space_def_delete(old_def);
 			pTab->aCol = 0;
-			pTab->nCol = 0;
 		}
 	}
 }
@@ -2469,13 +2498,13 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 	if (p == 0)
 		goto fk_end;
 	if (pFromCol == 0) {
-		int iCol = p->nCol - 1;
+		int iCol = p->def->field_count - 1;
 		if (NEVER(iCol < 0))
 			goto fk_end;
 		if (pToCol && pToCol->nExpr != 1) {
 			sqlite3ErrorMsg(pParse, "foreign key on %s"
 					" should reference only one column of table %T",
-					p->aCol[iCol].zName, pTo);
+					p->def->fields[iCol].name, pTo);
 			goto fk_end;
 		}
 		nCol = 1;
@@ -2508,19 +2537,19 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 	z += pTo->n + 1;
 	pFKey->nCol = nCol;
 	if (pFromCol == 0) {
-		pFKey->aCol[0].iFrom = p->nCol - 1;
+		pFKey->aCol[0].iFrom = p->def->field_count - 1;
 	} else {
 		for (i = 0; i < nCol; i++) {
 			int j;
-			for (j = 0; j < p->nCol; j++) {
+			for (j = 0; j < (int)p->def->field_count; j++) {
 				if (strcmp
-				    (p->aCol[j].zName,
+				    (p->def->fields[j].name,
 				     pFromCol->a[i].zName) == 0) {
 					pFKey->aCol[i].iFrom = j;
 					break;
 				}
 			}
-			if (j >= p->nCol) {
+			if (j >= (int)p->def->field_count) {
 				sqlite3ErrorMsg(pParse,
 						"unknown column \"%s\" in foreign key definition",
 						pFromCol->a[i].zName);
@@ -2904,7 +2933,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	assert(pTab != 0);
 	assert(pParse->nErr == 0);
 #ifndef SQLITE_OMIT_VIEW
-	if (pTab->pSelect) {
+	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+	if (pTab->def->opts.is_view) {
 		sqlite3ErrorMsg(pParse, "views may not be indexed");
 		goto exit_create_index;
 	}
@@ -2940,7 +2970,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 			if (!ifNotExist) {
 				sqlite3ErrorMsg(pParse,
 						"index %s.%s already exists",
-						pTab->zName, zName);
+						pTab->def->name, zName);
 			} else {
 				assert(!db->init.busy);
 			}
@@ -2953,7 +2983,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 		     pLoop = pLoop->pNext, n++) {
 		}
 		zName =
-		    sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName,
+		    sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab->def->name,
 				   n);
 		if (zName == 0) {
 			goto exit_create_index;
@@ -2966,7 +2996,9 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	 */
 	if (pList == 0) {
 		Token prevCol;
-		sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol - 1].zName);
+		sqlite3TokenInit(&prevCol,
+				 pTab->def->
+					 fields[pTab->def->field_count - 1].name);
 		pList = sqlite3ExprListAppend(pParse, 0,
 					      sqlite3ExprAlloc(db, TK_ID,
 							       &prevCol, 0));
@@ -4018,10 +4050,10 @@ sqlite3UniqueConstraint(Parse * pParse,	/* Parsing context */
 		for (j = 0; j < pIdx->nColumn; j++) {
 			char *zCol;
 			assert(pIdx->aiColumn[j] >= 0);
-			zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+			zCol = pTab->def->fields[pIdx->aiColumn[j]].name;
 			if (j)
 				sqlite3StrAccumAppend(&errMsg, ", ", 2);
-			sqlite3XPrintf(&errMsg, "%s.%s", pTab->zName, zCol);
+			sqlite3XPrintf(&errMsg, "%s.%s", pTab->def->name, zCol);
 		}
 	}
 	zErr = sqlite3StrAccumFinish(&errMsg);
@@ -4181,7 +4213,7 @@ sqlite3KeyInfoOfIndex(Parse * pParse, sqlite3 * db, Index * pIdx)
 {
 	int i;
 	int nCol = pIdx->nColumn;
-	int nTableCol = pIdx->pTable->nCol;
+	int nTableCol = pIdx->pTable->def->field_count;
 	KeyInfo *pKey;
 
 	if (pParse && pParse->nErr)
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 3f74b93..37baca2 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -82,13 +82,13 @@ sqlite3IsReadOnly(Parse * pParse, Table * pTab, int viewOk)
 	 */
 	if ((pTab->tabFlags & TF_Readonly) != 0 && pParse->nested == 0) {
 		sqlite3ErrorMsg(pParse, "table %s may not be modified",
-				pTab->zName);
+				pTab->def->name);
 		return 1;
 	}
 #ifndef SQLITE_OMIT_VIEW
 	if (!viewOk && space_is_view(pTab)) {
 		sqlite3ErrorMsg(pParse, "cannot modify %s because it is a view",
-				pTab->zName);
+				pTab->def->name);
 		return 1;
 	}
 #endif
@@ -115,7 +115,7 @@ sqlite3MaterializeView(Parse * pParse,	/* Parsing context */
 	pFrom = sqlite3SrcListAppend(db, 0, 0);
 	if (pFrom) {
 		assert(pFrom->nSrc == 1);
-		pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
+		pFrom->a[0].zName = sqlite3DbStrDup(db, pView->def->name);
 		assert(pFrom->a[0].pOn == 0);
 		assert(pFrom->a[0].pUsing == 0);
 	}
@@ -283,7 +283,8 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
 	 */
 #ifndef SQLITE_OMIT_TRIGGER
 	pTrigger = sqlite3TriggersExist(pTab, TK_DELETE, 0, 0);
-	isView = pTab->pSelect != 0;
+	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+	isView = pTab->def->opts.is_view;
 	bComplex = pTrigger || sqlite3FkRequired(pTab, 0);
 #else
 #define pTrigger 0
@@ -382,7 +383,7 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
 		 * there is no PK for it, so columns should be loaded manually.
 		 */
 		if (isView) {
-			nPk = pTab->nCol;
+			nPk = pTab->def->field_count;
 			iPk = pParse->nMem + 1;
 			pParse->nMem += nPk;
 			iEphCur = pParse->nTab++;
@@ -512,7 +513,8 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
 		if (eOnePass != ONEPASS_OFF) {
 			assert(nKey == nPk);	/* OP_Found will use an unpacked key */
 			if (aToOpen[iDataCur - iTabCur]) {
-				assert(pPk != 0 || pTab->pSelect != 0);
+				assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+				assert(pPk != 0 || pTab->def->opts.is_view);
 				sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur,
 						     addrBypass, iKey, nKey);
 
@@ -734,13 +736,13 @@ sqlite3GenerateRowDelete(Parse * pParse,	/* Parsing context */
 					  onconf);
 		mask |= sqlite3FkOldmask(pParse, pTab);
 		iOld = pParse->nMem + 1;
-		pParse->nMem += (1 + pTab->nCol);
+		pParse->nMem += (1 + pTab->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, iPk, iOld);
-		for (iCol = 0; iCol < pTab->nCol; iCol++) {
+		for (iCol = 0; iCol < (int)pTab->def->field_count; iCol++) {
 			testcase(mask != 0xffffffff && iCol == 31);
 			testcase(mask != 0xffffffff && iCol == 32);
 			if (mask == 0xffffffff
@@ -786,7 +788,8 @@ sqlite3GenerateRowDelete(Parse * pParse,	/* Parsing context */
 	 * the update-hook is not invoked for rows removed by REPLACE, but the
 	 * pre-update-hook is.
 	 */
-	if (pTab->pSelect == 0) {
+	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+	if (!pTab->def->opts.is_view) {
 		u8 p5 = 0;
 		/* kyukhin: Tarantool handles indices uypdate automatically.  */
 		/* sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);  */
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 0c86761..119940c 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -48,7 +48,7 @@ static int exprCodeVector(Parse * pParse, Expr * p, int *piToFree);
 char
 sqlite3TableColumnAffinity(Table * pTab, int iCol)
 {
-	assert(iCol < pTab->nCol);
+	assert(iCol < (int)pTab->def->field_count);
 	return iCol >= 0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
 }
 
@@ -2233,7 +2233,8 @@ isCandidateForInOpt(Expr * pX)
 		return 0;	/* FROM is not a subquery or view */
 	pTab = pSrc->a[0].pTab;
 	assert(pTab != 0);
-	assert(pTab->pSelect == 0);	/* FROM clause is not a view */
+	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+	assert(!pTab->def->opts.is_view);	/* FROM clause is not a view */
 	pEList = p->pEList;
 	assert(pEList != 0);
 	/* All SELECT results must be columns. */
@@ -4242,20 +4243,21 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			 */
 			Table *pTab = pExpr->pTab;
 			int p1 =
-			    pExpr->iTable * (pTab->nCol + 1) + 1 +
+			    pExpr->iTable * (pTab->def->field_count + 1) + 1 +
 			    pExpr->iColumn;
 
 			assert(pExpr->iTable == 0 || pExpr->iTable == 1);
 			assert(pExpr->iColumn >= 0
-			       && pExpr->iColumn < pTab->nCol);
+			       && pExpr->iColumn < (int)pTab->def->field_count);
 			assert(pTab->iPKey < 0
 			       || pExpr->iColumn != pTab->iPKey);
-			assert(p1 >= 0 && p1 < (pTab->nCol * 2 + 2));
+			assert(p1 >= 0 && p1 <
+					  ((int)pTab->def->field_count * 2 + 2));
 
 			sqlite3VdbeAddOp2(v, OP_Param, p1, target);
 			VdbeComment((v, "%s.%s -> $%d",
 				    (pExpr->iTable ? "new" : "old"),
-				    pExpr->pTab->aCol[pExpr->iColumn].zName,
+				    pExpr->pTab->def->fields[pExpr->iColumn].name,
 				    target));
 
 #ifndef SQLITE_OMIT_FLOATING_POINT
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index fb9a310..916b346 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -243,7 +243,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
 			if (!zKey)
 				return 0;
 			if (!strcmp
-			    (pParent->aCol[pParent->iPKey].zName, zKey))
+			    (pParent->def->fields[pParent->iPKey].name, zKey))
 				return 0;
 		}
 	} else if (paiCol) {
@@ -305,7 +305,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
 					if (def_coll != coll)
 						break;
 
-					zIdxCol = pParent->aCol[iCol].zName;
+					zIdxCol = pParent->def->fields[iCol].name;
 					for (j = 0; j < nCol; j++) {
 						if (strcmp
 						    (pFKey->aCol[j].zCol,
@@ -331,7 +331,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
 		if (!pParse->disableTriggers) {
 			sqlite3ErrorMsg(pParse,
 					"foreign key mismatch - \"%w\" referencing \"%w\"",
-					pFKey->pFrom->zName, pFKey->zTo);
+					pFKey->pFrom->def->name, pFKey->zTo);
 		}
 		sqlite3DbFree(pParse->db, aiCol);
 		return 1;
@@ -535,13 +535,8 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
 			pCol = &pTab->aCol[iCol];
 			pExpr->iTable = regBase + iCol + 1;
 			pExpr->affinity = pCol->affinity;
-			const char *coll_name;
-			if (pCol->coll == NULL && pCol->coll != NULL)
-				coll_name = pCol->coll->name;
-			else
-				coll_name = "binary";
 			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
-							    coll_name);
+							    "binary");
 		} else {
 			pExpr->iTable = regBase;
 			pExpr->affinity = SQLITE_AFF_INTEGER;
@@ -650,7 +645,7 @@ fkScanChildren(Parse * pParse,	/* Parse context */
 		pLeft = exprTableRegister(pParse, pTab, regData, iCol);
 		iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
 		assert(iCol >= 0);
-		zCol = pFKey->pFrom->aCol[iCol].zName;
+		zCol = pFKey->pFrom->def->fields[iCol].name;
 		pRight = sqlite3Expr(db, TK_ID, zCol);
 		pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
 		pWhere = sqlite3ExprAnd(db, pWhere, pEq);
@@ -724,7 +719,8 @@ fkScanChildren(Parse * pParse,	/* Parse context */
 FKey *
 sqlite3FkReferences(Table * pTab)
 {
-	return (FKey *) sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName);
+	return (FKey *) sqlite3HashFind(&pTab->pSchema->fkeyHash,
+					pTab->def->name);
 }
 
 /*
@@ -863,12 +859,12 @@ fkParentIsModified(Table * pTab, FKey * p, int *aChange)
 	for (i = 0; i < p->nCol; i++) {
 		char *zKey = p->aCol[i].zCol;
 		int iKey;
-		for (iKey = 0; iKey < pTab->nCol; iKey++) {
+		for (iKey = 0; iKey < (int)pTab->def->field_count; iKey++) {
 			if (aChange[iKey] >= 0) {
-				Column *pCol = &pTab->aCol[iKey];
 				if (zKey) {
 					if (0 ==
-					    strcmp(pCol->zName, zKey))
+					    strcmp(pTab->def->fields[iKey].name,
+						   zKey))
 						return 1;
 				} else if (table_column_is_in_pk(pTab, iKey)) {
 					return 1;
@@ -954,7 +950,7 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 		int bIgnore = 0;
 
 		if (aChange
-		    && sqlite3_stricmp(pTab->zName, pFKey->zTo) != 0
+		    && sqlite3_stricmp(pTab->def->name, pFKey->zTo) != 0
 		    && fkChildIsModified(pFKey, aChange) == 0) {
 			continue;
 		}
@@ -1079,7 +1075,7 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 		if (pSrc) {
 			struct SrcList_item *pItem = pSrc->a;
 			pItem->pTab = pFKey->pFrom;
-			pItem->zName = pFKey->pFrom->zName;
+			pItem->zName = pFKey->pFrom->def->name;
 			pItem->pTab->nTabRef++;
 			pItem->iCursor = pParse->nTab++;
 
@@ -1282,14 +1278,14 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 			assert(iFromCol >= 0);
 			assert(pIdx != 0
 			       || (pTab->iPKey >= 0
-				   && pTab->iPKey < pTab->nCol));
+				   && pTab->iPKey < (int)pTab->def->field_count));
 			assert(pIdx == 0 || pIdx->aiColumn[i] >= 0);
 			sqlite3TokenInit(&tToCol,
-					 pTab->aCol[pIdx ? pIdx->
+					 pTab->def->fields[pIdx ? pIdx->
 						    aiColumn[i] : pTab->iPKey].
-					 zName);
+					 name);
 			sqlite3TokenInit(&tFromCol,
-					 pFKey->pFrom->aCol[iFromCol].zName);
+					 pFKey->pFrom->def->fields[iFromCol].name);
 
 			/* Create the expression "OLD.zToCol = zFromCol". It is important
 			 * that the "OLD.zToCol" term is on the LHS of the = operator, so
@@ -1378,7 +1374,7 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 		}
 		sqlite3DbFree(db, aiCol);
 
-		zFrom = pFKey->pFrom->zName;
+		zFrom = pFKey->pFrom->def->name;
 		nFrom = sqlite3Strlen30(zFrom);
 
 		if (action == OE_Restrict) {
diff --git a/src/box/sql/hash.c b/src/box/sql/hash.c
index cedcb7d..b109cca 100644
--- a/src/box/sql/hash.c
+++ b/src/box/sql/hash.c
@@ -69,6 +69,7 @@ sqlite3HashClear(Hash * pH)
 	while (elem) {
 		HashElem *next_elem = elem->next;
 		sqlite3_free(elem);
+		free(elem->pKey);
 		elem = next_elem;
 	}
 	pH->count = 0;
@@ -292,7 +293,7 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
 			removeElementGivenHash(pH, elem, h);
 		} else {
 			elem->data = data;
-			elem->pKey = pKey;
+			elem->pKey = strdup(pKey);
 		}
 		return old_data;
 	}
@@ -301,7 +302,7 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
 	new_elem = (HashElem *) sqlite3Malloc(sizeof(HashElem));
 	if (new_elem == 0)
 		return data;
-	new_elem->pKey = pKey;
+	new_elem->pKey = strdup(pKey);
 	new_elem->data = data;
 	pH->count++;
 	if (pH->count >= 10 && pH->count > 2 * pH->htsize) {
diff --git a/src/box/sql/hash.h b/src/box/sql/hash.h
index f049c6d..f57824e 100644
--- a/src/box/sql/hash.h
+++ b/src/box/sql/hash.h
@@ -80,7 +80,7 @@ struct Hash {
 struct HashElem {
 	HashElem *next, *prev;	/* Next and previous elements in the table */
 	void *data;		/* Data associated with this element */
-	const char *pKey;	/* Key associated with this element */
+	char *pKey;		/* Key associated with this element */
 };
 
 /*
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 939b5e3..c272ae1 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -56,7 +56,7 @@ sqlite3OpenTable(Parse * pParse,	/* Generate code into this VDBE */
 	assert(pPk->tnum == pTab->tnum);
 	emit_open_cursor(pParse, iCur, pPk->tnum);
 	sqlite3VdbeSetP4KeyInfo(pParse, pPk);
-	VdbeComment((v, "%s", pTab->zName));
+	VdbeComment((v, "%s", pTab->def->name));
 }
 
 /*
@@ -146,13 +146,14 @@ sqlite3TableAffinity(Vdbe * v, Table * pTab, int iReg)
 	char *zColAff = pTab->zColAff;
 	if (zColAff == 0) {
 		sqlite3 *db = sqlite3VdbeDb(v);
-		zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol + 1);
+		zColAff = (char *)sqlite3DbMallocRaw(0,
+						     pTab->def->field_count + 1);
 		if (!zColAff) {
 			sqlite3OomFault(db);
 			return;
 		}
 
-		for (i = 0; i < pTab->nCol; i++) {
+		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			zColAff[i] = pTab->aCol[i].affinity;
 		}
 		do {
@@ -446,7 +447,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	 * the content of the new row, and the assembled row record.
 	 */
 	regTupleid = regIns = pParse->nMem + 1;
-	pParse->nMem += pTab->nCol + 1;
+	pParse->nMem += pTab->def->field_count + 1;
 	regData = regTupleid + 1;
 
 	/* If the INSERT statement included an IDLIST term, then make sure
@@ -465,17 +466,17 @@ 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, (pTab->nCol + 7) / 8);
+	memset(used_columns, 0, (pTab->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 < pTab->nCol; j++) {
+			for (j = 0; j < (int)pTab->def->field_count; j++) {
 				if (strcmp
 				    (pColumn->a[i].zName,
-				     pTab->aCol[j].zName) == 0) {
+				     pTab->def->fields[j].name) == 0) {
 					pColumn->a[i].idx = j;
 					if (i != j)
 						bIdListInOrder = 0;
@@ -486,7 +487,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 					break;
 				}
 			}
-			if (j >= pTab->nCol) {
+			if (j >= (int)pTab->def->field_count) {
 				sqlite3ErrorMsg(pParse,
 						"table %S has no column named %s",
 						pTabList, 0, pColumn->a[i].zName);
@@ -522,7 +523,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
 		sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
 		dest.iSdst = bIdListInOrder ? regData : 0;
-		dest.nSdst = pTab->nCol;
+		dest.nSdst = pTab->def->field_count;
 		rc = sqlite3Select(pParse, pSelect, &dest);
 		regFromSelect = dest.iSdst;
 		if (rc || db->mallocFailed || pParse->nErr)
@@ -611,10 +612,10 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		ipkColumn = pTab->iPKey;
 	}
 
-	if (pColumn == 0 && nColumn && nColumn != (pTab->nCol)) {
+	if (pColumn == 0 && nColumn && nColumn != ((int)pTab->def->field_count)) {
 		sqlite3ErrorMsg(pParse,
 				"table %S has %d columns but %d values were supplied",
-				pTabList, 0, pTab->nCol, nColumn);
+				pTabList, 0, pTab->def->field_count, nColumn);
 		goto insert_cleanup;
 	}
 	if (pColumn != 0 && nColumn != pColumn->nId) {
@@ -682,11 +683,12 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	 */
 	endOfLoop = sqlite3VdbeMakeLabel(v);
 	if (tmask & TRIGGER_BEFORE) {
-		int regCols = sqlite3GetTempRange(pParse, pTab->nCol + 1);
+		int regCols = sqlite3GetTempRange(pParse,
+						  pTab->def->field_count + 1);
 
 		/* Create the new column data
 		 */
-		for (i = j = 0; i < pTab->nCol; i++) {
+		for (i = j = 0; i < (int)pTab->def->field_count; i++) {
 			if (pColumn) {
 				for (j = 0; j < pColumn->nId; j++) {
 					if (pColumn->a[j].idx == i)
@@ -732,10 +734,11 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		/* Fire BEFORE or INSTEAD OF triggers */
 		sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
 				      TRIGGER_BEFORE, pTab,
-				      regCols - pTab->nCol - 1, onError,
-				      endOfLoop);
+				      regCols - pTab->def->field_count - 1,
+				      onError, endOfLoop);
 
-		sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol + 1);
+		sqlite3ReleaseTempRange(pParse, regCols,
+					pTab->def->field_count + 1);
 	}
 
 	/* Compute the content of the next row to insert into a range of
@@ -758,7 +761,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		/* Compute data for all columns of the new entry, beginning
 		 * with the first column.
 		 */
-		for (i = 0; i < pTab->nCol; i++) {
+		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			int iRegStore = regData + i;
 			if (pColumn == 0) {
 				j = i;
@@ -879,8 +882,8 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		/* Code AFTER triggers */
 		sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
 				      TRIGGER_AFTER, pTab,
-				      regData - 2 - pTab->nCol, onError,
-				      endOfLoop);
+				      regData - 2 - pTab->def->field_count,
+				      onError, endOfLoop);
 	}
 
 	/* The bottom of the main insertion loop, if the data source
@@ -1093,7 +1096,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 	assert(v != 0);
 	/* This table is not a VIEW */
 	assert(!space_is_view(pTab));
-	nCol = pTab->nCol;
+	nCol = pTab->def->field_count;
 
 	pPk = sqlite3PrimaryKeyIndex(pTab);
 	nPkField = index_column_count(pPk);
@@ -1142,8 +1145,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 		case ON_CONFLICT_ACTION_ROLLBACK:
 		case ON_CONFLICT_ACTION_FAIL: {
 				char *zMsg =
-				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
-						   pTab->aCol[i].zName);
+				    sqlite3MPrintf(db, "%s.%s", pTab->def->name,
+						   pTab->def->fields[i].name);
 				sqlite3VdbeAddOp3(v, OP_HaltIfNull,
 						  SQLITE_CONSTRAINT_NOTNULL,
 						  onError, regNewData + 1 + i);
@@ -1197,7 +1200,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			} else {
 				char *zName = pCheck->a[i].zName;
 				if (zName == 0)
-					zName = pTab->zName;
+					zName = pTab->def->name;
 				if (onError == ON_CONFLICT_ACTION_REPLACE)
 					onError = ON_CONFLICT_ACTION_ABORT;
 				sqlite3HaltConstraint(pParse,
@@ -1282,7 +1285,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 					sqlite3VdbeAddOp2(v, OP_SCopy,
 							  x, regIdx + i);
 					VdbeComment((v, "%s",
-						     pTab->aCol[iField].zName));
+						     pTab->def->fields[iField].
+							     name));
 				}
 			}
 		}
@@ -1310,7 +1314,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			}
 
 			sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,
-					pTab->nCol, aRegIdx[ix]);
+					pTab->def->field_count, aRegIdx[ix]);
 			VdbeComment((v, "for %s", pIdx->zName));
 		}
 
@@ -1390,10 +1394,11 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 				for (i = 0; i < nPkCol; i++) {
 					assert(pPk->aiColumn[i] >= 0);
 					x = pPk->aiColumn[i];
-					sqlite3VdbeAddOp3(v, OP_Column,
-							  iThisCur, x, regR + i);
-					VdbeComment((v, "%s.%s", pTab->zName,
-						pTab->aCol[pPk->aiColumn[i]].zName));
+					sqlite3VdbeAddOp3(v, OP_Column, iThisCur,
+							  x, regR + i);
+					VdbeComment((v, "%s.%s", pTab->def->name,
+						pTab->def->fields[
+							pPk->aiColumn[i]].name));
 				}
 			}
 			if (isUpdate && uniqueByteCodeNeeded) {
@@ -1784,13 +1789,13 @@ xferOptimization(Parse * pParse,	/* Parser context */
 	if (space_is_view(pSrc)) {
 		return 0;	/* tab2 may not be a view */
 	}
-	if (pDest->nCol != pSrc->nCol) {
+	if (pDest->def->field_count != pSrc->def->field_count) {
 		return 0;	/* Number of columns must be the same in tab1 and tab2 */
 	}
 	if (pDest->iPKey != pSrc->iPKey) {
 		return 0;	/* Both tables must have the same INTEGER PRIMARY KEY */
 	}
-	for (i = 0; i < pDest->nCol; i++) {
+	for (i = 0; i < (int)pDest->def->field_count; i++) {
 		Column *pDestCol = &pDest->aCol[i];
 		Column *pSrcCol = &pSrc->aCol[i];
 		if (pDestCol->affinity != pSrcCol->affinity) {
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index b9038b7..250c402 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -359,7 +359,8 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 				Index *pPk = sqlite3PrimaryKeyIndex(pTab);
 				pParse->nMem = 6;
 				sqlite3ViewGetColumnNames(pParse, pTab);
-				for (i = 0, pCol = pTab->aCol; i < pTab->nCol;
+				for (i = 0, pCol = pTab->aCol;
+				     i < (int)pTab->def->field_count;
 				     i++, pCol++) {
 					if (!table_column_is_in_pk(pTab, i)) {
 						k = 0;
@@ -367,7 +368,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 						k = 1;
 					} else {
 						for (k = 1;
-						     k <= pTab->nCol
+						     k <= (int)pTab->def->field_count
 						     && pPk->aiColumn[k - 1] !=
 						     i; k++) {
 						}
@@ -380,11 +381,13 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 						space_cache_find(space_id);
 					char *expr_str = space->
 						def->fields[i].default_value;
+					const char *name =
+						pTab->def->fields[i].name;
+					enum field_type type =
+						pTab->def->fields[i].type;
 					sqlite3VdbeMultiLoad(v, 1, "issisi",
-							     i, pCol->zName,
-							     field_type_strs[
-							     sqlite3ColumnType
-							     (pCol)],
+							     i, name,
+							     field_type_strs[type],
 							     nullable == 0,
 							     expr_str, k);
 					sqlite3VdbeAddOp2(v, OP_ResultRow, 1,
@@ -402,7 +405,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 			     i = sqliteHashNext(i)) {
 				Table *pTab = sqliteHashData(i);
 				sqlite3VdbeMultiLoad(v, 1, "ssii",
-						     pTab->zName,
+						     pTab->def->name,
 						     0,
 						     pTab->szTabRow,
 						     pTab->nRowLogEst);
@@ -453,8 +456,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 								     0 ? 0 :
 								     pIdx->
 								     pTable->
-								     aCol[cnum].
-								     zName);
+								     def->
+								     fields[cnum].
+								     name);
 						if (pPragma->iArg) {
 							const char *c_n;
 							struct coll *coll;
@@ -559,7 +563,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 		while (pFK != NULL) {
 			for (int j = 0; j < pFK->nCol; j++) {
 				const char *name =
-					pTab->aCol[pFK->aCol[j].iFrom].zName;
+					pTab->def->fields[pFK->aCol[j].iFrom].name;
 				sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j,
 						     pFK->zTo, name,
 						     pFK->aCol[j].zCol,
@@ -614,11 +618,12 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 				}
 				if (pTab == 0 || pTab->pFKey == 0)
 					continue;
-				if (pTab->nCol + regRow > pParse->nMem)
-					pParse->nMem = pTab->nCol + regRow;
+				if ((int)pTab->def->field_count + regRow > pParse->nMem)
+					pParse->nMem =
+						pTab->def->field_count + regRow;
 				sqlite3OpenTable(pParse, 0, pTab, OP_OpenRead);
 				sqlite3VdbeLoadString(v, regResult,
-						      pTab->zName);
+						      pTab->def->name);
 				for (i = 1, pFK = pTab->pFKey; pFK;
 				     i++, pFK = pFK->pNextFrom) {
 					pParent =
@@ -677,7 +682,8 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 					if (pParent && pIdx == 0) {
 						int iKey = pFK->aCol[0].iFrom;
 						assert(iKey >= 0
-						       && iKey < pTab->nCol);
+						       && iKey <
+						       (int)pTab->def->field_count);
 						if (iKey != pTab->iPKey) {
 							sqlite3VdbeAddOp3(v,
 									  OP_Column,
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 2ab8751..926c3dd 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -204,26 +204,6 @@ sqlite3InitDatabase(sqlite3 * db)
 	return rc;
 }
 
-
-/*
- * Free all memory allocations in the pParse object
- */
-void
-sqlite3ParserReset(Parse * pParse)
-{
-	if (pParse) {
-		sqlite3 *db = pParse->db;
-		sqlite3DbFree(db, pParse->aLabel);
-		sqlite3ExprListDelete(db, pParse->pConstExpr);
-		if (db) {
-			assert(db->lookaside.bDisable >=
-			       pParse->disableLookaside);
-			db->lookaside.bDisable -= pParse->disableLookaside;
-		}
-		pParse->disableLookaside = 0;
-	}
-}
-
 /*
  * Compile the UTF-8 encoded SQL statement zSql into a statement handle.
  */
@@ -241,9 +221,7 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
 	int rc = SQLITE_OK;	/* Result code */
 	int i;			/* Loop counter */
 	Parse sParse;		/* Parsing context */
-
-	memset(&sParse, 0, PARSE_HDR_SZ);
-	memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
+	sql_parser_create(&sParse);
 	sParse.pReprepare = pReprepare;
 	assert(ppStmt && *ppStmt == 0);
 	/* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
@@ -265,6 +243,7 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
 	 * works even if READ_UNCOMMITTED is set.
 	 */
 	sParse.db = db;
+
 	if (nBytes >= 0 && (nBytes == 0 || zSql[nBytes - 1] != 0)) {
 		char *zSqlCopy;
 		int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -350,7 +329,7 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
 
  end_prepare:
 
-	sqlite3ParserReset(&sParse);
+	sql_parser_free(&sParse);
 	rc = sqlite3ApiExit(db, rc);
 	assert((rc & db->errMask) == rc);
 	return rc;
@@ -456,3 +435,21 @@ sqlite3_prepare_v2(sqlite3 * db,	/* Database handle. */
 	assert(rc == SQLITE_OK || ppStmt == 0 || *ppStmt == 0);	/* VERIFY: F13021 */
 	return rc;
 }
+
+void
+sql_parser_free(Parse *parser)
+{
+	if (parser == NULL)
+		return;
+	sqlite3 *db = parser->db;
+	sqlite3DbFree(db, parser->aLabel);
+	sqlite3ExprListDelete(db, parser->pConstExpr);
+	if (db != NULL) {
+		assert(db->lookaside.bDisable >=
+		       parser->disableLookaside);
+		db->lookaside.bDisable -= parser->disableLookaside;
+	}
+	parser->disableLookaside = 0;
+	struct region *region = &fiber()->gc;
+	region_truncate(region, parser->region_initial_size);
+}
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 823062a..f95ef27 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -239,8 +239,8 @@ lookupName(Parse * pParse,	/* The parsing context */
 			for (i = 0, pItem = pSrcList->a; i < pSrcList->nSrc;
 			     i++, pItem++) {
 				pTab = pItem->pTab;
-				assert(pTab != 0 && pTab->zName != 0);
-				assert(pTab->nCol > 0);
+				assert(pTab != 0 && pTab->def->name != NULL);
+				assert(pTab->def->field_count > 0);
 				if (pItem->pSelect
 				    && (pItem->pSelect->
 					selFlags & SF_NestedFrom) != 0) {
@@ -263,7 +263,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 				if (zTab) {
 					const char *zTabName =
 					    pItem->zAlias ? pItem->
-					    zAlias : pTab->zName;
+					    zAlias : pTab->def->name;
 					assert(zTabName != 0);
 					if (strcmp(zTabName, zTab) != 0) {
 						continue;
@@ -272,9 +272,10 @@ lookupName(Parse * pParse,	/* The parsing context */
 				if (0 == (cntTab++)) {
 					pMatch = pItem;
 				}
-				for (j = 0, pCol = pTab->aCol; j < pTab->nCol;
+				for (j = 0, pCol = pTab->aCol;
+				     j < (int)pTab->def->field_count;
 				     j++, pCol++) {
-					if (strcmp(pCol->zName, zCol) ==
+					if (strcmp(pTab->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
@@ -332,16 +333,17 @@ lookupName(Parse * pParse,	/* The parsing context */
 				int iCol;
 				cntTab++;
 				for (iCol = 0, pCol = pTab->aCol;
-				     iCol < pTab->nCol; iCol++, pCol++) {
-					if (strcmp(pCol->zName, zCol) ==
-					    0) {
+				     iCol < (int)pTab->def->field_count;
+				     iCol++, pCol++) {
+					if (strcmp(pTab->def->fields[iCol].name,
+						   zCol) == 0) {
 						if (iCol == pTab->iPKey) {
 							iCol = -1;
 						}
 						break;
 					}
 				}
-				if (iCol < pTab->nCol) {
+				if (iCol < (int)pTab->def->field_count) {
 					cnt++;
 					if (iCol < 0) {
 						pExpr->affinity =
@@ -1602,7 +1604,7 @@ sqlite3ResolveSelfReference(Parse * pParse,	/* Parsing context */
 	memset(&sNC, 0, sizeof(sNC));
 	memset(&sSrc, 0, sizeof(sSrc));
 	sSrc.nSrc = 1;
-	sSrc.a[0].zName = pTab->zName;
+	sSrc.a[0].zName = pTab->def->name;
 	sSrc.a[0].pTab = pTab;
 	sSrc.a[0].iCursor = -1;
 	sNC.pParse = pParse;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 5a50413..32a8e08 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -318,9 +318,9 @@ sqlite3JoinType(Parse * pParse, Token * pA, Token * pB, Token * pC)
 static int
 columnIndex(Table * pTab, const char *zCol)
 {
-	int i;
-	for (i = 0; i < pTab->nCol; i++) {
-		if (strcmp(pTab->aCol[i].zName, zCol) == 0)
+	uint32_t i;
+	for (i = 0; i < pTab->def->field_count; i++) {
+		if (strcmp(pTab->def->fields[i].name, zCol) == 0)
 			return i;
 	}
 	return -1;
@@ -492,12 +492,12 @@ sqliteProcessJoin(Parse * pParse, Select * p)
 						"an ON or USING clause", 0);
 				return 1;
 			}
-			for (j = 0; j < pRightTab->nCol; j++) {
+			for (j = 0; j < (int)pRightTab->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->aCol[j].zName;
+				zName = pRightTab->def->fields[j].name;
 				if (tableAndColumnIndex
 				    (pSrc, i + 1, zName, &iLeft, &iLeftCol)) {
 					addWhereTerm(pParse, pSrc, iLeft,
@@ -1661,14 +1661,15 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
 			} else if (pTab->pSchema) {
 				/* A real table */
 				assert(!pS);
-				assert(iCol >= 0 && iCol < pTab->nCol);
+				assert(iCol >= 0 && iCol <
+						    (int)pTab->def->field_count);
 #ifdef SQLITE_ENABLE_COLUMN_METADATA
-				zOrigCol = pTab->aCol[iCol].zName;
-				zType = sqlite3ColumnType(&pTab->aCol[iCol], 0);
+				zOrigCol = pTab->def->fields[iCol].name;
+				zType = pTab->def->fields[iCol].type;
 				estWidth = pTab->aCol[iCol].szEst;
 				zOrigTab = pTab->zName;
 #else
-				column_type = sqlite3ColumnType(&pTab->aCol[iCol]);
+				column_type = pTab->def->fields[iCol].type;
 				estWidth = pTab->aCol[iCol].szEst;
 #endif
 			}
@@ -1754,8 +1755,8 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 			pTab = pTabList->a[j].pTab;
 			if (iCol < 0)
 				iCol = pTab->iPKey;
-			assert(iCol >= 0 && iCol < pTab->nCol);
-			zCol = pTab->aCol[iCol].zName;
+			assert(iCol >= 0 && iCol < (int)pTab->def->field_count);
+			zCol = pTab->def->fields[iCol].name;
 			if (!shortNames && !fullNames) {
 				sqlite3VdbeSetColName(v, i, COLNAME_NAME,
 						      sqlite3DbStrDup(db,
@@ -1764,8 +1765,7 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 			} else if (fullNames) {
 				char *zName = 0;
 				zName =
-				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
-						   zCol);
+				    sqlite3MPrintf(db, "%s.%s", pTab->def->name, zCol);
 				sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
 						      SQLITE_DYNAMIC);
 			} else {
@@ -1799,8 +1799,7 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 int
 sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			   ExprList * pEList,	/* Expr list from which to derive column names */
-			   i16 * pnCol,		/* Write the number of columns here */
-			   Column ** paCol)	/* Write the new column list here */
+			   Table * pTable)	/* Pointer to SQL Table Object*/
 {
 	sqlite3 *db = pParse->db;	/* Database connection */
 	int i, j;		/* Loop counters */
@@ -1819,11 +1818,25 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 		testcase(aCol == 0);
 	} else {
 		nCol = 0;
-		aCol = 0;
+		aCol = NULL;
 	}
 	assert(nCol == (i16) nCol);
-	*pnCol = nCol;
-	*paCol = aCol;
+
+	/*
+	 * This should be a table without resolved columns.
+	 * sqlite3ViewGetColumnNames could use it to resolve
+	 * names for existent table.
+	 */
+	assert(pTable->def->fields == NULL);
+	struct region *region = &fiber()->gc;
+	pTable->def->fields =
+		region_alloc(region, nCol * sizeof(pTable->def->fields[0]));
+	memset(pTable->def->fields, 0, nCol * sizeof(pTable->def->fields[0]));
+	/* NULL nullable_action should math is_nullable flag. */
+	for (int i = 0; i < nCol; i++)
+		pTable->def->fields[i].is_nullable = true;
+	pTable->def->field_count = (uint32_t)nCol;
+	pTable->aCol = aCol;
 
 	for (i = 0, pCol = aCol; i < nCol && !db->mallocFailed; i++, pCol++) {
 		/* Get an appropriate name for the column
@@ -1845,7 +1858,7 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 				pTab = pColExpr->pTab;
 				if (iCol < 0)
 					iCol = pTab->iPKey;
-				zName = pTab->aCol[iCol].zName;
+				zName = pTab->def->fields[iCol].name;
 			} else if (pColExpr->op == TK_ID) {
 				assert(!ExprHasProperty(pColExpr, EP_IntValue));
 				zName = pColExpr->u.zToken;
@@ -1874,22 +1887,28 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			if (cnt > 3)
 				sqlite3_randomness(sizeof(cnt), &cnt);
 		}
-		pCol->zName = zName;
-		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
+		uint32_t name_len = (uint32_t)strlen(zName);
+		if (zName != NULL && sqlite3HashInsert(&ht, zName, pCol) == pCol)
 			sqlite3OomFault(db);
+		pTable->def->fields[i].name =
+			region_alloc(region, name_len + 1);
+		if (pTable->def->fields[i].name == NULL) {
+			sqlite3OomFault(db);
+		} else {
+			memcpy(pTable->def->fields[i].name, zName, name_len);
+			pTable->def->fields[i].name[name_len] = '\0';
 		}
 	}
 	sqlite3HashClear(&ht);
-	if (db->mallocFailed) {
-		for (j = 0; j < i; j++) {
-			sqlite3DbFree(db, aCol[j].zName);
-		}
+	int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;
+	if (rc != SQLITE_OK) {
 		sqlite3DbFree(db, aCol);
-		*paCol = 0;
-		*pnCol = 0;
-		return SQLITE_NOMEM_BKPT;
+		pTable->def->fields = NULL;
+		pTable->def->field_count = 0;
+		pTable->aCol = NULL;
+		rc = SQLITE_NOMEM_BKPT;
 	}
-	return SQLITE_OK;
+	return rc;
 }
 
 /*
@@ -1918,26 +1937,28 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 
 	assert(pSelect != 0);
 	assert((pSelect->selFlags & SF_Resolved) != 0);
-	assert(pTab->nCol == pSelect->pEList->nExpr || db->mallocFailed);
+	assert((int)pTab->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 (i = 0, pCol = pTab->aCol; i < pTab->nCol; i++, pCol++) {
+	for (i = 0, pCol = pTab->aCol;
+	     i < (int)pTab->def->field_count; i++, pCol++) {
 		enum field_type type;
 		p = a[i].pExpr;
 		type = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
 		szAll += pCol->szEst;
 		pCol->affinity = sqlite3ExprAffinity(p);
-		pCol->type = type;
+		pTab->def->fields[i].type = type;
 
 		if (pCol->affinity == 0)
 			pCol->affinity = SQLITE_AFF_BLOB;
 		bool unused;
 		struct coll *coll = sql_expr_coll(pParse, p, &unused);
-		if (coll != NULL && pCol->coll == NULL)
-			pCol->coll = coll;
+		if (coll != NULL && pTab->def->fields[i].coll_id == COLL_NONE)
+			pTab->def->fields[i].coll_id = coll->id;
 	}
 	pTab->szTabRow = sqlite3LogEst(szAll * 4);
 }
@@ -1945,6 +1966,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 /*
  * Given a SELECT statement, generate a Table structure that describes
  * the result set of that SELECT.
+ * Return table with def allocated on region.
  */
 Table *
 sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
@@ -1963,20 +1985,17 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	while (pSelect->pPrior)
 		pSelect = pSelect->pPrior;
 	user_session->sql_flags = savedFlags;
-	pTab = sqlite3DbMallocZero(db, sizeof(Table));
-	if (pTab == 0) {
+	pTab = sql_ephemeral_table_new(pParse, NULL);
+	if (pTab == NULL)
 		return 0;
-	}
 	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
 	 * is disabled
 	 */
 	assert(db->lookaside.bDisable);
 	pTab->nTabRef = 1;
-	pTab->zName = 0;
 	pTab->nRowLogEst = 200;
 	assert(200 == sqlite3LogEst(1048576));
-	sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol,
-				   &pTab->aCol);
+	sqlite3ColumnsFromExprList(pParse, pSelect->pEList, pTab);
 	sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
 	pTab->iPKey = -1;
 	if (db->mallocFailed) {
@@ -4497,11 +4516,11 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			return SQLITE_ERROR;
 
 		assert(pFrom->pTab == 0);
-		pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
-		if (pTab == 0)
+		pFrom->pTab = pTab =
+			sql_ephemeral_table_new(pParse, pCte->zName);
+		if (pTab == NULL)
 			return WRC_Abort;
 		pTab->nTabRef = 1;
-		pTab->zName = sqlite3DbStrDup(db, pCte->zName);
 		pTab->iPKey = -1;
 		pTab->nRowLogEst = 200;
 		assert(200 == sqlite3LogEst(1048576));
@@ -4562,8 +4581,7 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			pEList = pCte->pCols;
 		}
 
-		sqlite3ColumnsFromExprList(pParse, pEList, &pTab->nCol,
-					   &pTab->aCol);
+		sqlite3ColumnsFromExprList(pParse, pEList, pTab);
 		if (bMayRecursive) {
 			if (pSel->selFlags & SF_Recursive) {
 				pCte->zCteErr =
@@ -4683,18 +4701,24 @@ selectExpander(Walker * pWalker, Select * p)
 			assert(pFrom->pTab == 0);
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
+			/* Will overwritten with pointer as unique identifier. */
+			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
 			pFrom->pTab = pTab =
-			    sqlite3DbMallocZero(db, sizeof(Table));
-			if (pTab == 0)
+				sql_ephemeral_table_new(pParse, name);
+			if (pTab == 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;
-			pTab->zName =
-			    sqlite3MPrintf(db, "sqlite_sq_%p", (void *)pTab);
 			while (pSel->pPrior) {
 				pSel = pSel->pPrior;
 			}
-			sqlite3ColumnsFromExprList(pParse, pSel->pEList,
-						   &pTab->nCol, &pTab->aCol);
+			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
+			if (sql_table_def_rebuild(db, pTab) != 0)
+				return WRC_Abort;
+
 			pTab->iPKey = -1;
 			pTab->nRowLogEst = 200;
 			assert(200 == sqlite3LogEst(1048576));
@@ -4709,7 +4733,7 @@ selectExpander(Walker * pWalker, Select * p)
 			if (pTab->nTabRef >= 0xffff) {
 				sqlite3ErrorMsg(pParse,
 						"too many references to \"%s\": max 65535",
-						pTab->zName);
+						pTab->def->name);
 				pFrom->pTab = 0;
 				return WRC_Abort;
 			}
@@ -4723,14 +4747,16 @@ selectExpander(Walker * pWalker, Select * p)
 				if (sqlite3ViewGetColumnNames(pParse, pTab))
 					return WRC_Abort;
 				assert(pFrom->pSelect == 0);
+				assert(pTab->def->opts.is_view ==
+				       (pTab->pSelect != NULL));
 				pFrom->pSelect =
 				    sqlite3SelectDup(db, pTab->pSelect, 0);
 				sqlite3SelectSetName(pFrom->pSelect,
-						     pTab->zName);
-				nCol = pTab->nCol;
-				pTab->nCol = -1;
+						     pTab->def->name);
+				nCol = pTab->def->field_count;
+				pTab->def->field_count = -1;
 				sqlite3WalkSelect(pWalker, pFrom->pSelect);
-				pTab->nCol = nCol;
+				pTab->def->field_count = nCol;
 			}
 #endif
 		}
@@ -4819,7 +4845,7 @@ selectExpander(Walker * pWalker, Select * p)
 					Select *pSub = pFrom->pSelect;
 					char *zTabName = pFrom->zAlias;
 					if (zTabName == 0) {
-						zTabName = pTab->zName;
+						zTabName = pTab->def->name;
 					}
 					if (db->mallocFailed)
 						break;
@@ -4835,9 +4861,8 @@ selectExpander(Walker * pWalker, Select * p)
 							continue;
 						}
 					}
-					for (j = 0; j < pTab->nCol; j++) {
-						char *zName =
-						    pTab->aCol[j].zName;
+					for (j = 0; j < (int)pTab->def->field_count; j++) {
+						char *zName = pTab->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 */
@@ -5266,7 +5291,7 @@ explainSimpleCount(Parse * pParse,	/* Parse context */
 	if (pParse->explain == 2) {
 		int bCover = (pIdx != 0 && !IsPrimaryKeyIndex(pIdx));
 		char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
-					    pTab->zName,
+					    pTab->def->name,
 					    bCover ? " USING COVERING INDEX " :
 					    "",
 					    bCover ? pIdx->zName : "");
@@ -5372,10 +5397,10 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		/* Catch mismatch in the declared columns of a view and the number of
 		 * columns in the SELECT on the RHS
 		 */
-		if (pTab->nCol != pSub->pEList->nExpr) {
+		if ((int)pTab->def->field_count != pSub->pEList->nExpr) {
 			sqlite3ErrorMsg(pParse,
 					"expected %d columns for '%s' but got %d",
-					pTab->nCol, pTab->zName,
+					pTab->def->field_count, pTab->def->name,
 					pSub->pEList->nExpr);
 			goto select_end;
 		}
@@ -5493,7 +5518,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 			pItem->regReturn = ++pParse->nMem;
 			sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn,
 					  0, addrTop);
-			VdbeComment((v, "%s", pItem->pTab->zName));
+			VdbeComment((v, "%s", pItem->pTab->def->name));
 			pItem->addrFillSub = addrTop;
 			sqlite3SelectDestInit(&dest, SRT_Coroutine,
 					      pItem->regReturn);
@@ -5528,10 +5553,10 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 				onceAddr = sqlite3VdbeAddOp0(v, OP_Once);
 				VdbeCoverage(v);
 				VdbeComment((v, "materialize \"%s\"",
-					     pItem->pTab->zName));
+					     pItem->pTab->def->name));
 			} else {
 				VdbeNoopComment((v, "materialize \"%s\"",
-						 pItem->pTab->zName));
+						 pItem->pTab->def->name));
 			}
 			sqlite3SelectDestInit(&dest, SRT_EphemTab,
 					      pItem->iCursor);
@@ -5542,7 +5567,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 				sqlite3VdbeJumpHere(v, onceAddr);
 			retAddr =
 			    sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
-			VdbeComment((v, "end %s", pItem->pTab->zName));
+			VdbeComment((v, "end %s", pItem->pTab->def->name));
 			sqlite3VdbeChangeP1(v, topAddr, retAddr);
 			sqlite3ClearTempRegCache(pParse);
 		}
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 8bb45c9..4fba008 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,15 +1867,6 @@ struct Savepoint {
  * of this structure.
  */
 struct Column {
-	char *zName;		/* Name of this column */
-	enum field_type type;	/* Column type. */
-	/** Collating sequence. */
-	struct coll *coll;
-	/**
-	 * An ON_CONFLICT_ACTION code for handling a NOT NULL
-	 * constraint.
-	 */
-	enum on_conflict_action notNull;
 	char affinity;		/* One of the SQLITE_AFF_... values */
 	u8 szEst;		/* Estimated size of value in this column. sizeof(INT)==1 */
 	u8 is_primkey;		/* Boolean propertie for being PK */
@@ -1936,7 +1927,6 @@ struct Column {
  * by an instance of the following structure.
  */
 struct Table {
-	char *zName;		/* Name of the table or view */
 	Column *aCol;		/* Information about each column */
 	Index *pIndex;		/* List of SQL indexes on this table. */
 	Select *pSelect;	/* NULL for tables.  Points to definition if a view. */
@@ -1950,7 +1940,6 @@ struct Table {
 	i16 iPKey;		/* If not negative, use aCol[iPKey] as the rowid */
 	i16 iAutoIncPKey;	/* If PK is marked INTEGER PRIMARY KEY AUTOINCREMENT, store
 				   column number here, -1 otherwise Tarantool specifics */
-	i16 nCol;		/* Number of columns in this table */
 	LogEst nRowLogEst;	/* Estimated rows in table - from _sql_stat1 table */
 	LogEst szTabRow;	/* Estimated size of each table row in bytes */
 #ifdef SQLITE_ENABLE_COSTMULT
@@ -2936,6 +2925,8 @@ struct Parse {
 	u8 eTriggerOp;		/* TK_UPDATE, TK_INSERT or TK_DELETE */
 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
 	u8 disableTriggers;	/* True to disable triggers */
+	/** Region size at the Parser launch. */
+	size_t region_initial_size;
 
   /**************************************************************************
   * Fields above must be initialized to zero.  The fields that follow,
@@ -3390,7 +3381,6 @@ int sqlite3IoerrnomemError(int);
  */
 int sqlite3StrICmp(const char *, const char *);
 unsigned sqlite3Strlen30(const char *);
-enum field_type sqlite3ColumnType(Column *);
 #define sqlite3StrNICmp sqlite3_strnicmp
 
 void sqlite3MallocInit(void);
@@ -3515,7 +3505,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *);
 void sqlite3CommitInternalChanges();
 void sqlite3DeleteColumnNames(sqlite3 *, Table *);
 bool table_column_is_in_pk(Table *, uint32_t);
-int sqlite3ColumnsFromExprList(Parse *, ExprList *, i16 *, Column **);
+int sqlite3ColumnsFromExprList(Parse *, ExprList *, Table *);
 void sqlite3SelectAddColumnTypeAndCollation(Parse *, Table *, Select *);
 Table *sqlite3ResultSetOfSelect(Parse *, Select *);
 Index *sqlite3PrimaryKeyIndex(Table *);
@@ -3975,7 +3965,6 @@ void sqlite3InvalidFunction(sqlite3_context *, int, sqlite3_value **);
 sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *);
 int sqlite3VdbeParameterIndex(Vdbe *, const char *, int);
 int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
-void sqlite3ParserReset(Parse *);
 int sqlite3Reprepare(Vdbe *);
 void sqlite3ExprListCheckLength(Parse *, ExprList *, const char *);
 struct coll *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
@@ -4153,4 +4142,24 @@ table_column_nullable_action(struct Table *tab, uint32_t column);
 bool
 table_column_is_nullable(struct Table *tab, uint32_t column);
 
+/**
+ * Initialize a new parser object.
+ * @param parser object to initialize.
+ */
+static inline void
+sql_parser_create(struct Parse *parser)
+{
+	memset(parser, 0, PARSE_HDR_SZ);
+	memset(PARSE_TAIL(parser), 0, PARSE_TAIL_SZ);
+	struct region *region = &fiber()->gc;
+	parser->region_initial_size = region_used(region);
+}
+
+/**
+ * Release the parser object resources.
+ * @param parser object to release.
+ */
+void
+sql_parser_free(struct Parse *parser);
+
 #endif				/* SQLITEINT_H */
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index c77aa9b..e37ad49 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -661,15 +661,16 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
 	sprintf(stmt, "%s%s", outer, expr);
 
 	struct Parse parser;
-	memset(&parser, 0, sizeof(parser));
+	sql_parser_create(&parser);
 	parser.db = db;
 	parser.parse_only = true;
+
 	char *unused;
 	if (sqlite3RunParser(&parser, stmt, &unused) != SQLITE_OK) {
 		diag_set(ClientError, ER_SQL_EXECUTE, expr);
 		return -1;
 	}
 	*result = parser.parsed_expr;
-	sqlite3ParserReset(&parser);
+	sql_parser_free(&parser);
 	return 0;
 }
diff --git a/src/box/sql/treeview.c b/src/box/sql/treeview.c
index 1ff949c..f9077c1 100644
--- a/src/box/sql/treeview.c
+++ b/src/box/sql/treeview.c
@@ -224,7 +224,7 @@ sqlite3TreeViewSelect(TreeView * pView, const Select * p, u8 moreToFollow)
 				}
 				if (pItem->pTab) {
 					sqlite3XPrintf(&x, " tabname=%Q",
-						       pItem->pTab->zName);
+						       pItem->pTab->def->name);
 				}
 				if (pItem->zAlias) {
 					sqlite3XPrintf(&x, " (AS %s)",
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 28c56db..9259343 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -134,7 +134,7 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 	}
 
 	/* Do not create a trigger on a system table */
-	if (sqlite3StrNICmp(pTab->zName, "sqlite_", 7) == 0) {
+	if (sqlite3StrNICmp(pTab->def->name, "sqlite_", 7) == 0) {
 		sqlite3ErrorMsg(pParse,
 				"cannot create trigger on system table");
 		goto trigger_cleanup;
@@ -868,6 +868,7 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 		return 0;
 	memset(&sNC, 0, sizeof(sNC));
 	sNC.pParse = pSubParse;
+	sql_parser_create(pSubParse);
 	pSubParse->db = db;
 	pSubParse->pTriggerTab = pTab;
 	pSubParse->pToplevel = pTop;
@@ -883,7 +884,7 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 			     (pTrigger->op == TK_UPDATE ? "UPDATE" : ""),
 			     (pTrigger->op == TK_INSERT ? "INSERT" : ""),
 			     (pTrigger->op == TK_DELETE ? "DELETE" : ""),
-			     pTab->zName));
+			     pTab->def->name));
 #ifndef SQLITE_OMIT_TRACE
 		sqlite3VdbeChangeP4(v, -1,
 				    sqlite3MPrintf(db, "-- TRIGGER %s",
@@ -934,7 +935,7 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 
 	assert(!pSubParse->pZombieTab);
 	assert(!pSubParse->pTriggerPrg && !pSubParse->nMaxArg);
-	sqlite3ParserReset(pSubParse);
+	sql_parser_free(pSubParse);
 	sqlite3StackFree(db, pSubParse);
 
 	return pPrg;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index f3bd0b7..5f5807c 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -72,11 +72,13 @@ void
 sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
 {
 	assert(pTab != 0);
-	if (!pTab->pSelect) {
+	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+	if (!pTab->def->opts.is_view) {
 		sqlite3_value *pValue = 0;
 		Column *pCol = &pTab->aCol[i];
-		VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
-		assert(i < pTab->nCol);
+		VdbeComment((v, "%s.%s", pTab->def->name,
+			     pTab->def->fields[i].name));
+		assert(i < (int)pTab->def->field_count);
 
 		Expr *expr = NULL;
 		struct space *space =
@@ -212,14 +214,15 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 */
 	aXRef =
 	    sqlite3DbMallocRawNN(db,
-				 sizeof(int) * (pTab->nCol + nIdx) + nIdx + 2);
+				 sizeof(int) *
+				 (pTab->def->field_count + nIdx) + nIdx + 2);
 	if (aXRef == 0)
 		goto update_cleanup;
-	aRegIdx = aXRef + pTab->nCol;
+	aRegIdx = aXRef + pTab->def->field_count;
 	aToOpen = (u8 *) (aRegIdx + nIdx);
 	memset(aToOpen, 1, nIdx + 1);
 	aToOpen[nIdx + 1] = 0;
-	for (i = 0; i < pTab->nCol; i++)
+	for (i = 0; i < (int)pTab->def->field_count; i++)
 		aXRef[i] = -1;
 
 	/* Initialize the name-context */
@@ -236,8 +239,8 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		if (sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr)) {
 			goto update_cleanup;
 		}
-		for (j = 0; j < pTab->nCol; j++) {
-			if (strcmp(pTab->aCol[j].zName,
+		for (j = 0; j < (int)pTab->def->field_count; j++) {
+			if (strcmp(pTab->def->fields[j].name,
 				   pChanges->a[i].zName) == 0) {
 				if (pPk && table_column_is_in_pk(pTab, j)) {
 					chngPk = 1;
@@ -253,7 +256,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 				break;
 			}
 		}
-		if (j >= pTab->nCol) {
+		if (j >= (int)pTab->def->field_count) {
 			sqlite3ErrorMsg(pParse, "no such column: %s",
 					pChanges->a[i].zName);
 			pParse->checkSchema = 1;
@@ -311,13 +314,13 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 
 	if (chngPk || pTrigger || hasFK) {
 		regOld = pParse->nMem + 1;
-		pParse->nMem += pTab->nCol;
+		pParse->nMem += pTab->def->field_count;
 	}
 	if (chngPk || pTrigger || hasFK) {
 		regNewPk = ++pParse->nMem;
 	}
 	regNew = pParse->nMem + 1;
-	pParse->nMem += pTab->nCol;
+	pParse->nMem += pTab->def->field_count;
 
 	/* If we are trying to update a view, realize that view into
 	 * an ephemeral table.
@@ -326,7 +329,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	if (isView) {
 		sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
 		/* Number of columns from SELECT plus ID.*/
-		nKey = pTab->nCol + 1;
+		nKey = pTab->def->field_count + 1;
 	}
 #endif
 
@@ -478,7 +481,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 						 pTrigger, pChanges, 0,
 						 TRIGGER_BEFORE | TRIGGER_AFTER,
 						 pTab, onError);
-		for (i = 0; i < pTab->nCol; i++) {
+		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			if (oldmask == 0xffffffff
 			    || (i < 32 && (oldmask & MASKBIT32(i)) != 0)
 			    || table_column_is_in_pk(pTab, i)) {
@@ -509,7 +512,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	newmask =
 	    sqlite3TriggerColmask(pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE,
 				  pTab, onError);
-	for (i = 0; i < pTab->nCol; i++) {
+	for (i = 0; i < (int)pTab->def->field_count; i++) {
 		if (i == pTab->iPKey) {
 			sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i);
 		} else {
@@ -565,7 +568,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		 * all columns not modified by the update statement into their
 		 * registers in case this has happened.
 		 */
-		for (i = 0; i < pTab->nCol; i++) {
+		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			if (aXRef[i] < 0 && i != pTab->iPKey) {
 				sqlite3ExprCodeGetColumnOfTable(v, pTab,
 								iDataCur, i,
diff --git a/src/box/sql/util.c b/src/box/sql/util.c
index 8c4e7b9..401b215 100644
--- a/src/box/sql/util.c
+++ b/src/box/sql/util.c
@@ -130,15 +130,6 @@ sqlite3Strlen30(const char *z)
 }
 
 /*
- * Return the declared type of a column.
- */
-inline enum field_type
-sqlite3ColumnType(Column * pCol)
-{
-	return pCol->type;
-}
-
-/*
  * Helper function for sqlite3Error() - called rarely.  Broken out into
  * a separate routine to avoid unnecessary register saves on entry to
  * sqlite3Error().
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 013460f..dc779b4 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -4799,7 +4799,7 @@ case OP_RenameTable: {
 		sqlite3HashInsert(&db->pSchema->fkeyHash, zNewTableName, pFKey);
 	}
 
-	sqlite3UnlinkAndDeleteTable(db, pTab->zName);
+	sqlite3UnlinkAndDeleteTable(db, pTab->def->name);
 
 	initData.db = db;
 	initData.pzErrMsg = &p->zErrMsg;
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index b3998ea..0ccca77 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4739,10 +4739,13 @@ table_column_is_nullable(struct Table *tab, uint32_t column)
 		assert(format);
 		assert(format->field_count > column);
 
-		return format->fields[column].nullable_action ==
-			ON_CONFLICT_ACTION_NONE;
+		return nullable_action_to_is_nullable(
+			format->fields[column].nullable_action);
 	} else {
 		/* tab is ephemeral (in SQLite sense).  */
-		return tab->aCol[column].notNull == 0;
+		assert(tab->def->fields[column].is_nullable ==
+		       nullable_action_to_is_nullable(
+			tab->def->fields[column].nullable_action));
+		return tab->def->fields[column].is_nullable;
 	}
 }
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 7a7103c..9ab6295 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -716,7 +716,7 @@ constructAutomaticIndex(Parse * pParse,			/* The parsing context */
 				sqlite3_log(SQLITE_WARNING_AUTOINDEX,
 					    "automatic index on %s(%s)",
 					    pTable->zName,
-					    pTable->aCol[iCol].zName);
+					    pTable->def->fields[iCol].name);
 				sentWarning = 1;
 			}
 			if ((idxCols & cMask) == 0) {
@@ -1638,8 +1638,8 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)
 #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->zName);
+	sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias :
+			   pTab->def->name);
 #endif
 	const char *zName;
 	if (p->pIndex && (zName = p->pIndex->zName) != 0) {
@@ -4514,9 +4514,9 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 			sqlite3OpenTable(pParse, pTabItem->iCursor, pTab, op);
 			assert(pTabItem->iCursor == pLevel->iTabCur);
 			testcase(pWInfo->eOnePass == ONEPASS_OFF
-				 && pTab->nCol == BMS - 1);
+				 && pTab->def->field_count == BMS - 1);
 			testcase(pWInfo->eOnePass == ONEPASS_OFF
-				 && pTab->nCol == BMS);
+				 && pTab->def->field_count == BMS);
 #ifdef SQLITE_ENABLE_CURSOR_HINTS
 			if (pLoop->pIndex != 0) {
 				sqlite3VdbeChangeP5(v,
@@ -4749,7 +4749,7 @@ sqlite3WhereEnd(WhereInfo * pWInfo)
 		}
 		VdbeModuleComment((v, "End WHERE-loop%d: %s", i,
 				   pWInfo->pTabList->a[pLevel->iFrom].pTab->
-				   zName));
+				   def->name));
 	}
 
 	/* The "break" point is here, just past the end of the outer loop.
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index f1112f2..9d4055a 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -50,7 +50,7 @@ explainIndexColumnName(Index * pIdx, int i)
 	i = pIdx->aiColumn[i];
 	if (i == XN_EXPR)
 		return "<expr>";
-	return pIdx->pTable->aCol[i].zName;
+	return pIdx->pTable->def->fields[i].name;
 }
 
 /*
@@ -1158,7 +1158,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->zName));
+		VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->def->name));
 		pLevel->op = OP_Goto;
 	} else if (pLoop->wsFlags & WHERE_INDEXED) {
 		/* Case 4: A scan using an index.
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 86ee273..e602111 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1502,13 +1502,13 @@ sqlite3WhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 	if (pArgs == 0)
 		return;
 	for (j = k = 0; j < pArgs->nExpr; j++) {
-		while (k < pTab->nCol) {
+		while (k < (int)pTab->def->field_count) {
 			k++;
 		}
-		if (k >= pTab->nCol) {
+		if (k >= (int)pTab->def->field_count) {
 			sqlite3ErrorMsg(pParse,
 					"too many arguments on %s() - max %d",
-					pTab->zName, j);
+					pTab->def->name, j);
 			return;
 		}
 		pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
-- 
2.7.4

  parent reply	other threads:[~2018-05-11  8:49 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-11  8:49 [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures Kirill Shcherbatov
2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-11  8:49 ` Kirill Shcherbatov [this message]
2018-05-11 20:59   ` [tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column Vladislav Shpilevoy
2018-05-14 11:20     ` Kirill Shcherbatov
2018-05-14 13:39       ` Vladislav Shpilevoy
2018-05-15 15:56         ` Kirill Shcherbatov
2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 3/3] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-14 11:20     ` Kirill Shcherbatov
2018-05-11  8:58 ` [tarantool-patches] Re: [PATCH v5 0/3] sql: refactor SQL Parser structures Vladislav Shpilevoy
2018-05-11 19:40   ` [tarantool-patches] Re[2]: [tarantool-patches] " Kirill Shcherbatov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b0e872bbbb844bf9527aaa481f7c76894f195332.1526028449.git.kshcherbatov@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox