Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations
@ 2018-04-25 16:52 Kirill Shcherbatov
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma Kirill Shcherbatov
                   ` (5 more replies)
  0 siblings, 6 replies; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 16:52 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
Issue: https://github.com/tarantool/tarantool/issues/3272

Kirill Shcherbatov (4):
  sql: Fix code style in sqlite3Pragma.
  sql: Remove zName and nColumn from SQL.
  sql: Removed type from SQL.
  sql: Region-based allocations.

 src/box/space_def.c     |  29 ++++---
 src/box/sql.c           |  90 ++++++++++++++++---
 src/box/sql.h           |  32 +++++++
 src/box/sql/alter.c     |  32 ++++---
 src/box/sql/analyze.c   |   5 +-
 src/box/sql/build.c     | 226 +++++++++++++++++++++++-------------------------
 src/box/sql/delete.c    |   6 +-
 src/box/sql/expr.c      |  11 +--
 src/box/sql/fkey.c      |  20 ++---
 src/box/sql/insert.c    |  55 ++++++------
 src/box/sql/pragma.c    |  84 +++++++++---------
 src/box/sql/prepare.c   |  26 +++---
 src/box/sql/resolve.c   |  16 ++--
 src/box/sql/select.c    | 120 ++++++++++++++-----------
 src/box/sql/sqliteInt.h |   8 +-
 src/box/sql/tokenize.c  |   3 +
 src/box/sql/trigger.c   |   2 +
 src/box/sql/update.c    |  29 ++++---
 src/box/sql/util.c      |   9 --
 src/box/sql/where.c     |   6 +-
 src/box/sql/wherecode.c |   2 +-
 src/box/sql/whereexpr.c |   4 +-
 22 files changed, 477 insertions(+), 338 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma.
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
@ 2018-04-25 16:52 ` Kirill Shcherbatov
  2018-04-26 11:47   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL Kirill Shcherbatov
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 16:52 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

---
 src/box/sql/pragma.c | 56 +++++++++++++++++++++++++---------------------------
 1 file changed, 27 insertions(+), 29 deletions(-)

diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index e41f69b..4a68cad 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -544,37 +544,35 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
 	case PragTyp_FOREIGN_KEY_LIST:{
-			if (zRight) {
-				FKey *pFK;
-				Table *pTab;
-				pTab = sqlite3HashFind(&db->pSchema->tblHash,
-						       zRight);
-				if (pTab != NULL) {
-					pFK = pTab->pFKey;
-					if (pFK) {
-						int i = 0;
-						pParse->nMem = 8;
-						while (pFK) {
-							int j;
-							for (j = 0;
-							     j < pFK->nCol;
-							     j++) {
-								sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j, pFK->zTo, pTab->aCol[pFK->aCol[j].iFrom].zName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]),	/* ON UPDATE */
-										     actionName(pFK->aAction[0]),	/* ON DELETE */
-										     "NONE");
-								sqlite3VdbeAddOp2
-								    (v,
-								     OP_ResultRow,
-								     1, 8);
-							}
-							++i;
-							pFK = pFK->pNextFrom;
-						}
-					}
-				}
-			}
+		if (!zRight)
+			break;
+		FKey *pFK;
+		Table *pTab;
+		pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
+		if (pTab == NULL)
 			break;
+		pFK = pTab->pFKey;
+		if (!pFK)
+			break;
+		int i = 0;
+		pParse->nMem = 8;
+		while (pFK) {
+			for (int j = 0; j < pFK->nCol; j++) {
+				const char *name =
+					pTab->aCol[pFK->aCol[j].iFrom].zName;
+				sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j,
+						     pFK->zTo, name,
+						     pFK->aCol[j].zCol,
+						     actionName(pFK->aAction[1]),
+						     actionName(pFK->aAction[0]),
+						     "NONE");
+				sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
+			}
+			++i;
+			pFK = pFK->pNextFrom;
 		}
+		break;
+	}
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL.
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-04-25 16:52 ` Kirill Shcherbatov
  2018-04-25 17:10   ` [tarantool-patches] " Kirill Shcherbatov
  2018-04-26 11:47   ` Vladislav Shpilevoy
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 3/4] sql: Removed type " Kirill Shcherbatov
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 16:52 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

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

Needed for #3272.
---
 src/box/space_def.c     |  29 ++++----
 src/box/sql.c           |  61 +++++++++++++---
 src/box/sql.h           |  23 ++++++
 src/box/sql/alter.c     |  32 ++++++---
 src/box/sql/analyze.c   |   5 +-
 src/box/sql/build.c     | 181 ++++++++++++++++++++++--------------------------
 src/box/sql/delete.c    |   6 +-
 src/box/sql/expr.c      |  11 +--
 src/box/sql/fkey.c      |  20 +++---
 src/box/sql/insert.c    |  55 ++++++++-------
 src/box/sql/pragma.c    |  24 ++++---
 src/box/sql/resolve.c   |  16 +++--
 src/box/sql/select.c    |  92 ++++++++++++------------
 src/box/sql/sqliteInt.h |   4 +-
 src/box/sql/update.c    |  29 ++++----
 src/box/sql/where.c     |   6 +-
 src/box/sql/wherecode.c |   2 +-
 src/box/sql/whereexpr.c |   4 +-
 18 files changed, 338 insertions(+), 262 deletions(-)

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..38aeac6 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1433,7 +1433,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 	struct SqliteIndex *pk_idx = sqlite3PrimaryKeyIndex(pTable);
 	int pk_forced_int = -1;
 	char *base = buf, *p;
-	int i, n = pTable->nCol;
+	int i, n = pTable->def->field_count;
 
 	p = enc->encode_array(base, n);
 
@@ -1449,15 +1449,15 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 		const char *t;
 		struct coll *coll = aCol[i].coll;
 		struct field_def *field = &pTable->def->fields[i];
-		struct Expr *def = field->default_value_expr;
+		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);
 		if (i == pk_forced_int) {
 			t = "integer";
@@ -1477,11 +1477,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 			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);
@@ -1681,3 +1679,48 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
 
 	return space->def->fields[fieldno].default_value_expr;
 }
+
+Table *
+sql_ephemeral_table_new(Parse *parser)
+{
+	sqlite3 *db = parser->db;
+	struct space_def *def = NULL;
+	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
+	if (table != NULL) {
+		def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
+				    &space_opts_default, NULL, 0);
+	}
+	if (def == NULL) {
+		sqlite3DbFree(db, table);
+		parser->rc = SQLITE_NOMEM_BKPT;
+		parser->nErr++;
+		return NULL;
+	}
+	table->def = def;
+	return table;
+}
+
+int
+sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
+{
+	struct space_def *old_def = pTable->def;
+	struct space_def *new_def = NULL;
+	new_def = space_def_new(old_def->id, old_def->uid,
+				old_def->field_count, old_def->name,
+				strlen(old_def->name), old_def->engine_name,
+				strlen(old_def->engine_name), &old_def->opts,
+				old_def->fields, old_def->field_count);
+	if (new_def == NULL) {
+		sqlite3OomFault(db);
+		return -1;
+	}
+	struct field_def *fields = old_def->fields;
+	for (uint32_t i = 0; i < old_def->field_count; ++i) {
+		sqlite3DbFree(db, fields[i].default_value);
+		sqlite3DbFree(db, fields[i].name);
+	}
+	space_def_delete(old_def);
+	sqlite3DbFree(db, fields);
+	pTable->def = new_def;
+	return 0;
+}
diff --git a/src/box/sql.h b/src/box/sql.h
index db92d80..9fb3ad1 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,28 @@ 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 pParse SQL Parser object.
+ * @param zName 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);
+
+/**
+ * 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 pTable 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..bedf602 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -144,6 +144,9 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
 void
 sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 {
+	/* This function is not implemented yet #3075. */
+	assert(false);
+
 	Table *pNew;		/* Copy of pParse->pNewTable */
 	Table *pTab;		/* Table being altered */
 	const char *zTab;	/* Table name */
@@ -161,10 +164,10 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 	assert(pNew);
 
 	zTab = &pNew->zName[16];	/* Skip the "sqlite_altertab_" prefix on the name */
-	pCol = &pNew->aCol[pNew->nCol - 1];
+	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);
 
@@ -248,10 +251,13 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 void
 sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 {
+	/* This function is not implemented yet #3075. */
+	assert(false);
+
 	Table *pNew;
 	Table *pTab;
 	Vdbe *v;
-	int i;
+	uint32_t i;
 	int nAlloc;
 	sqlite3 *db = pParse->db;
 
@@ -281,13 +287,17 @@ 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);
@@ -295,10 +305,10 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 		assert(db->mallocFailed);
 		goto exit_begin_add_column;
 	}
-	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->nCol);
-	for (i = 0; i < pNew->nCol; i++) {
+	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
+	for (i = 0; i < pNew->def->field_count; i++) {
 		Column *pCol = &pNew->aCol[i];
-		pCol->zName = sqlite3DbStrDup(db, pCol->zName);
+		/* FIXME: pCol->zName = sqlite3DbStrDup(db, pCol->zName); */
 		pCol->coll = NULL;
 	}
 	pNew->pSchema = db->pSchema;
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index f0054c5..a0ad511 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -1023,9 +1023,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);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a2b712a..c712b46 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);
 }
 
 /*
@@ -393,12 +386,8 @@ deleteTable(sqlite3 * db, Table * pTable)
 	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;
+	if (pTable->def != NULL)
 		space_def_delete(pTable->def);
-		sqlite3DbFree(db, fields);
-	}
 	sqlite3DbFree(db, pTable);
 
 	/* Verify that no lookaside memory was used by schema tables */
@@ -496,28 +485,16 @@ sqlite3PrimaryKeyIndex(Table * pTab)
  * Create and initialize a new SQL Table object.
  * @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);
+	if (table == NULL)
 		return NULL;
-	}
-
-	table->def = def;
 	table->zName = name;
 	table->iPKey = -1;
 	table->iAutoIncPKey = -1;
@@ -629,7 +606,6 @@ 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) {
@@ -676,12 +652,12 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	if ((p = pParse->pNewTable) == 0)
 		return;
 #if SQLITE_MAX_COLUMN
-	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
+	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
 		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
 		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);
 	if (z == 0)
@@ -689,28 +665,27 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	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]));
+				     (p->def->field_count + 8) * sizeof(p->aCol[0]));
 		if (aNew == 0) {
 			sqlite3DbFree(db, z);
 			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;
-
+	p->def->fields[p->def->field_count].name = z;
 	if (pType->n == 0) {
 		/* If there is no type specified, columns have the default affinity
 		 * 'BLOB' and type SCALAR.
@@ -740,7 +715,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 			sqlite3_free(zType);
 		}
 	}
-	p->nCol++;
 	p->def->field_count++;
 	pParse->constraintName.n = 0;
 }
@@ -756,9 +730,9 @@ 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->aCol[p->def->field_count - 1].notNull = (u8) onError;
 }
 
 /*
@@ -871,38 +845,30 @@ void
 sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 {
 	Table *p;
-	Column *pCol;
 	sqlite3 *db = pParse->db;
 	p = pParse->pNewTable;
 	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);
@@ -947,7 +913,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 	}
 	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 +928,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;
@@ -1036,7 +1002,7 @@ 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)
@@ -1087,7 +1053,7 @@ 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);
+		assert(column < (uint32_t)table->def->field_count);
 		return table->aCol[column].coll;
 	}
 
@@ -1306,9 +1272,8 @@ 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;
-	}
+	for (i = 0; i < (int)p->def->field_count; i++)
+		n += identLength(p->def->fields[i].name) + 5;
 	n += identLength(p->zName);
 	if (n < 50) {
 		zSep = "";
@@ -1319,7 +1284,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);
@@ -1329,7 +1294,7 @@ createTableStmt(sqlite3 * db, Table * p)
 	k = sqlite3Strlen30(zStmt);
 	identPut(zStmt, &k, p->zName);
 	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 +1308,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 +1338,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 +1358,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,7 +1399,7 @@ 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;
 			}
@@ -1446,7 +1412,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));
@@ -1663,7 +1629,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 			  sqlite3DbStrDup(pParse->db, p->zName), 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);
@@ -1853,6 +1819,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	p = pParse->pNewTable;
 	if (p == 0)
 		return;
+	if (sql_table_def_rebuild(db, p) != 0)
+		return;
 
 	assert(!db->init.busy || !pSelect);
 
@@ -2101,7 +2069,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 +2087,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);
 		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
@@ -2138,7 +2106,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	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 +2117,9 @@ 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);
 			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 +2129,15 @@ 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);
+			struct space_def *def = pSelTab->def;
+			pSelTab->def = pTable->def;
+			pSelTab->def->field_count = 0;
+			pTable->def = def;
 			pTable->aCol = pSelTab->aCol;
-			pSelTab->nCol = 0;
 			pSelTab->aCol = 0;
 		} else {
-			pTable->nCol = 0;
+			pTable->def->field_count = 0;
 			nErr++;
 		}
 		sqlite3DeleteTable(db, pSelTab);
@@ -2195,8 +2164,18 @@ sqliteViewResetAll(sqlite3 * db)
 		Table *pTab = sqliteHashData(i);
 		if (pTab->pSelect) {
 			sqlite3DeleteColumnNames(db, pTab);
+			struct space_def *old_def = pTab->def;
+			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 +2448,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 +2487,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);
@@ -2966,7 +2945,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,7 +3999,7 @@ 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);
@@ -4181,7 +4162,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..65d20fa 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -382,7 +382,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++;
@@ -734,13 +734,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
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 0c86761..5f7d741 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;
 }
 
@@ -4242,20 +4242,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..c15ad8c 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,
@@ -650,7 +650,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);
@@ -863,12 +863,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;
@@ -1282,14 +1282,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
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 1cb9525..06635ee 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -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);
@@ -1143,7 +1146,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 		case ON_CONFLICT_ACTION_FAIL: {
 				char *zMsg =
 				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
-						   pTab->aCol[i].zName);
+						   pTab->def->fields[i].name);
 				sqlite3VdbeAddOp3(v, OP_HaltIfNull,
 						  SQLITE_CONSTRAINT_NOTNULL,
 						  onError, regNewData + 1 + i);
@@ -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 */
 			}
 			if (IsPrimaryKeyIndex(pIdx) || uniqueByteCodeNeeded) {
 				sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,
-						  pTab->nCol, aRegIdx[ix]);
+						  pTab->def->field_count, aRegIdx[ix]);
 				VdbeComment((v, "for %s", pIdx->zName));
 			}
 		} else {
@@ -1401,7 +1405,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 					sqlite3VdbeAddOp3(v, OP_Column,
 							  iThisCur, x, regR + i);
 					VdbeComment((v, "%s.%s", pTab->zName,
-						pTab->aCol[pPk->aiColumn[i]].zName));
+						pTab->def->fields[
+							pPk->aiColumn[i]].name));
 				}
 			}
 			if (isUpdate && uniqueByteCodeNeeded) {
@@ -1792,13 +1797,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 4a68cad..e93f377 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++) {
 						}
@@ -381,7 +382,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 					char *expr_str = space->
 						def->fields[i].default_value;
 					sqlite3VdbeMultiLoad(v, 1, "issisi",
-							     i, pCol->zName,
+							     i,
+							     pTab->def->fields[i].
+								     name,
 							     field_type_strs[
 							     sqlite3ColumnType
 							     (pCol)],
@@ -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) {
 			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,8 +618,9 @@ 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);
@@ -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/resolve.c b/src/box/sql/resolve.c
index 823062a..109c410 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -240,7 +240,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 			     i++, pItem++) {
 				pTab = pItem->pTab;
 				assert(pTab != 0 && pTab->zName != 0);
-				assert(pTab->nCol > 0);
+				assert(pTab->def->field_count > 0);
 				if (pItem->pSelect
 				    && (pItem->pSelect->
 					selFlags & SF_NestedFrom) != 0) {
@@ -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 =
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 5a50413..1390c5d 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,7 +1661,8 @@ 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);
@@ -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,
@@ -1799,8 +1800,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 */
@@ -1822,8 +1822,11 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 		aCol = 0;
 	}
 	assert(nCol == (i16) nCol);
-	*pnCol = nCol;
-	*paCol = aCol;
+	assert(pTable->def->fields == NULL);
+	pTable->def->fields =
+		sqlite3DbMallocZero(db, nCol*sizeof(pTable->def->fields[0]));
+	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 +1848,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 +1877,24 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			if (cnt > 3)
 				sqlite3_randomness(sizeof(cnt), &cnt);
 		}
-		pCol->zName = zName;
+		pTable->def->fields[i].name = zName;
 		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
 			sqlite3OomFault(db);
 		}
 	}
 	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 (sql_table_def_rebuild(db, pTable) != 0)
+		rc = SQLITE_NOMEM_BKPT;
+	if (rc != SQLITE_OK) {
 		sqlite3DbFree(db, aCol);
-		*paCol = 0;
-		*pnCol = 0;
-		return SQLITE_NOMEM_BKPT;
+		sqlite3DbFree(db, pTable->def->fields);
+		pTable->def->fields = NULL;
+		pTable->def->field_count = 0;
+		pTable->aCol = 0;
+		rc = SQLITE_NOMEM_BKPT;
 	}
-	return SQLITE_OK;
+	return rc;
 }
 
 /*
@@ -1918,13 +1923,15 @@ 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);
@@ -1963,10 +1970,9 @@ 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);
+	if (pTab == NULL)
 		return 0;
-	}
 	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
 	 * is disabled
 	 */
@@ -1975,8 +1981,7 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	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,8 +4502,8 @@ 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);
+		if (pTab == NULL)
 			return WRC_Abort;
 		pTab->nTabRef = 1;
 		pTab->zName = sqlite3DbStrDup(db, pCte->zName);
@@ -4562,8 +4567,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 =
@@ -4684,8 +4688,8 @@ selectExpander(Walker * pWalker, Select * p)
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
 			pFrom->pTab = pTab =
-			    sqlite3DbMallocZero(db, sizeof(Table));
-			if (pTab == 0)
+				sql_ephemeral_table_new(pParse);
+			if (pTab == NULL)
 				return WRC_Abort;
 			pTab->nTabRef = 1;
 			pTab->zName =
@@ -4693,8 +4697,7 @@ selectExpander(Walker * pWalker, Select * p)
 			while (pSel->pPrior) {
 				pSel = pSel->pPrior;
 			}
-			sqlite3ColumnsFromExprList(pParse, pSel->pEList,
-						   &pTab->nCol, &pTab->aCol);
+			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
 			pTab->iPKey = -1;
 			pTab->nRowLogEst = 200;
 			assert(200 == sqlite3LogEst(1048576));
@@ -4727,10 +4730,10 @@ selectExpander(Walker * pWalker, Select * p)
 				    sqlite3SelectDup(db, pTab->pSelect, 0);
 				sqlite3SelectSetName(pFrom->pSelect,
 						     pTab->zName);
-				nCol = pTab->nCol;
-				pTab->nCol = -1;
+				nCol = pTab->def->field_count;
+				pTab->def->field_count = -1;
 				sqlite3WalkSelect(pWalker, pFrom->pSelect);
-				pTab->nCol = nCol;
+				pTab->def->field_count = nCol;
 			}
 #endif
 		}
@@ -4835,9 +4838,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 */
@@ -5372,10 +5374,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->zName,
 					pSub->pEList->nExpr);
 			goto select_end;
 		}
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 8bb45c9..8e1c135 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,7 +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;
@@ -1950,7 +1949,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
@@ -3515,7 +3513,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 *);
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index f3bd0b7..464feee 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -75,8 +75,8 @@ sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
 	if (!pTab->pSelect) {
 		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->zName, pTab->def->fields[i].name));
+		assert(i < (int)pTab->def->field_count);
 
 		Expr *expr = NULL;
 		struct space *space =
@@ -212,14 +212,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 +237,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 +254,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 +312,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 +327,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 +479,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 +510,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 +566,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/where.c b/src/box/sql/where.c
index 7a7103c..fc0f84c 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) {
@@ -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,
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index f1112f2..233fde0 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;
 }
 
 /*
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 86ee273..1b0d961 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1502,10 +1502,10 @@ 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);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v3 3/4] sql: Removed type from SQL.
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma Kirill Shcherbatov
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL Kirill Shcherbatov
@ 2018-04-25 16:52 ` Kirill Shcherbatov
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 4/4] sql: Region-based allocations Kirill Shcherbatov
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 16:52 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Needed for #3272.
---
 src/box/sql.c           | 13 +++++++++----
 src/box/sql/build.c     | 12 +++++++-----
 src/box/sql/pragma.c    | 12 ++++++------
 src/box/sql/select.c    |  8 ++++----
 src/box/sql/sqliteInt.h |  2 --
 src/box/sql/util.c      |  9 ---------
 6 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 38aeac6..6d4255e 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->def->field_count;
+	int i, n = def->field_count;
 
 	p = enc->encode_array(base, n);
 
@@ -1441,14 +1443,14 @@ 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 field_def *field = &def->fields[i];
 		const char *zToken = field->default_value;
 		int base_len = 4;
 		if (coll != NULL)
@@ -1521,6 +1523,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;
@@ -1532,7 +1537,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;
 	}
 
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index c712b46..584e6b1 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -685,7 +685,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	}
 	pCol = &p->aCol[p->def->field_count];
 	memset(pCol, 0, sizeof(p->aCol[0]));
-	p->def->fields[p->def->field_count].name = z;
+	struct field_def *column_def = &p->def->fields[p->def->field_count];
+	column_def->name = z;
 	if (pType->n == 0) {
 		/* If there is no type specified, columns have the default affinity
 		 * 'BLOB' and type SCALAR.
@@ -693,8 +694,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.
@@ -703,16 +704,16 @@ 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->def->field_count++;
@@ -939,9 +940,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;
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index e93f377..0cf103c 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -381,13 +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,
-							     pTab->def->fields[i].
-								     name,
-							     field_type_strs[
-							     sqlite3ColumnType
-							     (pCol)],
+							     i, name,
+							     field_type_strs[type],
 							     nullable == 0,
 							     expr_str, k);
 					sqlite3VdbeAddOp2(v, OP_ResultRow, 1,
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 1390c5d..9eeff8e 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1664,12 +1664,12 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
 				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
 			}
@@ -1937,7 +1937,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 		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;
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 8e1c135..94d5c80 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,7 +1867,6 @@ struct Savepoint {
  * of this structure.
  */
 struct Column {
-	enum field_type type;	/* Column type. */
 	/** Collating sequence. */
 	struct coll *coll;
 	/**
@@ -3388,7 +3387,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);
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().
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v3 4/4] sql: Region-based allocations.
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
                   ` (2 preceding siblings ...)
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 3/4] sql: Removed type " Kirill Shcherbatov
@ 2018-04-25 16:52 ` Kirill Shcherbatov
  2018-04-26 11:47   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-26 11:47 ` [tarantool-patches] Re: [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Vladislav Shpilevoy
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
  5 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 16:52 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Function sql_field_retrieve could leak names
in error cases. Let allocate all memory with
region allocator.

Needed for #3272.
---
 src/box/sql.c           | 44 +++++++++++++++++++++++++++++++-------------
 src/box/sql.h           |  9 +++++++++
 src/box/sql/build.c     | 37 ++++++++++++++++++++++++-------------
 src/box/sql/prepare.c   | 26 ++++++++++++++++----------
 src/box/sql/select.c    | 24 +++++++++++++++++++++---
 src/box/sql/sqliteInt.h |  2 ++
 src/box/sql/tokenize.c  |  3 +++
 src/box/sql/trigger.c   |  2 ++
 8 files changed, 108 insertions(+), 39 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 6d4255e..2893d70 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1685,22 +1685,43 @@ 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)
+{
+	struct space_def *def = NULL;
+	struct region *region = &fiber()->gc;
+	size_t size = sizeof(struct space_def) + 1;
+	def = (struct space_def *)region_alloc(region, size);
+	if (def != NULL) {
+		memset(def, 0, size);
+		def->dict =
+			(struct tuple_dictionary *)
+				region_alloc(region,
+					     sizeof(struct tuple_dictionary));
+	}
+	if (def == NULL || def->dict == NULL) {
+		parser->rc = SQLITE_NOMEM_BKPT;
+		parser->nErr++;
+		return NULL;
+	}
+	def->dict->refs = 1;
+	def->opts.temporary = true;
+	return def;
+}
+
 Table *
 sql_ephemeral_table_new(Parse *parser)
 {
 	sqlite3 *db = parser->db;
 	struct space_def *def = NULL;
 	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
-	if (table != NULL) {
-		def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
-				    &space_opts_default, NULL, 0);
-	}
+	if (table != NULL)
+		def = sql_ephemeral_space_def_new(parser);
 	if (def == NULL) {
 		sqlite3DbFree(db, table);
-		parser->rc = SQLITE_NOMEM_BKPT;
-		parser->nErr++;
 		return NULL;
 	}
+
 	table->def = def;
 	return table;
 }
@@ -1708,6 +1729,9 @@ sql_ephemeral_table_new(Parse *parser)
 int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
+	assert(pTable->def->opts.temporary == true);
+
+	/* 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,
@@ -1719,13 +1743,7 @@ sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 		sqlite3OomFault(db);
 		return -1;
 	}
-	struct field_def *fields = old_def->fields;
-	for (uint32_t i = 0; i < old_def->field_count; ++i) {
-		sqlite3DbFree(db, fields[i].default_value);
-		sqlite3DbFree(db, fields[i].name);
-	}
-	space_def_delete(old_def);
-	sqlite3DbFree(db, fields);
 	pTable->def = new_def;
+	pTable->def->opts.temporary = false;
 	return 0;
 }
diff --git a/src/box/sql.h b/src/box/sql.h
index 9fb3ad1..410653b 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -155,6 +155,15 @@ struct Table *
 sql_ephemeral_table_new(struct Parse *parser);
 
 /**
+ * Create and initialize a new ephemeric space_def object.
+ * @param pParse SQL Parser object.
+ * @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);
+
+/**
  * Rebuild struct def in Table with memory allocated on a single
  * malloc. Fields and strings are expected to be allocated with
  * sqlite3DbMalloc.
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 584e6b1..4db4356 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -386,7 +386,9 @@ deleteTable(sqlite3 * db, Table * pTable)
 	sqlite3DbFree(db, pTable->zColAff);
 	sqlite3SelectDelete(db, pTable->pSelect);
 	sqlite3ExprListDelete(db, pTable->pCheck);
-	if (pTable->def != NULL)
+	/* Do not delete pTable->def allocated not on region. */
+	assert(pTable->def != NULL);
+	if (pTable->def->opts.temporary == false)
 		space_def_delete(pTable->def);
 	sqlite3DbFree(db, pTable);
 
@@ -483,6 +485,7 @@ 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.
@@ -603,29 +606,33 @@ 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(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;
 			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];
@@ -651,6 +658,7 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	sqlite3 *db = pParse->db;
 	if ((p = pParse->pNewTable) == 0)
 		return;
+	assert(p->def->opts.temporary == true);
 #if SQLITE_MAX_COLUMN
 	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
 		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
@@ -659,7 +667,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 #endif
 	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);
@@ -668,7 +677,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	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;
 		}
 	}
@@ -677,10 +685,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		aNew =
 		    sqlite3DbRealloc(db, p->aCol,
 				     (p->def->field_count + 8) * sizeof(p->aCol[0]));
-		if (aNew == 0) {
-			sqlite3DbFree(db, z);
+		if (aNew == 0)
 			return;
-		}
 		p->aCol = aNew;
 	}
 	pCol = &p->aCol[p->def->field_count];
@@ -848,6 +854,7 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 	Table *p;
 	sqlite3 *db = pParse->db;
 	p = pParse->pNewTable;
+	assert(p->def->opts.temporary == true);
 	if (p != 0) {
 		if (!sqlite3ExprIsConstantOrFunction
 		    (pSpan->pExpr, db->init.busy)) {
@@ -1956,6 +1963,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (db->init.busy) {
 		Table *pOld;
 		Schema *pSchema = p->pSchema;
+		assert(p->def->opts.temporary == false);
 		pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
 		if (pOld) {
 			assert(p == pOld);	/* Malloc must have failed inside HashInsert() */
@@ -2127,6 +2135,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 								       pSel);
 			}
 		} else if (pSelTab) {
+			assert(pTable->def->opts.temporary == false);
 			/* CREATE VIEW name AS...  without an argument list.  Construct
 			 * the column names from the SELECT statement that defines the view.
 			 */
@@ -2167,6 +2176,8 @@ sqliteViewResetAll(sqlite3 * db)
 		if (pTab->pSelect) {
 			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,
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 2ab8751..7dc14f6 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -211,17 +211,19 @@ sqlite3InitDatabase(sqlite3 * db)
 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;
+	if (pParse == NULL)
+		return;
+	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;
+	struct region *region = &fiber()->gc;
+	region_truncate(region, pParse->region_initial_size);
 }
 
 /*
@@ -265,6 +267,10 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
 	 * works even if READ_UNCOMMITTED is set.
 	 */
 	sParse.db = db;
+	/* Store region initial size to revert future allocations */
+	struct region *region = &fiber()->gc;
+	sParse.region_initial_size = region_used(region);
+
 	if (nBytes >= 0 && (nBytes == 0 || zSql[nBytes - 1] != 0)) {
 		char *zSqlCopy;
 		int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 9eeff8e..03bfcf9 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1822,9 +1822,20 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 		aCol = 0;
 	}
 	assert(nCol == (i16) nCol);
+
+	struct region *region = &fiber()->gc;
 	assert(pTable->def->fields == NULL);
+	if (pTable->def->opts.temporary == false) {
+		/* CREATE VIEW name AS...  without an argument list.  Construct
+		 * the column names from the SELECT statement that defines the view.
+		 */
+		pTable->def->field_count = 0;
+		space_def_delete(pTable->def);
+		pTable->def = sql_ephemeral_space_def_new(pParse);
+	}
 	pTable->def->fields =
-		sqlite3DbMallocZero(db, nCol*sizeof(pTable->def->fields[0]));
+		region_alloc(region, nCol*sizeof(pTable->def->fields[0]));
+	memset(pTable->def->fields, 0, nCol*sizeof(pTable->def->fields[0]));
 	pTable->def->field_count = (uint32_t)nCol;
 	pTable->aCol = aCol;
 
@@ -1877,9 +1888,16 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			if (cnt > 3)
 				sqlite3_randomness(sizeof(cnt), &cnt);
 		}
-		pTable->def->fields[i].name = zName;
-		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
+		uint32_t zNameLen = (uint32_t)strlen(zName);
+		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol)
+			sqlite3OomFault(db);
+		pTable->def->fields[i].name =
+			region_alloc(region, zNameLen + 1);
+		if (pTable->def->fields[i].name == NULL) {
 			sqlite3OomFault(db);
+		} else {
+			memcpy(pTable->def->fields[i].name, zName, zNameLen);
+			pTable->def->fields[i].name[zNameLen] = '\0';
 		}
 	}
 	sqlite3HashClear(&ht);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 94d5c80..520b74c 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2933,6 +2933,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,
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index c77aa9b..9055bd0 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -664,6 +664,9 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
 	memset(&parser, 0, sizeof(parser));
 	parser.db = db;
 	parser.parse_only = true;
+	struct region *region = &fiber()->gc;
+	parser.region_initial_size = region_used(region);
+
 	char *unused;
 	if (sqlite3RunParser(&parser, stmt, &unused) != SQLITE_OK) {
 		diag_set(ClientError, ER_SQL_EXECUTE, expr);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 28c56db..9b03be1 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -873,6 +873,8 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 	pSubParse->pToplevel = pTop;
 	pSubParse->eTriggerOp = pTrigger->op;
 	pSubParse->nQueryLoop = pParse->nQueryLoop;
+	struct region *region = &fiber()->gc;
+	pSubParse->region_initial_size = region_used(region);
 
 	v = sqlite3GetVdbe(pSubParse);
 	if (v) {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 2/4] sql: Remove zName and nColumn from SQL.
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL Kirill Shcherbatov
@ 2018-04-25 17:10   ` Kirill Shcherbatov
  2018-04-26 12:12     ` Vladislav Shpilevoy
  2018-04-26 11:47   ` Vladislav Shpilevoy
  1 sibling, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-25 17:10 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

I've included some extra changes in this commit. Fixed.

---
 src/box/sql/build.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index c712b46..d4624eb 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -854,21 +854,17 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 					"default value of column [%s] is not constant",
 					p->def->fields[p->def->field_count - 1].name);
 		} else {
-			assert(p->def != NULL);
 			struct field_def *field =
 				&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);
+			field->default_value =
+				sqlite3DbStrNDup(db,
+						 (char *)pSpan->zStart,
+						 (int)(pSpan->zEnd - pSpan->zStart));
 			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);
-- 
2.7.4



On 25.04.2018 19:52, Kirill Shcherbatov wrote:
> 1. Removed zName from SQL Column.
> 2. Removed zColumns from SQL Table.
> 3. Refactored Parser to use def_expression directly.
> 4. Introduced sql_table_def_rebuild intended for collect
> fragmented with sql_field_retrieve space_def into memory
> located in one allocation.
> 
> Needed for #3272.
> ---
>  src/box/space_def.c     |  29 ++++----
>  src/box/sql.c           |  61 +++++++++++++---
>  src/box/sql.h           |  23 ++++++
>  src/box/sql/alter.c     |  32 ++++++---
>  src/box/sql/analyze.c   |   5 +-
>  src/box/sql/build.c     | 181 ++++++++++++++++++++++--------------------------
>  src/box/sql/delete.c    |   6 +-
>  src/box/sql/expr.c      |  11 +--
>  src/box/sql/fkey.c      |  20 +++---
>  src/box/sql/insert.c    |  55 ++++++++-------
>  src/box/sql/pragma.c    |  24 ++++---
>  src/box/sql/resolve.c   |  16 +++--
>  src/box/sql/select.c    |  92 ++++++++++++------------
>  src/box/sql/sqliteInt.h |   4 +-
>  src/box/sql/update.c    |  29 ++++----
>  src/box/sql/where.c     |   6 +-
>  src/box/sql/wherecode.c |   2 +-
>  src/box/sql/whereexpr.c |   4 +-
>  18 files changed, 338 insertions(+), 262 deletions(-)
> 
> 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..38aeac6 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1433,7 +1433,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>  	struct SqliteIndex *pk_idx = sqlite3PrimaryKeyIndex(pTable);
>  	int pk_forced_int = -1;
>  	char *base = buf, *p;
> -	int i, n = pTable->nCol;
> +	int i, n = pTable->def->field_count;
>  
>  	p = enc->encode_array(base, n);
>  
> @@ -1449,15 +1449,15 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>  		const char *t;
>  		struct coll *coll = aCol[i].coll;
>  		struct field_def *field = &pTable->def->fields[i];
> -		struct Expr *def = field->default_value_expr;
> +		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);
>  		if (i == pk_forced_int) {
>  			t = "integer";
> @@ -1477,11 +1477,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>  			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);
> @@ -1681,3 +1679,48 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
>  
>  	return space->def->fields[fieldno].default_value_expr;
>  }
> +
> +Table *
> +sql_ephemeral_table_new(Parse *parser)
> +{
> +	sqlite3 *db = parser->db;
> +	struct space_def *def = NULL;
> +	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
> +	if (table != NULL) {
> +		def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
> +				    &space_opts_default, NULL, 0);
> +	}
> +	if (def == NULL) {
> +		sqlite3DbFree(db, table);
> +		parser->rc = SQLITE_NOMEM_BKPT;
> +		parser->nErr++;
> +		return NULL;
> +	}
> +	table->def = def;
> +	return table;
> +}
> +
> +int
> +sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
> +{
> +	struct space_def *old_def = pTable->def;
> +	struct space_def *new_def = NULL;
> +	new_def = space_def_new(old_def->id, old_def->uid,
> +				old_def->field_count, old_def->name,
> +				strlen(old_def->name), old_def->engine_name,
> +				strlen(old_def->engine_name), &old_def->opts,
> +				old_def->fields, old_def->field_count);
> +	if (new_def == NULL) {
> +		sqlite3OomFault(db);
> +		return -1;
> +	}
> +	struct field_def *fields = old_def->fields;
> +	for (uint32_t i = 0; i < old_def->field_count; ++i) {
> +		sqlite3DbFree(db, fields[i].default_value);
> +		sqlite3DbFree(db, fields[i].name);
> +	}
> +	space_def_delete(old_def);
> +	sqlite3DbFree(db, fields);
> +	pTable->def = new_def;
> +	return 0;
> +}
> diff --git a/src/box/sql.h b/src/box/sql.h
> index db92d80..9fb3ad1 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,28 @@ 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 pParse SQL Parser object.
> + * @param zName 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);
> +
> +/**
> + * 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 pTable 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..bedf602 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -144,6 +144,9 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
>  void
>  sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>  {
> +	/* This function is not implemented yet #3075. */
> +	assert(false);
> +
>  	Table *pNew;		/* Copy of pParse->pNewTable */
>  	Table *pTab;		/* Table being altered */
>  	const char *zTab;	/* Table name */
> @@ -161,10 +164,10 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>  	assert(pNew);
>  
>  	zTab = &pNew->zName[16];	/* Skip the "sqlite_altertab_" prefix on the name */
> -	pCol = &pNew->aCol[pNew->nCol - 1];
> +	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);
>  
> @@ -248,10 +251,13 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>  void
>  sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>  {
> +	/* This function is not implemented yet #3075. */
> +	assert(false);
> +
>  	Table *pNew;
>  	Table *pTab;
>  	Vdbe *v;
> -	int i;
> +	uint32_t i;
>  	int nAlloc;
>  	sqlite3 *db = pParse->db;
>  
> @@ -281,13 +287,17 @@ 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);
> @@ -295,10 +305,10 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>  		assert(db->mallocFailed);
>  		goto exit_begin_add_column;
>  	}
> -	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->nCol);
> -	for (i = 0; i < pNew->nCol; i++) {
> +	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
> +	for (i = 0; i < pNew->def->field_count; i++) {
>  		Column *pCol = &pNew->aCol[i];
> -		pCol->zName = sqlite3DbStrDup(db, pCol->zName);
> +		/* FIXME: pCol->zName = sqlite3DbStrDup(db, pCol->zName); */
>  		pCol->coll = NULL;
>  	}
>  	pNew->pSchema = db->pSchema;
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index f0054c5..a0ad511 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -1023,9 +1023,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);
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index a2b712a..c712b46 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);
>  }
>  
>  /*
> @@ -393,12 +386,8 @@ deleteTable(sqlite3 * db, Table * pTable)
>  	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;
> +	if (pTable->def != NULL)
>  		space_def_delete(pTable->def);
> -		sqlite3DbFree(db, fields);
> -	}
>  	sqlite3DbFree(db, pTable);
>  
>  	/* Verify that no lookaside memory was used by schema tables */
> @@ -496,28 +485,16 @@ sqlite3PrimaryKeyIndex(Table * pTab)
>   * Create and initialize a new SQL Table object.
>   * @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);
> +	if (table == NULL)
>  		return NULL;
> -	}
> -
> -	table->def = def;
>  	table->zName = name;
>  	table->iPKey = -1;
>  	table->iAutoIncPKey = -1;
> @@ -629,7 +606,6 @@ 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) {
> @@ -676,12 +652,12 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>  	if ((p = pParse->pNewTable) == 0)
>  		return;
>  #if SQLITE_MAX_COLUMN
> -	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> +	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
>  		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
>  		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);
>  	if (z == 0)
> @@ -689,28 +665,27 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>  	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]));
> +				     (p->def->field_count + 8) * sizeof(p->aCol[0]));
>  		if (aNew == 0) {
>  			sqlite3DbFree(db, z);
>  			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;
> -
> +	p->def->fields[p->def->field_count].name = z;
>  	if (pType->n == 0) {
>  		/* If there is no type specified, columns have the default affinity
>  		 * 'BLOB' and type SCALAR.
> @@ -740,7 +715,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>  			sqlite3_free(zType);
>  		}
>  	}
> -	p->nCol++;
>  	p->def->field_count++;
>  	pParse->constraintName.n = 0;
>  }
> @@ -756,9 +730,9 @@ 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->aCol[p->def->field_count - 1].notNull = (u8) onError;
>  }
>  
>  /*
> @@ -871,38 +845,30 @@ void
>  sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
>  {
>  	Table *p;
> -	Column *pCol;
>  	sqlite3 *db = pParse->db;
>  	p = pParse->pNewTable;
>  	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);
> @@ -947,7 +913,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
>  	}
>  	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 +928,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;
> @@ -1036,7 +1002,7 @@ 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)
> @@ -1087,7 +1053,7 @@ 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);
> +		assert(column < (uint32_t)table->def->field_count);
>  		return table->aCol[column].coll;
>  	}
>  
> @@ -1306,9 +1272,8 @@ 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;
> -	}
> +	for (i = 0; i < (int)p->def->field_count; i++)
> +		n += identLength(p->def->fields[i].name) + 5;
>  	n += identLength(p->zName);
>  	if (n < 50) {
>  		zSep = "";
> @@ -1319,7 +1284,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);
> @@ -1329,7 +1294,7 @@ createTableStmt(sqlite3 * db, Table * p)
>  	k = sqlite3Strlen30(zStmt);
>  	identPut(zStmt, &k, p->zName);
>  	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 +1308,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 +1338,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 +1358,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,7 +1399,7 @@ 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;
>  			}
> @@ -1446,7 +1412,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));
> @@ -1663,7 +1629,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
>  			  sqlite3DbStrDup(pParse->db, p->zName), 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);
> @@ -1853,6 +1819,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
>  	p = pParse->pNewTable;
>  	if (p == 0)
>  		return;
> +	if (sql_table_def_rebuild(db, p) != 0)
> +		return;
>  
>  	assert(!db->init.busy || !pSelect);
>  
> @@ -2101,7 +2069,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 +2087,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);
>  		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
> @@ -2138,7 +2106,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>  	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 +2117,9 @@ 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);
>  			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 +2129,15 @@ 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);
> +			struct space_def *def = pSelTab->def;
> +			pSelTab->def = pTable->def;
> +			pSelTab->def->field_count = 0;
> +			pTable->def = def;
>  			pTable->aCol = pSelTab->aCol;
> -			pSelTab->nCol = 0;
>  			pSelTab->aCol = 0;
>  		} else {
> -			pTable->nCol = 0;
> +			pTable->def->field_count = 0;
>  			nErr++;
>  		}
>  		sqlite3DeleteTable(db, pSelTab);
> @@ -2195,8 +2164,18 @@ sqliteViewResetAll(sqlite3 * db)
>  		Table *pTab = sqliteHashData(i);
>  		if (pTab->pSelect) {
>  			sqlite3DeleteColumnNames(db, pTab);
> +			struct space_def *old_def = pTab->def;
> +			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 +2448,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 +2487,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);
> @@ -2966,7 +2945,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,7 +3999,7 @@ 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);
> @@ -4181,7 +4162,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..65d20fa 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -382,7 +382,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++;
> @@ -734,13 +734,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
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 0c86761..5f7d741 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;
>  }
>  
> @@ -4242,20 +4242,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..c15ad8c 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,
> @@ -650,7 +650,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);
> @@ -863,12 +863,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;
> @@ -1282,14 +1282,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
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 1cb9525..06635ee 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -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);
> @@ -1143,7 +1146,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>  		case ON_CONFLICT_ACTION_FAIL: {
>  				char *zMsg =
>  				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
> -						   pTab->aCol[i].zName);
> +						   pTab->def->fields[i].name);
>  				sqlite3VdbeAddOp3(v, OP_HaltIfNull,
>  						  SQLITE_CONSTRAINT_NOTNULL,
>  						  onError, regNewData + 1 + i);
> @@ -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 */
>  			}
>  			if (IsPrimaryKeyIndex(pIdx) || uniqueByteCodeNeeded) {
>  				sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,
> -						  pTab->nCol, aRegIdx[ix]);
> +						  pTab->def->field_count, aRegIdx[ix]);
>  				VdbeComment((v, "for %s", pIdx->zName));
>  			}
>  		} else {
> @@ -1401,7 +1405,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>  					sqlite3VdbeAddOp3(v, OP_Column,
>  							  iThisCur, x, regR + i);
>  					VdbeComment((v, "%s.%s", pTab->zName,
> -						pTab->aCol[pPk->aiColumn[i]].zName));
> +						pTab->def->fields[
> +							pPk->aiColumn[i]].name));
>  				}
>  			}
>  			if (isUpdate && uniqueByteCodeNeeded) {
> @@ -1792,13 +1797,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 4a68cad..e93f377 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++) {
>  						}
> @@ -381,7 +382,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>  					char *expr_str = space->
>  						def->fields[i].default_value;
>  					sqlite3VdbeMultiLoad(v, 1, "issisi",
> -							     i, pCol->zName,
> +							     i,
> +							     pTab->def->fields[i].
> +								     name,
>  							     field_type_strs[
>  							     sqlite3ColumnType
>  							     (pCol)],
> @@ -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) {
>  			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,8 +618,9 @@ 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);
> @@ -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/resolve.c b/src/box/sql/resolve.c
> index 823062a..109c410 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -240,7 +240,7 @@ lookupName(Parse * pParse,	/* The parsing context */
>  			     i++, pItem++) {
>  				pTab = pItem->pTab;
>  				assert(pTab != 0 && pTab->zName != 0);
> -				assert(pTab->nCol > 0);
> +				assert(pTab->def->field_count > 0);
>  				if (pItem->pSelect
>  				    && (pItem->pSelect->
>  					selFlags & SF_NestedFrom) != 0) {
> @@ -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 =
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 5a50413..1390c5d 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,7 +1661,8 @@ 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);
> @@ -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,
> @@ -1799,8 +1800,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 */
> @@ -1822,8 +1822,11 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>  		aCol = 0;
>  	}
>  	assert(nCol == (i16) nCol);
> -	*pnCol = nCol;
> -	*paCol = aCol;
> +	assert(pTable->def->fields == NULL);
> +	pTable->def->fields =
> +		sqlite3DbMallocZero(db, nCol*sizeof(pTable->def->fields[0]));
> +	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 +1848,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 +1877,24 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>  			if (cnt > 3)
>  				sqlite3_randomness(sizeof(cnt), &cnt);
>  		}
> -		pCol->zName = zName;
> +		pTable->def->fields[i].name = zName;
>  		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
>  			sqlite3OomFault(db);
>  		}
>  	}
>  	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 (sql_table_def_rebuild(db, pTable) != 0)
> +		rc = SQLITE_NOMEM_BKPT;
> +	if (rc != SQLITE_OK) {
>  		sqlite3DbFree(db, aCol);
> -		*paCol = 0;
> -		*pnCol = 0;
> -		return SQLITE_NOMEM_BKPT;
> +		sqlite3DbFree(db, pTable->def->fields);
> +		pTable->def->fields = NULL;
> +		pTable->def->field_count = 0;
> +		pTable->aCol = 0;
> +		rc = SQLITE_NOMEM_BKPT;
>  	}
> -	return SQLITE_OK;
> +	return rc;
>  }
>  
>  /*
> @@ -1918,13 +1923,15 @@ 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);
> @@ -1963,10 +1970,9 @@ 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);
> +	if (pTab == NULL)
>  		return 0;
> -	}
>  	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
>  	 * is disabled
>  	 */
> @@ -1975,8 +1981,7 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
>  	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,8 +4502,8 @@ 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);
> +		if (pTab == NULL)
>  			return WRC_Abort;
>  		pTab->nTabRef = 1;
>  		pTab->zName = sqlite3DbStrDup(db, pCte->zName);
> @@ -4562,8 +4567,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 =
> @@ -4684,8 +4688,8 @@ selectExpander(Walker * pWalker, Select * p)
>  			if (sqlite3WalkSelect(pWalker, pSel))
>  				return WRC_Abort;
>  			pFrom->pTab = pTab =
> -			    sqlite3DbMallocZero(db, sizeof(Table));
> -			if (pTab == 0)
> +				sql_ephemeral_table_new(pParse);
> +			if (pTab == NULL)
>  				return WRC_Abort;
>  			pTab->nTabRef = 1;
>  			pTab->zName =
> @@ -4693,8 +4697,7 @@ selectExpander(Walker * pWalker, Select * p)
>  			while (pSel->pPrior) {
>  				pSel = pSel->pPrior;
>  			}
> -			sqlite3ColumnsFromExprList(pParse, pSel->pEList,
> -						   &pTab->nCol, &pTab->aCol);
> +			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
>  			pTab->iPKey = -1;
>  			pTab->nRowLogEst = 200;
>  			assert(200 == sqlite3LogEst(1048576));
> @@ -4727,10 +4730,10 @@ selectExpander(Walker * pWalker, Select * p)
>  				    sqlite3SelectDup(db, pTab->pSelect, 0);
>  				sqlite3SelectSetName(pFrom->pSelect,
>  						     pTab->zName);
> -				nCol = pTab->nCol;
> -				pTab->nCol = -1;
> +				nCol = pTab->def->field_count;
> +				pTab->def->field_count = -1;
>  				sqlite3WalkSelect(pWalker, pFrom->pSelect);
> -				pTab->nCol = nCol;
> +				pTab->def->field_count = nCol;
>  			}
>  #endif
>  		}
> @@ -4835,9 +4838,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 */
> @@ -5372,10 +5374,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->zName,
>  					pSub->pEList->nExpr);
>  			goto select_end;
>  		}
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 8bb45c9..8e1c135 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1867,7 +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;
> @@ -1950,7 +1949,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
> @@ -3515,7 +3513,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 *);
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index f3bd0b7..464feee 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -75,8 +75,8 @@ sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
>  	if (!pTab->pSelect) {
>  		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->zName, pTab->def->fields[i].name));
> +		assert(i < (int)pTab->def->field_count);
>  
>  		Expr *expr = NULL;
>  		struct space *space =
> @@ -212,14 +212,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 +237,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 +254,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 +312,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 +327,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 +479,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 +510,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 +566,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/where.c b/src/box/sql/where.c
> index 7a7103c..fc0f84c 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) {
> @@ -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,
> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
> index f1112f2..233fde0 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;
>  }
>  
>  /*
> diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
> index 86ee273..1b0d961 100644
> --- a/src/box/sql/whereexpr.c
> +++ b/src/box/sql/whereexpr.c
> @@ -1502,10 +1502,10 @@ 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);
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 0/4] sql: Removed Column fields to server with region allocations
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
                   ` (3 preceding siblings ...)
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 4/4] sql: Region-based allocations Kirill Shcherbatov
@ 2018-04-26 11:47 ` Vladislav Shpilevoy
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
  5 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-26 11:47 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Thanks for contributing! See below my 1 comment.

On 25/04/2018 19:52, Kirill Shcherbatov wrote:
> Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
> Issue: https://github.com/tarantool/tarantool/issues/3272

Please, provide a description of the patchset.

> 
> Kirill Shcherbatov (4):
>    sql: Fix code style in sqlite3Pragma.
>    sql: Remove zName and nColumn from SQL.
>    sql: Removed type from SQL.
>    sql: Region-based allocations.
> 
>   src/box/space_def.c     |  29 ++++---
>   src/box/sql.c           |  90 ++++++++++++++++---
>   src/box/sql.h           |  32 +++++++
>   src/box/sql/alter.c     |  32 ++++---
>   src/box/sql/analyze.c   |   5 +-
>   src/box/sql/build.c     | 226 +++++++++++++++++++++++-------------------------
>   src/box/sql/delete.c    |   6 +-
>   src/box/sql/expr.c      |  11 +--
>   src/box/sql/fkey.c      |  20 ++---
>   src/box/sql/insert.c    |  55 ++++++------
>   src/box/sql/pragma.c    |  84 +++++++++---------
>   src/box/sql/prepare.c   |  26 +++---
>   src/box/sql/resolve.c   |  16 ++--
>   src/box/sql/select.c    | 120 ++++++++++++++-----------
>   src/box/sql/sqliteInt.h |   8 +-
>   src/box/sql/tokenize.c  |   3 +
>   src/box/sql/trigger.c   |   2 +
>   src/box/sql/update.c    |  29 ++++---
>   src/box/sql/util.c      |   9 --
>   src/box/sql/where.c     |   6 +-
>   src/box/sql/wherecode.c |   2 +-
>   src/box/sql/whereexpr.c |   4 +-
>   22 files changed, 477 insertions(+), 338 deletions(-)
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma.
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-04-26 11:47   ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-26 11:47 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Please, do not start a commit message from a capital letter, and do
not put dot at the end. Read this:
https://tarantool.io/en/doc/1.9/dev_guide/developer_guidelines.html#how-to-write-a-commit-message
Fix this in other commits too.

See below 1 comment.

On 25/04/2018 19:52, Kirill Shcherbatov wrote:
> ---
>   src/box/sql/pragma.c | 56 +++++++++++++++++++++++++---------------------------
>   1 file changed, 27 insertions(+), 29 deletions(-)
> 
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index e41f69b..4a68cad 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -544,37 +544,35 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>   
>   #ifndef SQLITE_OMIT_FOREIGN_KEY
>   	case PragTyp_FOREIGN_KEY_LIST:{
> -			if (zRight) {
> -				FKey *pFK;
> -				Table *pTab;
> -				pTab = sqlite3HashFind(&db->pSchema->tblHash,
> -						       zRight);
> -				if (pTab != NULL) {
> -					pFK = pTab->pFKey;
> -					if (pFK) {
> -						int i = 0;
> -						pParse->nMem = 8;
> -						while (pFK) {
> -							int j;
> -							for (j = 0;
> -							     j < pFK->nCol;
> -							     j++) {
> -								sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j, pFK->zTo, pTab->aCol[pFK->aCol[j].iFrom].zName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]),	/* ON UPDATE */
> -										     actionName(pFK->aAction[0]),	/* ON DELETE */
> -										     "NONE");
> -								sqlite3VdbeAddOp2
> -								    (v,
> -								     OP_ResultRow,
> -								     1, 8);
> -							}
> -							++i;
> -							pFK = pFK->pNextFrom;
> -						}
> -					}
> -				}
> -			}
> +		if (!zRight)

1. Please, use explicit == NULL. Same in other places.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 2/4] sql: Remove zName and nColumn from SQL.
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL Kirill Shcherbatov
  2018-04-25 17:10   ` [tarantool-patches] " Kirill Shcherbatov
@ 2018-04-26 11:47   ` Vladislav Shpilevoy
  1 sibling, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-26 11:47 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. See my comment about the smaller version of this commit.
I can not see the new version on the branch, so I am reviewing
this one.

See my 8 comments below about the first part of the patch. Next part
I will review, when you push actual version.

On 25/04/2018 19:52, Kirill Shcherbatov wrote:
> 1. Removed zName from SQL Column.
> 2. Removed zColumns from SQL Table.
> 3. Refactored Parser to use def_expression directly.
> 4. Introduced sql_table_def_rebuild intended for collect
> fragmented with sql_field_retrieve space_def into memory
1. Collect fragmented what, field_def? If so, the it would be good
to write it explicitly.

> located in one allocation.
> 
> Needed for #3272.
> ---
>   src/box/space_def.c     |  29 ++++----
>   src/box/sql.c           |  61 +++++++++++++---
>   src/box/sql.h           |  23 ++++++
>   src/box/sql/alter.c     |  32 ++++++---
>   src/box/sql/analyze.c   |   5 +-
>   src/box/sql/build.c     | 181 ++++++++++++++++++++++--------------------------
>   src/box/sql/delete.c    |   6 +-
>   src/box/sql/expr.c      |  11 +--
>   src/box/sql/fkey.c      |  20 +++---
>   src/box/sql/insert.c    |  55 ++++++++-------
>   src/box/sql/pragma.c    |  24 ++++---
>   src/box/sql/resolve.c   |  16 +++--
>   src/box/sql/select.c    |  92 ++++++++++++------------
>   src/box/sql/sqliteInt.h |   4 +-
>   src/box/sql/update.c    |  29 ++++----
>   src/box/sql/where.c     |   6 +-
>   src/box/sql/wherecode.c |   2 +-
>   src/box/sql/whereexpr.c |   4 +-
>   18 files changed, 338 insertions(+), 262 deletions(-)
> 
> 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
>   
> @@ -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);

2. Garbage diff. You just wrap &expr_pos on the new line, but it fits in one
line with sql_expr_dup.
> @@ -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);

3. Same as 2. Please, before sending a patch make a self-review. It really
helps, I do it too, even for review letters, that I am sending now. During
self-review you can catch and remove such things with no waiting until I
found it myself.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 166bb71..38aeac6 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1681,3 +1679,48 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
>   
>   	return space->def->fields[fieldno].default_value_expr;
>   }
> +
> +Table *
> +sql_ephemeral_table_new(Parse *parser)
> +{
> +	sqlite3 *db = parser->db;
> +	struct space_def *def = NULL;
> +	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
> +	if (table != NULL) {
> +		def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
> +				    &space_opts_default, NULL, 0);
> +	}
> +	if (def == NULL) {
> +		sqlite3DbFree(db, table);
> +		parser->rc = SQLITE_NOMEM_BKPT;
> +		parser->nErr++;
> +		return NULL;
> +	}

4. Table can be NULL, and def not NULL - it leads to crash. And why you do not
call sqlite3OomFault(db); here?
> diff --git a/src/box/sql.h b/src/box/sql.h
> index db92d80..9fb3ad1 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,28 @@ 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.

5. As I said in the previous reviews, 'ephemeric' word does not mean
'эфемерный'. Use ephemeral.

> + * @param pParse SQL Parser object.
> + * @param zName Table to create name.

6. I do not see zName parameter here.

> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */
> +struct Table *
> +sql_ephemeral_table_new(struct Parse *parser);
> +
> +/**
> + * 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 pTable The Table with fragmented def to rebuild.
> + * @retval 1 on memory allocation error

7. Please, return -1 on errors.
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index 24f0965..bedf602 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -144,6 +144,9 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
>   void
>   sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>   {
> +	/* This function is not implemented yet #3075. */
> +	assert(false);

8. We have 'unreachable()' macro for this.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 4/4] sql: Region-based allocations.
  2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 4/4] sql: Region-based allocations Kirill Shcherbatov
@ 2018-04-26 11:47   ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-26 11:47 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Thanks for contributing! See below 16 comments.

On 25/04/2018 19:52, Kirill Shcherbatov wrote:
> Function sql_field_retrieve could leak names
> in error cases. Let allocate all memory with
> region allocator.
> 
> Needed for #3272.

1. It is not only 3272. It is a part of the separate region
allocations issue. I do not remember the number, please, find and
write here 'Part of #nnnn#' on new line.

> diff --git a/src/box/sql.c b/src/box/sql.c
> index 6d4255e..2893d70 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1685,22 +1685,43 @@ 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)
> +{
> +	struct space_def *def = NULL;
> +	struct region *region = &fiber()->gc;
> +	size_t size = sizeof(struct space_def) + 1;
> +	def = (struct space_def *)region_alloc(region, size);
> +	if (def != NULL) {
> +		memset(def, 0, size);
> +		def->dict =
> +			(struct tuple_dictionary *)
> +				region_alloc(region,
> +					     sizeof(struct tuple_dictionary));

2. Why do you need a dictionary? It must not be used in SQL anywhere. And
please, do not mess checks on allocations error - try to check it right after
allocation. It is hard to review code, where after alloc its result is checked
multiple times on ==/!= NULL. If you have multiple allocations, and after fail of
each one you must destroy some results of the previous ones, or set some error
codes, you can use this way:

	if (alloc1_failed)
		goto error1;
	...
	if (alloc2_failed)
		goto error2;
	...
error3:
	do_clean3...
error2:
	do_clean2...
error1:
	do_clean1...
	return -1;

See the good example in vy_lsm_new() function.

> +	}
> +	if (def == NULL || def->dict == NULL) {
> +		parser->rc = SQLITE_NOMEM_BKPT;
> +		parser->nErr++;
> +		return NULL;
> +	}
> +	def->dict->refs = 1;
> +	def->opts.temporary = true;
> +	return def;
> +}
> +
>   Table *
>   sql_ephemeral_table_new(Parse *parser)
>   {
>   	sqlite3 *db = parser->db;
>   	struct space_def *def = NULL;
>   	Table *table = sqlite3DbMallocZero(db, sizeof(Table));
> -	if (table != NULL) {
> -		def = space_def_new(0, 0, 0, NULL, 0, NULL, 0,
> -				    &space_opts_default, NULL, 0);
> -	}
> +	if (table != NULL)
> +		def = sql_ephemeral_space_def_new(parser);
>   	if (def == NULL) {
>   		sqlite3DbFree(db, table);
> -		parser->rc = SQLITE_NOMEM_BKPT;
> -		parser->nErr++;

3. Why did you remove it? Region allocation error is the same, as
malloc allocation error. See details in 6.

>   		return NULL;
>   	}
> +

4. Garbage diff.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 584e6b1..4db4356 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -386,7 +386,9 @@ deleteTable(sqlite3 * db, Table * pTable)
>   	sqlite3DbFree(db, pTable->zColAff);
>   	sqlite3SelectDelete(db, pTable->pSelect);
>   	sqlite3ExprListDelete(db, pTable->pCheck);
> -	if (pTable->def != NULL)
> +	/* Do not delete pTable->def allocated not on region. */
> +	assert(pTable->def != NULL);
> +	if (pTable->def->opts.temporary == false)

5. Boolean variables checking on true/false can be omitted.

> @@ -603,29 +606,33 @@ 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(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]))

6. On region error please do diag_set error. Region error is Tarantool error, not
sqlite. And on such error you must return not SQLITE_NOMEM_BKPT, but
SQL_TARANTOOL_ERROR. It leads to returning to a user an error, that is set in diag.

>   		if (field == NULL) {
>   			parser->rc = SQLITE_NOMEM_BKPT;
>   			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));
> +		}

7. It would be good to note here, that field names are not on the same
region allocation, and it is ok to memcpy pointers on them.

> +		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;

8. This diff, and other columns->columns_new diff are garbage. Why did you
rename it? The previous name was ok.
> @@ -677,10 +685,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>   		aNew =
>   		    sqlite3DbRealloc(db, p->aCol,
>   				     (p->def->field_count + 8) * sizeof(p->aCol[0]));
> -		if (aNew == 0) {
> -			sqlite3DbFree(db, z);
> +		if (aNew == 0)
>   			return;

9. Please, use == NULL for pointers. I see, that it is left from the original
code, but if you modify it, then use Tarantool code style in the new code please.
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 2ab8751..7dc14f6 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -211,17 +211,19 @@ sqlite3InitDatabase(sqlite3 * db)
>   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;
> +	if (pParse == NULL)
> +		return;
> +	sqlite3 *db = pParse->db;
> +	sqlite3DbFree(db, pParse->aLabel);
> +	sqlite3ExprListDelete(db, pParse->pConstExpr);
> +	if (db) {

10. Same as 9.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 9eeff8e..03bfcf9 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1822,9 +1822,20 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   		aCol = 0;
>   	}
>   	assert(nCol == (i16) nCol);
> +
> +	struct region *region = &fiber()->gc;
>   	assert(pTable->def->fields == NULL);
> +	if (pTable->def->opts.temporary == false) {

11. At first, same as 5. At second, why? I can not see this in the original code.
> @@ -1877,9 +1888,16 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   			if (cnt > 3)
>   				sqlite3_randomness(sizeof(cnt), &cnt);
>   		}
> -		pTable->def->fields[i].name = zName;
> -		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
> +		uint32_t zNameLen = (uint32_t)strlen(zName);

12. For new code please use Tarantool code style - no Camel case.
zNameLen -> name_len.

> +		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol)

13. Same as 9.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 94d5c80..520b74c 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2933,6 +2933,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 */

14. Please, put a dot at the end of the comment.
> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index c77aa9b..9055bd0 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -664,6 +664,9 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
>   	memset(&parser, 0, sizeof(parser));
>   	parser.db = db;
>   	parser.parse_only = true;
> +	struct region *region = &fiber()->gc;
> +	parser.region_initial_size = region_used(region);

15. It is a second place, where a parser is created. How about creating a function
sql_parser_create(struct Parser *parser), that will do this?

16. I do not see, that you removed def rebuilding from sqlite3ColumnsFromExprList -
it is not needed anymore.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v3 2/4] sql: Remove zName and nColumn from SQL.
  2018-04-25 17:10   ` [tarantool-patches] " Kirill Shcherbatov
@ 2018-04-26 12:12     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-26 12:12 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Look like you did not push the new commit. On the branch
it still is huge, not src/box/sql/build.c | 12 ++++--------.

On 25/04/2018 20:10, Kirill Shcherbatov wrote:
> I've included some extra changes in this commit. Fixed.
> 
> ---
>   src/box/sql/build.c | 12 ++++--------
>   1 file changed, 4 insertions(+), 8 deletions(-)
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures
  2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
                   ` (4 preceding siblings ...)
  2018-04-26 11:47 ` [tarantool-patches] Re: [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Vladislav Shpilevoy
@ 2018-04-28 18:26 ` Kirill Shcherbatov
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
                     ` (7 more replies)
  5 siblings, 8 replies; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
Issue: https://github.com/tarantool/tarantool/issues/3272

Kirill Shcherbatov (7):
  sql: fix code style in sqlite3Pragma
  sql: remove zName and nColumn from SQL
  sql: start using type from space_def
  sql: start using collations and is_nullable from space_def
  sql: move names to server
  sql: start using is_view field from space_def
  sql: space_def* instead of Table* in Expr

 src/box/field_def.c     |   1 +
 src/box/field_def.h     |   5 +
 src/box/space_def.c     |  29 ++--
 src/box/sql.c           | 156 +++++++++++++++++----
 src/box/sql.h           |  32 +++++
 src/box/sql/alter.c     |  54 ++++---
 src/box/sql/analyze.c   |  16 ++-
 src/box/sql/build.c     | 364 ++++++++++++++++++++++++++----------------------
 src/box/sql/delete.c    |  25 ++--
 src/box/sql/expr.c      |  65 +++++----
 src/box/sql/fkey.c      |  51 ++++---
 src/box/sql/hash.c      |   5 +-
 src/box/sql/insert.c    |  88 ++++++------
 src/box/sql/pragma.c    |  96 +++++++------
 src/box/sql/prepare.c   |  26 ++--
 src/box/sql/resolve.c   |  33 +++--
 src/box/sql/select.c    | 185 +++++++++++++-----------
 src/box/sql/sqliteInt.h |  37 ++---
 src/box/sql/tokenize.c  |   3 +
 src/box/sql/treeview.c  |   2 +-
 src/box/sql/trigger.c   |   6 +-
 src/box/sql/update.c    |  52 +++----
 src/box/sql/util.c      |   9 --
 src/box/sql/vdbe.c      |   2 +-
 src/box/sql/vdbeaux.c   |  19 ++-
 src/box/sql/where.c     |  24 ++--
 src/box/sql/wherecode.c |  23 +--
 src/box/sql/whereexpr.c |   8 +-
 28 files changed, 831 insertions(+), 585 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 10:10     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 2/7] sql: remove zName and nColumn from SQL Kirill Shcherbatov
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

---
 src/box/sql/pragma.c | 56 +++++++++++++++++++++++++---------------------------
 1 file changed, 27 insertions(+), 29 deletions(-)

diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index e41f69b..4a68cad 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -544,37 +544,35 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
 	case PragTyp_FOREIGN_KEY_LIST:{
-			if (zRight) {
-				FKey *pFK;
-				Table *pTab;
-				pTab = sqlite3HashFind(&db->pSchema->tblHash,
-						       zRight);
-				if (pTab != NULL) {
-					pFK = pTab->pFKey;
-					if (pFK) {
-						int i = 0;
-						pParse->nMem = 8;
-						while (pFK) {
-							int j;
-							for (j = 0;
-							     j < pFK->nCol;
-							     j++) {
-								sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j, pFK->zTo, pTab->aCol[pFK->aCol[j].iFrom].zName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]),	/* ON UPDATE */
-										     actionName(pFK->aAction[0]),	/* ON DELETE */
-										     "NONE");
-								sqlite3VdbeAddOp2
-								    (v,
-								     OP_ResultRow,
-								     1, 8);
-							}
-							++i;
-							pFK = pFK->pNextFrom;
-						}
-					}
-				}
-			}
+		if (!zRight)
+			break;
+		FKey *pFK;
+		Table *pTab;
+		pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
+		if (pTab == NULL)
 			break;
+		pFK = pTab->pFKey;
+		if (!pFK)
+			break;
+		int i = 0;
+		pParse->nMem = 8;
+		while (pFK) {
+			for (int j = 0; j < pFK->nCol; j++) {
+				const char *name =
+					pTab->aCol[pFK->aCol[j].iFrom].zName;
+				sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j,
+						     pFK->zTo, name,
+						     pFK->aCol[j].zCol,
+						     actionName(pFK->aAction[1]),
+						     actionName(pFK->aAction[0]),
+						     "NONE");
+				sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
+			}
+			++i;
+			pFK = pFK->pNextFrom;
 		}
+		break;
+	}
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 2/7] sql: remove zName and nColumn from SQL
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 10:10     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 3/7] sql: start using type from space_def Kirill Shcherbatov
                     ` (5 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

1. Removed zName from SQL Column.
2. Removed zColumns from SQL Table.
3. Refactored Parser to use def_expression directly.
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.
---
 src/box/space_def.c     |  29 ++++---
 src/box/sql.c           |  79 +++++++++++++++--
 src/box/sql.h           |  32 +++++++
 src/box/sql/alter.c     |  32 ++++---
 src/box/sql/analyze.c   |   5 +-
 src/box/sql/build.c     | 221 +++++++++++++++++++++++-------------------------
 src/box/sql/delete.c    |   6 +-
 src/box/sql/expr.c      |  11 +--
 src/box/sql/fkey.c      |  20 ++---
 src/box/sql/insert.c    |  55 ++++++------
 src/box/sql/pragma.c    |  24 ++++--
 src/box/sql/prepare.c   |  26 +++---
 src/box/sql/resolve.c   |  16 ++--
 src/box/sql/select.c    | 112 ++++++++++++++----------
 src/box/sql/sqliteInt.h |   6 +-
 src/box/sql/tokenize.c  |   3 +
 src/box/sql/trigger.c   |   2 +
 src/box/sql/update.c    |  29 ++++---
 src/box/sql/where.c     |   6 +-
 src/box/sql/wherecode.c |   2 +-
 src/box/sql/whereexpr.c |   4 +-
 21 files changed, 433 insertions(+), 287 deletions(-)

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..5ab116b 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1433,7 +1433,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 	struct SqliteIndex *pk_idx = sqlite3PrimaryKeyIndex(pTable);
 	int pk_forced_int = -1;
 	char *base = buf, *p;
-	int i, n = pTable->nCol;
+	int i, n = pTable->def->field_count;
 
 	p = enc->encode_array(base, n);
 
@@ -1449,15 +1449,15 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 		const char *t;
 		struct coll *coll = aCol[i].coll;
 		struct field_def *field = &pTable->def->fields[i];
-		struct Expr *def = field->default_value_expr;
+		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);
 		if (i == pk_forced_int) {
 			t = "integer";
@@ -1477,11 +1477,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 			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);
@@ -1681,3 +1679,66 @@ 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)
+{
+	struct space_def *def = NULL;
+	struct region *region = &fiber()->gc;
+	size_t size = sizeof(struct space_def) + 1;
+	def = (struct space_def *)region_alloc(region, size);
+	if (def != NULL) {
+		memset(def, 0, size);
+		def->dict =
+			(struct tuple_dictionary *)
+				region_alloc(region,
+					     sizeof(struct tuple_dictionary));
+	}
+	if (def == NULL || def->dict == NULL) {
+		parser->rc = SQLITE_NOMEM_BKPT;
+		parser->nErr++;
+		return NULL;
+	}
+	def->dict->refs = 1;
+	def->opts.temporary = true;
+	return def;
+}
+
+Table *
+sql_ephemeral_table_new(Parse *parser)
+{
+	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);
+	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 == true);
+
+	/* 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..410653b 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,37 @@ 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 pParse SQL Parser object.
+ * @param zName 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);
+
+/**
+ * Create and initialize a new ephemeric space_def object.
+ * @param pParse SQL Parser object.
+ * @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);
+
+/**
+ * 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 pTable 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..bedf602 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -144,6 +144,9 @@ sqlite3AlterRenameTable(Parse * pParse,	/* Parser context. */
 void
 sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 {
+	/* This function is not implemented yet #3075. */
+	assert(false);
+
 	Table *pNew;		/* Copy of pParse->pNewTable */
 	Table *pTab;		/* Table being altered */
 	const char *zTab;	/* Table name */
@@ -161,10 +164,10 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 	assert(pNew);
 
 	zTab = &pNew->zName[16];	/* Skip the "sqlite_altertab_" prefix on the name */
-	pCol = &pNew->aCol[pNew->nCol - 1];
+	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);
 
@@ -248,10 +251,13 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 void
 sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 {
+	/* This function is not implemented yet #3075. */
+	assert(false);
+
 	Table *pNew;
 	Table *pTab;
 	Vdbe *v;
-	int i;
+	uint32_t i;
 	int nAlloc;
 	sqlite3 *db = pParse->db;
 
@@ -281,13 +287,17 @@ 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);
@@ -295,10 +305,10 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 		assert(db->mallocFailed);
 		goto exit_begin_add_column;
 	}
-	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->nCol);
-	for (i = 0; i < pNew->nCol; i++) {
+	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
+	for (i = 0; i < pNew->def->field_count; i++) {
 		Column *pCol = &pNew->aCol[i];
-		pCol->zName = sqlite3DbStrDup(db, pCol->zName);
+		/* FIXME: pCol->zName = sqlite3DbStrDup(db, pCol->zName); */
 		pCol->coll = NULL;
 	}
 	pNew->pSchema = db->pSchema;
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index f0054c5..a0ad511 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -1023,9 +1023,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);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a2b712a..d3a33d5 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);
 }
 
 /*
@@ -393,12 +386,10 @@ deleteTable(sqlite3 * db, Table * pTable)
 	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 == false)
 		space_def_delete(pTable->def);
-		sqlite3DbFree(db, fields);
-	}
 	sqlite3DbFree(db, pTable);
 
 	/* Verify that no lookaside memory was used by schema tables */
@@ -494,30 +485,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);
+	if (table == NULL)
 		return NULL;
-	}
-
-	table->def = def;
 	table->zName = name;
 	table->iPKey = -1;
 	table->iAutoIncPKey = -1;
@@ -568,10 +548,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 +607,33 @@ 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;
 			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,40 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	sqlite3 *db = pParse->db;
 	if ((p = pParse->pNewTable) == 0)
 		return;
+	assert(p->def->opts.temporary == true);
 #if SQLITE_MAX_COLUMN
-	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
+	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
 		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
 		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 == 0)
 			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;
-
+	p->def->fields[p->def->field_count].name = z;
 	if (pType->n == 0) {
 		/* If there is no type specified, columns have the default affinity
 		 * 'BLOB' and type SCALAR.
@@ -740,7 +722,6 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 			sqlite3_free(zType);
 		}
 	}
-	p->nCol++;
 	p->def->field_count++;
 	pParse->constraintName.n = 0;
 }
@@ -756,9 +737,9 @@ 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->aCol[p->def->field_count - 1].notNull = (u8) onError;
 }
 
 /*
@@ -871,38 +852,31 @@ void
 sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 {
 	Table *p;
-	Column *pCol;
 	sqlite3 *db = pParse->db;
 	p = pParse->pNewTable;
+	assert(p->def->opts.temporary == true);
 	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);
@@ -947,7 +921,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 	}
 	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 +936,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;
@@ -1036,7 +1010,7 @@ 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)
@@ -1087,7 +1061,7 @@ 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);
+		assert(column < (uint32_t)table->def->field_count);
 		return table->aCol[column].coll;
 	}
 
@@ -1306,9 +1280,8 @@ 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;
-	}
+	for (i = 0; i < (int)p->def->field_count; i++)
+		n += identLength(p->def->fields[i].name) + 5;
 	n += identLength(p->zName);
 	if (n < 50) {
 		zSep = "";
@@ -1319,7 +1292,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);
@@ -1329,7 +1302,7 @@ createTableStmt(sqlite3 * db, Table * p)
 	k = sqlite3Strlen30(zStmt);
 	identPut(zStmt, &k, p->zName);
 	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 +1316,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 +1346,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 +1366,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,7 +1407,7 @@ 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;
 			}
@@ -1446,7 +1420,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));
@@ -1663,7 +1637,7 @@ createSpace(Parse * pParse, int iSpaceId, char *zStmt)
 			  sqlite3DbStrDup(pParse->db, p->zName), 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);
@@ -1853,7 +1827,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
@@ -1874,6 +1847,9 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		}
 	}
 
+	if (sql_table_def_rebuild(db, p) != 0)
+		return;
+
 #ifndef SQLITE_OMIT_CHECK
 	/* Resolve names in all CHECK constraint expressions.
 	 */
@@ -1986,6 +1962,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (db->init.busy) {
 		Table *pOld;
 		Schema *pSchema = p->pSchema;
+		assert(p->def->opts.temporary == false);
 		pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
 		if (pOld) {
 			assert(p == pOld);	/* Malloc must have failed inside HashInsert() */
@@ -2101,7 +2078,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 +2096,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);
 		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
@@ -2138,7 +2115,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	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,26 +2126,28 @@ 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);
 			if (db->mallocFailed == 0 && pParse->nErr == 0
-			    && pTable->nCol == pSel->pEList->nExpr) {
+			    && (int)pTable->def->field_count == pSel->pEList->nExpr) {
 				sqlite3SelectAddColumnTypeAndCollation(pParse,
 								       pTable,
 								       pSel);
 			}
 		} else if (pSelTab) {
+			assert(pTable->def->opts.temporary == false);
 			/* CREATE VIEW name AS...  without an argument list.  Construct
 			 * 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);
+			struct space_def *def = pSelTab->def;
+			pSelTab->def = pTable->def;
+			pSelTab->def->field_count = 0;
+			pTable->def = def;
 			pTable->aCol = pSelTab->aCol;
-			pSelTab->nCol = 0;
 			pSelTab->aCol = 0;
 		} else {
-			pTable->nCol = 0;
+			pTable->def->field_count = 0;
 			nErr++;
 		}
 		sqlite3DeleteTable(db, pSelTab);
@@ -2195,8 +2174,20 @@ sqliteViewResetAll(sqlite3 * db)
 		Table *pTab = sqliteHashData(i);
 		if (pTab->pSelect) {
 			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 +2460,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 +2499,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);
@@ -2966,7 +2957,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,7 +4011,7 @@ 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);
@@ -4181,7 +4174,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..65d20fa 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -382,7 +382,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++;
@@ -734,13 +734,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
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 0c86761..5f7d741 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;
 }
 
@@ -4242,20 +4242,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..c15ad8c 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,
@@ -650,7 +650,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);
@@ -863,12 +863,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;
@@ -1282,14 +1282,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
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 1cb9525..06635ee 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -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);
@@ -1143,7 +1146,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 		case ON_CONFLICT_ACTION_FAIL: {
 				char *zMsg =
 				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
-						   pTab->aCol[i].zName);
+						   pTab->def->fields[i].name);
 				sqlite3VdbeAddOp3(v, OP_HaltIfNull,
 						  SQLITE_CONSTRAINT_NOTNULL,
 						  onError, regNewData + 1 + i);
@@ -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 */
 			}
 			if (IsPrimaryKeyIndex(pIdx) || uniqueByteCodeNeeded) {
 				sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData + 1,
-						  pTab->nCol, aRegIdx[ix]);
+						  pTab->def->field_count, aRegIdx[ix]);
 				VdbeComment((v, "for %s", pIdx->zName));
 			}
 		} else {
@@ -1401,7 +1405,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 					sqlite3VdbeAddOp3(v, OP_Column,
 							  iThisCur, x, regR + i);
 					VdbeComment((v, "%s.%s", pTab->zName,
-						pTab->aCol[pPk->aiColumn[i]].zName));
+						pTab->def->fields[
+							pPk->aiColumn[i]].name));
 				}
 			}
 			if (isUpdate && uniqueByteCodeNeeded) {
@@ -1792,13 +1797,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 4a68cad..e93f377 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++) {
 						}
@@ -381,7 +382,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 					char *expr_str = space->
 						def->fields[i].default_value;
 					sqlite3VdbeMultiLoad(v, 1, "issisi",
-							     i, pCol->zName,
+							     i,
+							     pTab->def->fields[i].
+								     name,
 							     field_type_strs[
 							     sqlite3ColumnType
 							     (pCol)],
@@ -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) {
 			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,8 +618,9 @@ 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);
@@ -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..7dc14f6 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -211,17 +211,19 @@ sqlite3InitDatabase(sqlite3 * db)
 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;
+	if (pParse == NULL)
+		return;
+	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;
+	struct region *region = &fiber()->gc;
+	region_truncate(region, pParse->region_initial_size);
 }
 
 /*
@@ -265,6 +267,10 @@ sqlite3Prepare(sqlite3 * db,	/* Database handle. */
 	 * works even if READ_UNCOMMITTED is set.
 	 */
 	sParse.db = db;
+	/* Store region initial size to revert future allocations */
+	struct region *region = &fiber()->gc;
+	sParse.region_initial_size = region_used(region);
+
 	if (nBytes >= 0 && (nBytes == 0 || zSql[nBytes - 1] != 0)) {
 		char *zSqlCopy;
 		int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 823062a..109c410 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -240,7 +240,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 			     i++, pItem++) {
 				pTab = pItem->pTab;
 				assert(pTab != 0 && pTab->zName != 0);
-				assert(pTab->nCol > 0);
+				assert(pTab->def->field_count > 0);
 				if (pItem->pSelect
 				    && (pItem->pSelect->
 					selFlags & SF_NestedFrom) != 0) {
@@ -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 =
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 5a50413..d71603e 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,7 +1661,8 @@ 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);
@@ -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,
@@ -1799,8 +1800,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 */
@@ -1822,8 +1822,22 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 		aCol = 0;
 	}
 	assert(nCol == (i16) nCol);
-	*pnCol = nCol;
-	*paCol = aCol;
+
+	struct region *region = &fiber()->gc;
+	assert(pTable->def->fields == NULL);
+	if (pTable->def->opts.temporary == false) {
+		/* CREATE VIEW name AS...  without an argument list.  Construct
+		 * the column names from the SELECT statement that defines the view.
+		 */
+		pTable->def->field_count = 0;
+		space_def_delete(pTable->def);
+		pTable->def = sql_ephemeral_space_def_new(pParse);
+	}
+	pTable->def->fields =
+		region_alloc(region, nCol*sizeof(pTable->def->fields[0]));
+	memset(pTable->def->fields, 0, nCol*sizeof(pTable->def->fields[0]));
+	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 +1859,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 +1888,31 @@ 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 zNameLen = (uint32_t)strlen(zName);
+		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol)
+			sqlite3OomFault(db);
+		pTable->def->fields[i].name =
+			region_alloc(region, zNameLen + 1);
+		if (pTable->def->fields[i].name == NULL) {
 			sqlite3OomFault(db);
+		} else {
+			memcpy(pTable->def->fields[i].name, zName, zNameLen);
+			pTable->def->fields[i].name[zNameLen] = '\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 (sql_table_def_rebuild(db, pTable) != 0)
+		rc = SQLITE_NOMEM_BKPT;
+	if (rc != SQLITE_OK) {
 		sqlite3DbFree(db, aCol);
-		*paCol = 0;
-		*pnCol = 0;
-		return SQLITE_NOMEM_BKPT;
+		sqlite3DbFree(db, pTable->def->fields);
+		pTable->def->fields = NULL;
+		pTable->def->field_count = 0;
+		pTable->aCol = 0;
+		rc = SQLITE_NOMEM_BKPT;
 	}
-	return SQLITE_OK;
+	return rc;
 }
 
 /*
@@ -1918,13 +1941,15 @@ 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);
@@ -1963,10 +1988,9 @@ 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);
+	if (pTab == NULL)
 		return 0;
-	}
 	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
 	 * is disabled
 	 */
@@ -1975,8 +1999,7 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	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,8 +4520,8 @@ 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);
+		if (pTab == NULL)
 			return WRC_Abort;
 		pTab->nTabRef = 1;
 		pTab->zName = sqlite3DbStrDup(db, pCte->zName);
@@ -4562,8 +4585,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 =
@@ -4684,8 +4706,8 @@ selectExpander(Walker * pWalker, Select * p)
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
 			pFrom->pTab = pTab =
-			    sqlite3DbMallocZero(db, sizeof(Table));
-			if (pTab == 0)
+				sql_ephemeral_table_new(pParse);
+			if (pTab == NULL)
 				return WRC_Abort;
 			pTab->nTabRef = 1;
 			pTab->zName =
@@ -4693,8 +4715,7 @@ selectExpander(Walker * pWalker, Select * p)
 			while (pSel->pPrior) {
 				pSel = pSel->pPrior;
 			}
-			sqlite3ColumnsFromExprList(pParse, pSel->pEList,
-						   &pTab->nCol, &pTab->aCol);
+			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
 			pTab->iPKey = -1;
 			pTab->nRowLogEst = 200;
 			assert(200 == sqlite3LogEst(1048576));
@@ -4727,10 +4748,10 @@ selectExpander(Walker * pWalker, Select * p)
 				    sqlite3SelectDup(db, pTab->pSelect, 0);
 				sqlite3SelectSetName(pFrom->pSelect,
 						     pTab->zName);
-				nCol = pTab->nCol;
-				pTab->nCol = -1;
+				nCol = pTab->def->field_count;
+				pTab->def->field_count = -1;
 				sqlite3WalkSelect(pWalker, pFrom->pSelect);
-				pTab->nCol = nCol;
+				pTab->def->field_count = nCol;
 			}
 #endif
 		}
@@ -4835,9 +4856,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 */
@@ -5372,10 +5392,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->zName,
 					pSub->pEList->nExpr);
 			goto select_end;
 		}
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 8bb45c9..98212e3 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,7 +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;
@@ -1950,7 +1949,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 +2934,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,
@@ -3515,7 +3515,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 *);
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index c77aa9b..9055bd0 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -664,6 +664,9 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
 	memset(&parser, 0, sizeof(parser));
 	parser.db = db;
 	parser.parse_only = true;
+	struct region *region = &fiber()->gc;
+	parser.region_initial_size = region_used(region);
+
 	char *unused;
 	if (sqlite3RunParser(&parser, stmt, &unused) != SQLITE_OK) {
 		diag_set(ClientError, ER_SQL_EXECUTE, expr);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 28c56db..9b03be1 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -873,6 +873,8 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 	pSubParse->pToplevel = pTop;
 	pSubParse->eTriggerOp = pTrigger->op;
 	pSubParse->nQueryLoop = pParse->nQueryLoop;
+	struct region *region = &fiber()->gc;
+	pSubParse->region_initial_size = region_used(region);
 
 	v = sqlite3GetVdbe(pSubParse);
 	if (v) {
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index f3bd0b7..464feee 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -75,8 +75,8 @@ sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
 	if (!pTab->pSelect) {
 		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->zName, pTab->def->fields[i].name));
+		assert(i < (int)pTab->def->field_count);
 
 		Expr *expr = NULL;
 		struct space *space =
@@ -212,14 +212,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 +237,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 +254,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 +312,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 +327,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 +479,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 +510,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 +566,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/where.c b/src/box/sql/where.c
index 7a7103c..fc0f84c 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) {
@@ -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,
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index f1112f2..233fde0 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;
 }
 
 /*
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 86ee273..1b0d961 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1502,10 +1502,10 @@ 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);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 3/7] sql: start using type from space_def
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 2/7] sql: remove zName and nColumn from SQL Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 4/7] sql: start using collations and is_nullable " Kirill Shcherbatov
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Part of #3272.
---
 src/box/sql.c           | 13 +++++++++----
 src/box/sql/build.c     | 12 +++++++-----
 src/box/sql/pragma.c    | 12 ++++++------
 src/box/sql/select.c    |  8 ++++----
 src/box/sql/sqliteInt.h |  2 --
 src/box/sql/util.c      |  9 ---------
 6 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 5ab116b..2893d70 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->def->field_count;
+	int i, n = def->field_count;
 
 	p = enc->encode_array(base, n);
 
@@ -1441,14 +1443,14 @@ 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 field_def *field = &def->fields[i];
 		const char *zToken = field->default_value;
 		int base_len = 4;
 		if (coll != NULL)
@@ -1521,6 +1523,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;
@@ -1532,7 +1537,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;
 	}
 
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index d3a33d5..99059b3 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -692,7 +692,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	}
 	pCol = &p->aCol[p->def->field_count];
 	memset(pCol, 0, sizeof(p->aCol[0]));
-	p->def->fields[p->def->field_count].name = z;
+	struct field_def *column_def = &p->def->fields[p->def->field_count];
+	column_def->name = z;
 	if (pType->n == 0) {
 		/* If there is no type specified, columns have the default affinity
 		 * 'BLOB' and type SCALAR.
@@ -700,8 +701,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.
@@ -710,16 +711,16 @@ 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->def->field_count++;
@@ -947,9 +948,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;
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index e93f377..0cf103c 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -381,13 +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,
-							     pTab->def->fields[i].
-								     name,
-							     field_type_strs[
-							     sqlite3ColumnType
-							     (pCol)],
+							     i, name,
+							     field_type_strs[type],
 							     nullable == 0,
 							     expr_str, k);
 					sqlite3VdbeAddOp2(v, OP_ResultRow, 1,
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index d71603e..03bfcf9 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1664,12 +1664,12 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
 				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
 			}
@@ -1955,7 +1955,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 		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;
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 98212e3..520b74c 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,7 +1867,6 @@ struct Savepoint {
  * of this structure.
  */
 struct Column {
-	enum field_type type;	/* Column type. */
 	/** Collating sequence. */
 	struct coll *coll;
 	/**
@@ -3390,7 +3389,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);
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().
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 4/7] sql: start using collations and is_nullable from space_def
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
                     ` (2 preceding siblings ...)
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 3/7] sql: start using type from space_def Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 10:21     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 5/7] sql: move names to server Kirill Shcherbatov
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Part of #3272.
---
 src/box/sql.c           | 36 +++++++++++++++++++++++++-----------
 src/box/sql/alter.c     | 11 ++++++++---
 src/box/sql/build.c     | 20 +++++++++++++-------
 src/box/sql/fkey.c      |  7 ++-----
 src/box/sql/select.c    |  6 ++++--
 src/box/sql/sqliteInt.h |  7 -------
 src/box/sql/vdbeaux.c   |  5 ++++-
 7 files changed, 56 insertions(+), 36 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 2893d70..ef11eb9 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1449,7 +1449,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 
 	for (i = 0; i < n; i++) {
 		const char *t;
-		struct coll *coll = aCol[i].coll;
+		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;
@@ -1461,19 +1463,25 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 		p = enc->encode_str(p, "name", 4);
 		p = enc->encode_str(p, field->name, strlen(field->name));
 		p = enc->encode_str(p, "type", 4);
+
+		assert(def->fields[i].is_nullable ==
+		       (def->fields[i].nullable_action ==
+			ON_CONFLICT_ACTION_NONE));
+
 		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"));
@@ -1552,11 +1560,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 ==
+		       (def->fields[col].nullable_action ==
+			ON_CONFLICT_ACTION_NONE));
 		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);
@@ -1568,9 +1581,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);
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index bedf602..f830a15 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -198,7 +198,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 ==
+	       (pNew->def->fields[pNew->def->field_count - 1].nullable_action ==
+		ON_CONFLICT_ACTION_NONE));
+
+	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;
@@ -307,9 +312,9 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 	}
 	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
 	for (i = 0; i < pNew->def->field_count; i++) {
-		Column *pCol = &pNew->aCol[i];
+		/* FIXME: Column *pCol = &pNew->aCol[i]; */
 		/* FIXME: pCol->zName = sqlite3DbStrDup(db, pCol->zName); */
-		pCol->coll = NULL;
+		/* FIXME: pCol->coll = NULL; */
 	}
 	pNew->pSchema = db->pSchema;
 	pNew->addColOffset = pTab->addColOffset;
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 99059b3..e9c0686 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -694,6 +694,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	memset(pCol, 0, sizeof(p->aCol[0]));
 	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.
@@ -740,7 +742,9 @@ sqlite3AddNotNull(Parse * pParse, int onError)
 	p = pParse->pNewTable;
 	if (p == 0 || NEVER(p->def->field_count < 1))
 		return;
-	p->aCol[p->def->field_count - 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);
 }
 
 /*
@@ -1018,10 +1022,10 @@ sqlite3AddCollateType(Parse * pParse, Token * 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
@@ -1032,8 +1036,6 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 			if (pIdx->aiColumn[0] == i)
 				pIdx->coll_array[0] = sql_column_collation(p, i);
 		}
-	} else {
-		sqlite3DbFree(db, zColl);
 	}
 }
 
@@ -1064,7 +1066,9 @@ sql_column_collation(Table *table, uint32_t column)
 	 */
 	if (space == NULL || space_index(space, 0) == NULL) {
 		assert(column < (uint32_t)table->def->field_count);
-		return table->aCol[column].coll;
+		struct coll *coll =
+			coll_by_id(table->def->fields[column].coll_id);
+		return coll;
 	}
 
 	return space->format->fields[column].coll;
@@ -1411,7 +1415,9 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)
 	if (!db->init.imposterTable) {
 		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;
 			}
 		}
 	}
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index c15ad8c..8c015c9 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -34,6 +34,7 @@
  * support to compiled SQL statements.
  */
 #include <box/coll.h>
+#include "box/coll_cache.h"
 #include "sqliteInt.h"
 #include "box/session.h"
 #include "tarantoolInt.h"
@@ -535,11 +536,7 @@ 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";
+			const char *coll_name = "binary";
 			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
 							    coll_name);
 		} else {
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 03bfcf9..fa1de9b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1836,6 +1836,8 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 	pTable->def->fields =
 		region_alloc(region, nCol*sizeof(pTable->def->fields[0]));
 	memset(pTable->def->fields, 0, nCol*sizeof(pTable->def->fields[0]));
+	for (int i = 0; i < nCol; i++)
+		pTable->def->fields[i].is_nullable = true;
 	pTable->def->field_count = (uint32_t)nCol;
 	pTable->aCol = aCol;
 
@@ -1961,8 +1963,8 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 			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);
 }
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 520b74c..a045286 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,13 +1867,6 @@ struct Savepoint {
  * of this structure.
  */
 struct Column {
-	/** 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 */
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index b3998ea..f76c689 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4743,6 +4743,9 @@ table_column_is_nullable(struct Table *tab, uint32_t column)
 			ON_CONFLICT_ACTION_NONE;
 	} else {
 		/* tab is ephemeral (in SQLite sense).  */
-		return tab->aCol[column].notNull == 0;
+		assert(tab->def->fields[column].is_nullable ==
+		       (tab->def->fields[column].nullable_action ==
+			ON_CONFLICT_ACTION_NONE));
+		return tab->def->fields[column].is_nullable;
 	}
 }
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 5/7] sql: move names to server
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
                     ` (3 preceding siblings ...)
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 4/7] sql: start using collations and is_nullable " Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 11:08     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 6/7] sql: start using is_view field from space_def Kirill Shcherbatov
                     ` (2 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Part of #3272.
---
 src/box/sql.c           | 38 ++++++++++++++++++++++-------
 src/box/sql.h           |  6 ++---
 src/box/sql/alter.c     | 11 +++++----
 src/box/sql/analyze.c   | 11 +++++----
 src/box/sql/build.c     | 61 ++++++++++++++++++++++++++++++-----------------
 src/box/sql/delete.c    |  6 ++---
 src/box/sql/fkey.c      | 11 +++++----
 src/box/sql/hash.c      |  5 ++--
 src/box/sql/insert.c    |  9 +++----
 src/box/sql/pragma.c    |  4 ++--
 src/box/sql/resolve.c   |  7 +++---
 src/box/sql/select.c    | 63 ++++++++++++++++++++++++-------------------------
 src/box/sql/sqliteInt.h |  1 -
 src/box/sql/treeview.c  |  2 +-
 src/box/sql/trigger.c   |  4 ++--
 src/box/sql/update.c    |  3 ++-
 src/box/sql/vdbe.c      |  2 +-
 src/box/sql/where.c     |  5 ++--
 src/box/sql/wherecode.c |  3 ++-
 src/box/sql/whereexpr.c |  2 +-
 20 files changed, 150 insertions(+), 104 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index ef11eb9..47f7cb1 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1699,12 +1699,13 @@ 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)
+static 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 size = sizeof(struct space_def) + 1;
+	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) {
 		memset(def, 0, size);
@@ -1718,19 +1719,40 @@ sql_ephemeral_space_def_new(Parse *parser)
 		parser->nErr++;
 		return NULL;
 	}
+	memcpy(def->name, name, name_len);
+	def->name[name_len] = '\0';
 	def->dict->refs = 1;
 	def->opts.temporary = true;
 	return def;
 }
 
+struct space_def *
+sql_ephemeral_space_def_clone(Parse *parser, struct space_def *old_def)
+{
+	struct space_def *new_def = NULL;
+	new_def = sql_ephemeral_space_def_new(parser, old_def->name);
+	if (new_def == NULL) {
+		parser->rc = SQLITE_NOMEM_BKPT;
+		parser->nErr++;
+		return NULL;
+	}
+	new_def->opts = old_def->opts;
+	new_def->opts.temporary = true;
+	new_def->id = old_def->id;
+	new_def->uid = old_def->uid;
+	memcpy(new_def->engine_name, old_def->engine_name,
+	       strlen(old_def->engine_name));
+	return new_def;
+}
+
 Table *
-sql_ephemeral_table_new(Parse *parser)
+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);
+		def = sql_ephemeral_space_def_new(parser, name);
 	if (def == NULL) {
 		sqlite3DbFree(db, table);
 		return NULL;
@@ -1744,13 +1766,13 @@ int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
 	assert(pTable->def->opts.temporary == true);
-
 	/* 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,
+				old_def->field_count,
+				pTable->def->name, strlen(pTable->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) {
diff --git a/src/box/sql.h b/src/box/sql.h
index 410653b..d7cfd70 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -152,16 +152,16 @@ sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
  * @retval not NULL on success.
  */
 struct Table *
-sql_ephemeral_table_new(struct Parse *parser);
+sql_ephemeral_table_new(struct Parse *parser, const char *name);
 
 /**
- * Create and initialize a new ephemeric space_def object.
+ * Create and initialize a new ephemeric space_def object copy.
  * @param pParse SQL Parser object.
  * @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);
+sql_ephemeral_space_def_clone(struct Parse *parser, struct space_def *old_def);
 
 /**
  * Rebuild struct def in Table with memory allocated on a single
diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
index f830a15..33a4f4d 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
@@ -163,7 +163,7 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 	pNew = pParse->pNewTable;
 	assert(pNew);
 
-	zTab = &pNew->zName[16];	/* Skip the "sqlite_altertab_" prefix on the name */
+	zTab = &pNew->def->name[16];	/* Skip the "sqlite_altertab_" prefix on the name */
 	pCol = &pNew->aCol[pNew->def->field_count - 1];
 	assert(pNew->def != NULL);
 	pDflt = space_column_default_expr(SQLITE_PAGENO_TO_SPACEID(pNew->tnum),
@@ -235,7 +235,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);
 }
 
 /*
@@ -305,8 +305,9 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 	       && 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;
 	}
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index a0ad511..dc5b4e9 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():
@@ -1146,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 e9c0686..4e0ae87 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -382,7 +382,6 @@ 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);
@@ -495,10 +494,9 @@ static Table *
 sql_table_new(Parse *parser, char *name)
 {
 	sqlite3 *db = parser->db;
-	struct Table *table = sql_ephemeral_table_new(parser);
+	struct Table *table = sql_ephemeral_table_new(parser, name);
 	if (table == NULL)
 		return NULL;
-	table->zName = name;
 	table->iPKey = -1;
 	table->iAutoIncPKey = -1;
 	table->pSchema = db->pSchema;
@@ -662,7 +660,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 	assert(p->def->opts.temporary == true);
 #if SQLITE_MAX_COLUMN
 	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
-		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
+		sqlite3ErrorMsg(pParse, "too many columns on %s", 
+				p->def->name);
 		return;
 	}
 #endif
@@ -921,7 +920,7 @@ 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;
@@ -1288,7 +1287,7 @@ createTableStmt(sqlite3 * db, Table * p)
 	n = 0;
 	for (i = 0; i < (int)p->def->field_count; i++)
 		n += identLength(p->def->fields[i].name) + 5;
-	n += identLength(p->zName);
+	n += identLength(p->def->name);
 	if (n < 50) {
 		zSep = "";
 		zSep2 = ",";
@@ -1306,7 +1305,7 @@ createTableStmt(sqlite3 * db, Table * p)
 	}
 	sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
 	k = sqlite3Strlen30(zStmt);
-	identPut(zStmt, &k, p->zName);
+	identPut(zStmt, &k, (char *)p->def->name);
 	zStmt[k++] = '(';
 	for (pCol = p->aCol, i = 0; i < (int)p->def->field_count; i++, pCol++) {
 		static const char *const azType[] = {
@@ -1642,7 +1641,7 @@ 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->def->field_count,
@@ -1710,7 +1709,8 @@ parseTableSchemaRecord(Parse * pParse, int iSpaceId, char *zStmt)
 
 	sqlite3VdbeAddOp4(v,
 			  OP_String8, 0, iTop, 0,
-			  sqlite3DbStrDup(pParse->db, p->zName), P4_DYNAMIC);
+			  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);
@@ -1848,7 +1848,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		if ((p->tabFlags & TF_HasPrimaryKey) == 0) {
 			sqlite3ErrorMsg(pParse,
 					"PRIMARY KEY missing on table %s",
-					p->zName);
+					p->def->name);
 			return;
 		} else {
 			convertToWithoutRowidTable(pParse, p);
@@ -1950,7 +1950,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. */
@@ -1970,8 +1970,8 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (db->init.busy) {
 		Table *pOld;
 		Schema *pSchema = p->pSchema;
-		assert(p->def->opts.temporary == false);
-		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);
@@ -2106,7 +2106,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 	 */
 	if ((int)pTable->def->field_count < 0) {
 		sqlite3ErrorMsg(pParse, "view %s is circularly defined",
-				pTable->zName);
+				pTable->def->name);
 		return 1;
 	}
 	assert((int)pTable->def->field_count >= 0);
@@ -2135,6 +2135,11 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * a VIEW it holds the list of column names.
 			 */
 			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
+			struct space_def *old_def = pTable->def;
+			old_def->opts.temporary = true; /* delete it manually */
+			if (sql_table_def_rebuild(db, pTable) != 0)
+				nErr++;
+			space_def_delete(old_def);
 			if (db->mallocFailed == 0 && pParse->nErr == 0
 			    && (int)pTable->def->field_count == pSel->pEList->nExpr) {
 				sqlite3SelectAddColumnTypeAndCollation(pParse,
@@ -2142,18 +2147,30 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 								       pSel);
 			}
 		} else if (pSelTab) {
-			assert(pTable->def->opts.temporary == false);
 			/* CREATE VIEW name AS...  without an argument list.  Construct
 			 * the column names from the SELECT statement that defines the view.
 			 */
 			assert(pTable->aCol == 0);
 			assert((int)pTable->def->field_count == -1);
-			struct space_def *def = pSelTab->def;
-			pSelTab->def = pTable->def;
-			pSelTab->def->field_count = 0;
-			pTable->def = def;
+			assert(pSelTab->def->opts.temporary);
+
+			struct space_def *old_def = pTable->def;
+			struct space_def *new_def =
+				sql_ephemeral_space_def_clone(pParse, old_def);
+			if (new_def == NULL) {
+				nErr++;
+			} else {
+				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->aCol = 0;
+			pSelTab->def = old_def;
+			pSelTab->def->fields = NULL;
+			pSelTab->def->field_count = 0;
 		} else {
 			pTable->def->field_count = 0;
 			nErr++;
@@ -2939,7 +2956,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);
 			}
@@ -2952,7 +2969,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;
@@ -4022,7 +4039,7 @@ sqlite3UniqueConstraint(Parse * pParse,	/* Parsing context */
 			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);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 65d20fa..c6ecba6 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);
 	}
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 8c015c9..16f72d8 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -332,7 +332,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;
@@ -721,7 +721,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);
 }
 
 /*
@@ -951,7 +952,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;
 		}
@@ -1076,7 +1077,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++;
 
@@ -1375,7 +1376,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..79f0840 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((void *)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/insert.c b/src/box/sql/insert.c
index 06635ee..bde0cc1 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));
 }
 
 /*
@@ -1145,7 +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,
+				    sqlite3MPrintf(db, "%s.%s", 
+						   pTab->def->name,
 						   pTab->def->fields[i].name);
 				sqlite3VdbeAddOp3(v, OP_HaltIfNull,
 						  SQLITE_CONSTRAINT_NOTNULL,
@@ -1200,7 +1201,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,
@@ -1404,7 +1405,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 					x = pPk->aiColumn[i];
 					sqlite3VdbeAddOp3(v, OP_Column,
 							  iThisCur, x, regR + i);
-					VdbeComment((v, "%s.%s", pTab->zName,
+					VdbeComment((v, "%s.%s", pTab->def->name,
 						pTab->def->fields[
 							pPk->aiColumn[i]].name));
 				}
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 0cf103c..463bb7e 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -405,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);
@@ -623,7 +623,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 						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 =
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 109c410..5d85ef7 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -239,7 +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 != 0 && 
+					pTab->def->name != NULL);
 				assert(pTab->def->field_count > 0);
 				if (pItem->pSelect
 				    && (pItem->pSelect->
@@ -263,7 +264,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;
@@ -1604,7 +1605,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 fa1de9b..199caf0 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1765,7 +1765,8 @@ generateColumnNames(Parse * pParse,	/* Parser context */
 			} else if (fullNames) {
 				char *zName = 0;
 				zName =
-				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
+				    sqlite3MPrintf(db, "%s.%s", 
+						   pTab->def->name,
 						   zCol);
 				sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName,
 						      SQLITE_DYNAMIC);
@@ -1825,14 +1826,6 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 
 	struct region *region = &fiber()->gc;
 	assert(pTable->def->fields == NULL);
-	if (pTable->def->opts.temporary == false) {
-		/* CREATE VIEW name AS...  without an argument list.  Construct
-		 * the column names from the SELECT statement that defines the view.
-		 */
-		pTable->def->field_count = 0;
-		space_def_delete(pTable->def);
-		pTable->def = sql_ephemeral_space_def_new(pParse);
-	}
 	pTable->def->fields =
 		region_alloc(region, nCol*sizeof(pTable->def->fields[0]));
 	memset(pTable->def->fields, 0, nCol*sizeof(pTable->def->fields[0]));
@@ -1904,11 +1897,8 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 	}
 	sqlite3HashClear(&ht);
 	int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;
-	if (sql_table_def_rebuild(db, pTable) != 0)
-		rc = SQLITE_NOMEM_BKPT;
 	if (rc != SQLITE_OK) {
 		sqlite3DbFree(db, aCol);
-		sqlite3DbFree(db, pTable->def->fields);
 		pTable->def->fields = NULL;
 		pTable->def->field_count = 0;
 		pTable->aCol = 0;
@@ -1972,6 +1962,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 is allocated on region.
  */
 Table *
 sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
@@ -1990,7 +1981,7 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	while (pSelect->pPrior)
 		pSelect = pSelect->pPrior;
 	user_session->sql_flags = savedFlags;
-	pTab = sql_ephemeral_table_new(pParse);
+	pTab = sql_ephemeral_table_new(pParse, NULL);
 	if (pTab == NULL)
 		return 0;
 	/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
@@ -1998,7 +1989,6 @@ sqlite3ResultSetOfSelect(Parse * pParse, Select * pSelect)
 	 */
 	assert(db->lookaside.bDisable);
 	pTab->nTabRef = 1;
-	pTab->zName = 0;
 	pTab->nRowLogEst = 200;
 	assert(200 == sqlite3LogEst(1048576));
 	sqlite3ColumnsFromExprList(pParse, pSelect->pEList, pTab);
@@ -4522,11 +4512,11 @@ withExpand(Walker * pWalker, struct SrcList_item *pFrom)
 			return SQLITE_ERROR;
 
 		assert(pFrom->pTab == 0);
-		pFrom->pTab = pTab = sql_ephemeral_table_new(pParse);
+		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));
@@ -4691,12 +4681,14 @@ selectExpander(Walker * pWalker, Select * p)
 	for (i = 0, pFrom = pTabList->a; i < pTabList->nSrc; i++, pFrom++) {
 		Table *pTab;
 		assert(pFrom->fg.isRecursive == 0 || pFrom->pTab != 0);
-		if (pFrom->fg.isRecursive)
+		if (pFrom->fg.isRecursive) {
 			continue;
+		}
 		assert(pFrom->pTab == 0);
 #ifndef SQLITE_OMIT_CTE
-		if (withExpand(pWalker, pFrom))
+		if (withExpand(pWalker, pFrom)) {
 			return WRC_Abort;
+		}
 		if (pFrom->pTab) {
 		} else
 #endif
@@ -4707,17 +4699,24 @@ selectExpander(Walker * pWalker, Select * p)
 			assert(pFrom->pTab == 0);
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
+			char *name = "sqlite_sq_DEADBEAFDEADBEAF";
 			pFrom->pTab = pTab =
-				sql_ephemeral_table_new(pParse);
+				sql_ephemeral_table_new(pParse, name);
 			if (pTab == NULL)
 				return WRC_Abort;
+			/* rewrite old name with correct pointer */
+			name = sqlite3MPrintf(db, "sqlite_sq_%p", (void *)pTab);
+			sprintf(pTab->def->name, "%s", name);
+			sqlite3DbFree(db, name);
+
 			pTab->nTabRef = 1;
-			pTab->zName =
-			    sqlite3MPrintf(db, "sqlite_sq_%p", (void *)pTab);
 			while (pSel->pPrior) {
 				pSel = pSel->pPrior;
 			}
 			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));
@@ -4727,12 +4726,13 @@ selectExpander(Walker * pWalker, Select * p)
 			assert(pFrom->pTab == 0);
 			pFrom->pTab = pTab =
 			    sqlite3LocateTable(pParse, 0, pFrom->zName);
-			if (pTab == NULL)
+			if (pTab == NULL) {
 				return WRC_Abort;
+			}
 			if (pTab->nTabRef >= 0xffff) {
 				sqlite3ErrorMsg(pParse,
 						"too many references to \"%s\": max 65535",
-						pTab->zName);
+						pTab->def->name);
 				pFrom->pTab = 0;
 				return WRC_Abort;
 			}
@@ -4749,7 +4749,7 @@ selectExpander(Walker * pWalker, Select * p)
 				pFrom->pSelect =
 				    sqlite3SelectDup(db, pTab->pSelect, 0);
 				sqlite3SelectSetName(pFrom->pSelect,
-						     pTab->zName);
+						     pTab->def->name);
 				nCol = pTab->def->field_count;
 				pTab->def->field_count = -1;
 				sqlite3WalkSelect(pWalker, pFrom->pSelect);
@@ -4842,7 +4842,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;
@@ -5288,7 +5288,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 : "");
@@ -5390,14 +5390,13 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		Table *pTab = pItem->pTab;
 		if (pSub == 0)
 			continue;
-
 		/* Catch mismatch in the declared columns of a view and the number of
 		 * columns in the SELECT on the RHS
 		 */
 		if ((int)pTab->def->field_count != pSub->pEList->nExpr) {
 			sqlite3ErrorMsg(pParse,
 					"expected %d columns for '%s' but got %d",
-					pTab->def->field_count, pTab->zName,
+					pTab->def->field_count, pTab->def->name,
 					pSub->pEList->nExpr);
 			goto select_end;
 		}
@@ -5515,7 +5514,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);
@@ -5550,10 +5549,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);
@@ -5564,7 +5563,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 a045286..6eddbec 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1927,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. */
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 9b03be1..ae992eb 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;
@@ -885,7 +885,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",
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 464feee..ad00537 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -75,7 +75,8 @@ sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
 	if (!pTab->pSelect) {
 		sqlite3_value *pValue = 0;
 		Column *pCol = &pTab->aCol[i];
-		VdbeComment((v, "%s.%s", pTab->zName, pTab->def->fields[i].name));
+		VdbeComment((v, "%s.%s", pTab->def->name,
+			pTab->def->fields[i].name));
 		assert(i < (int)pTab->def->field_count);
 
 		Expr *expr = NULL;
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/where.c b/src/box/sql/where.c
index fc0f84c..88f4c28 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -1639,7 +1639,8 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)
 	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);
+			   pItem->zAlias ? pItem->zAlias : 
+			   pTab->def->name);
 #endif
 	const char *zName;
 	if (p->pIndex && (zName = p->pIndex->zName) != 0) {
@@ -4749,7 +4750,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 233fde0..3da7cdb 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1158,7 +1158,8 @@ 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 1b0d961..e602111 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1508,7 +1508,7 @@ sqlite3WhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 		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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 6/7] sql: start using is_view field from space_def
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
                     ` (4 preceding siblings ...)
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 5/7] sql: move names to server Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 11:16     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 7/7] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
  2018-05-03 10:10   ` [tarantool-patches] Re: [PATCH v4 0/7] sql: refactor SQL Parser structures Vladislav Shpilevoy
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Part of #3272.
---
 src/box/sql.c        |  2 ++
 src/box/sql/build.c  | 24 +++++++++++++++++-------
 src/box/sql/delete.c |  9 ++++++---
 src/box/sql/expr.c   |  3 ++-
 src/box/sql/select.c |  2 ++
 src/box/sql/update.c |  3 ++-
 6 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 47f7cb1..9f5a124 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1511,6 +1511,8 @@ int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
 	bool is_view = false;
 	if (pTable != NULL)
 		is_view = pTable->pSelect != NULL;
+	assert(!pTable || pTable->def->opts.is_view == 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));
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 4e0ae87..a98a6bd 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1844,7 +1844,8 @@ 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",
@@ -1892,7 +1893,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
@@ -1921,7 +1923,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;
@@ -1933,7 +1937,8 @@ 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);
 		}
 
@@ -1981,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);
@@ -2033,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;
@@ -2119,6 +2126,7 @@ 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;
@@ -2197,7 +2205,8 @@ 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);
@@ -2920,7 +2929,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;
 	}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index c6ecba6..37baca2 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -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
@@ -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);
 
@@ -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 5f7d741..119940c 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -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. */
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 199caf0..34d296d 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -4746,6 +4746,8 @@ 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,
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index ad00537..2f36423 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -72,7 +72,8 @@ 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->def->name,
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] [PATCH v4 7/7] sql: space_def* instead of Table* in Expr
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
                     ` (5 preceding siblings ...)
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 6/7] sql: start using is_view field from space_def Kirill Shcherbatov
@ 2018-04-28 18:26   ` Kirill Shcherbatov
  2018-05-03 11:32     ` [tarantool-patches] " Vladislav Shpilevoy
  2018-05-03 10:10   ` [tarantool-patches] Re: [PATCH v4 0/7] sql: refactor SQL Parser structures Vladislav Shpilevoy
  7 siblings, 1 reply; 26+ messages in thread
From: Kirill Shcherbatov @ 2018-04-28 18:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Part of #3272.
---
 src/box/field_def.c     |  1 +
 src/box/field_def.h     |  5 ++++
 src/box/sql.c           | 10 ++++----
 src/box/sql/build.c     | 48 ++++++++++++++++++++------------------
 src/box/sql/delete.c    |  4 ++--
 src/box/sql/expr.c      | 61 ++++++++++++++++++++++++++-----------------------
 src/box/sql/fkey.c      | 13 +++++------
 src/box/sql/insert.c    | 24 ++++++++++---------
 src/box/sql/pragma.c    |  8 ++++---
 src/box/sql/resolve.c   | 10 ++++----
 src/box/sql/select.c    | 26 +++++++++++++--------
 src/box/sql/sqliteInt.h | 21 +++++++++--------
 src/box/sql/update.c    | 29 +++++++++++------------
 src/box/sql/vdbeaux.c   | 20 ++++++----------
 src/box/sql/where.c     | 13 +++++++----
 src/box/sql/wherecode.c | 18 +++++++++------
 src/box/sql/whereexpr.c |  2 +-
 17 files changed, 167 insertions(+), 146 deletions(-)

diff --git a/src/box/field_def.c b/src/box/field_def.c
index 010b3b7..63aab46 100644
--- a/src/box/field_def.c
+++ b/src/box/field_def.c
@@ -100,6 +100,7 @@ const struct opt_def field_def_reg[] = {
 
 const struct field_def field_def_default = {
 	.type = FIELD_TYPE_ANY,
+	.affinity = 0,
 	.name = NULL,
 	.is_nullable = false,
 	.nullable_action = ON_CONFLICT_ACTION_DEFAULT,
diff --git a/src/box/field_def.h b/src/box/field_def.h
index dfc1950..4577f6b 100644
--- a/src/box/field_def.h
+++ b/src/box/field_def.h
@@ -102,6 +102,11 @@ struct field_def {
 	 * then UNKNOWN is stored for it.
 	 */
 	enum field_type type;
+	/**
+	 * Affinity type for comparations in SQL.
+	 * FIXME: Remove affinity after types redesign in SQL.
+	 */
+	char affinity;
 	/** 0-terminated field name. */
 	char *name;
 	/** True, if a field can store NULL. */
diff --git a/src/box/sql.c b/src/box/sql.c
index 9f5a124..11ec0b1 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1428,7 +1428,6 @@ static const char *convertSqliteAffinity(int affinity, bool allow_nulls)
  */
 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);
@@ -1471,8 +1470,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 		if (i == pk_forced_int) {
 			t = "integer";
 		} else {
-			t = aCol[i].affinity == SQLITE_AFF_BLOB ? "scalar" :
-				convertSqliteAffinity(aCol[i].affinity,
+			char affinity = def->fields[i].affinity;
+			t = affinity == SQLITE_AFF_BLOB ? "scalar" :
+				convertSqliteAffinity(affinity,
 						      def->fields[i].is_nullable);
 		}
 		p = enc->encode_str(p, t, strlen(t));
@@ -1532,7 +1532,6 @@ 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);
 
@@ -1569,7 +1568,8 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
 		if (pk_forced_int == col) {
 			t = "integer";
 		} else {
-			t = convertSqliteAffinity(aCol[col].affinity,
+			char affinity = def->fields[col].affinity;
+			t = convertSqliteAffinity(affinity,
 						  def->fields[col].is_nullable);
 		}
 		/* do not decode default collation */
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index a98a6bd..742f8a5 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -701,7 +701,7 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		 * TODO: since SQL standard prohibits column creation without
 		 * specified type, the code below should emit an error.
 		 */
-		pCol->affinity = SQLITE_AFF_BLOB;
+		column_def->affinity = SQLITE_AFF_BLOB;
 		pCol->szEst = 1;
 		column_def->type = FIELD_TYPE_SCALAR;
 	} else {
@@ -712,14 +712,14 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		     pType->n == 7) ||
 		    (sqlite3StrNICmp(pType->z, "INT", 3) == 0 &&
 		     pType->n == 3)) {
-			pCol->affinity = SQLITE_AFF_INTEGER;
+			column_def->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);
+			column_def->affinity  = sqlite3AffinityType(zType, 0);
 			sqlite3_free(zType);
 			column_def->type = FIELD_TYPE_SCALAR;
 		}
@@ -1032,8 +1032,10 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 		 */
 		for (pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {
 			assert(pIdx->nColumn == 1);
-			if (pIdx->aiColumn[0] == i)
-				pIdx->coll_array[0] = sql_column_collation(p, i);
+			if (pIdx->aiColumn[0] == i) {
+				pIdx->coll_array[0] =
+					sql_column_collation(p->def, i);
+			}
 		}
 	}
 }
@@ -1046,10 +1048,10 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
  * @retval Pointer to collation.
  */
 struct coll *
-sql_column_collation(Table *table, uint32_t column)
+sql_column_collation(struct space_def *def, uint32_t column)
 {
-	assert(table != NULL);
-	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(table->tnum);
+	assert(def != NULL);
+	uint32_t space_id = def->id;
 	struct space *space = space_by_id(space_id);
 	/*
 	 * It is not always possible to fetch collation directly
@@ -1064,9 +1066,9 @@ sql_column_collation(Table *table, uint32_t column)
 	 * SQL specific structures.
 	 */
 	if (space == NULL || space_index(space, 0) == NULL) {
-		assert(column < (uint32_t)table->def->field_count);
+		assert(column < (uint32_t)def->field_count);
 		struct coll *coll =
-			coll_by_id(table->def->fields[column].coll_id);
+			coll_by_id(def->fields[column].coll_id);
 		return coll;
 	}
 
@@ -1322,18 +1324,19 @@ createTableStmt(sqlite3 * db, Table * p)
 		k += sqlite3Strlen30(&zStmt[k]);
 		zSep = zSep2;
 		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);
-		testcase(pCol->affinity == SQLITE_AFF_TEXT);
-		testcase(pCol->affinity == SQLITE_AFF_NUMERIC);
-		testcase(pCol->affinity == SQLITE_AFF_INTEGER);
-		testcase(pCol->affinity == SQLITE_AFF_REAL);
-
-		zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
+		char affinity = p->def->fields[i].affinity;
+		assert(affinity - SQLITE_AFF_BLOB >= 0);
+		assert(affinity - SQLITE_AFF_BLOB < ArraySize(azType));
+		testcase(affinity == SQLITE_AFF_BLOB);
+		testcase(affinity == SQLITE_AFF_TEXT);
+		testcase(affinity == SQLITE_AFF_NUMERIC);
+		testcase(affinity == SQLITE_AFF_INTEGER);
+		testcase(affinity == SQLITE_AFF_REAL);
+
+		zType = azType[affinity - SQLITE_AFF_BLOB];
 		len = sqlite3Strlen30(zType);
-		assert(pCol->affinity == SQLITE_AFF_BLOB
-		       || pCol->affinity == sqlite3AffinityType(zType, 0));
+		assert(affinity == SQLITE_AFF_BLOB
+		       || affinity == sqlite3AffinityType(zType, 0));
 		memcpy(&zStmt[k], zType, len);
 		k += len;
 		assert(k <= n);
@@ -1843,6 +1846,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	 */
 	if (db->init.busy)
 		p->tnum = db->init.newTnum;
+	p->def->id = SQLITE_PAGENO_TO_SPACEID(p->tnum);
 
 	assert(p->def->opts.is_view == (p->pSelect != NULL));
 	if (!p->def->opts.is_view) {
@@ -3094,7 +3098,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 				goto exit_create_index;
 			}
 		} else if (j >= 0) {
-			coll = sql_column_collation(pTab, j);
+			coll = sql_column_collation(pTab->def, j);
 		} else {
 			coll = NULL;
 		}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 37baca2..5056005 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -431,7 +431,7 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
 		if (!isView) {
 			for (i = 0; i < nPk; i++) {
 				assert(pPk->aiColumn[i] >= 0);
-				sqlite3ExprCodeGetColumnOfTable(v, pTab,
+				sqlite3ExprCodeGetColumnOfTable(v, pTab->def,
 								iTabCur,
 								pPk->
 								aiColumn[i],
@@ -747,7 +747,7 @@ sqlite3GenerateRowDelete(Parse * pParse,	/* Parsing context */
 			testcase(mask != 0xffffffff && iCol == 32);
 			if (mask == 0xffffffff
 			    || (iCol <= 31 && (mask & MASKBIT32(iCol)) != 0)) {
-				sqlite3ExprCodeGetColumnOfTable(v, pTab,
+				sqlite3ExprCodeGetColumnOfTable(v, pTab->def,
 								iDataCur, iCol,
 								iOld + iCol +
 								1);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 119940c..4e20098 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -46,10 +46,11 @@ static int exprCodeVector(Parse * pParse, Expr * p, int *piToFree);
  * Return the affinity character for a single column of a table.
  */
 char
-sqlite3TableColumnAffinity(Table * pTab, int iCol)
+sqlite3TableColumnAffinity(struct space_def *def, int iCol)
 {
-	assert(iCol < (int)pTab->def->field_count);
-	return iCol >= 0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+	assert(iCol < (int)def->field_count);
+	return iCol >= 0 ? def->fields[iCol].affinity :
+	       SQLITE_AFF_INTEGER;
 }
 
 /*
@@ -90,7 +91,8 @@ sqlite3ExprAffinity(Expr * pExpr)
 	}
 #endif
 	if (op == TK_AGG_COLUMN || op == TK_COLUMN) {
-		return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
+		return sqlite3TableColumnAffinity(pExpr->space_def,
+						  pExpr->iColumn);
 	}
 	if (op == TK_SELECT_COLUMN) {
 		assert(pExpr->pLeft->flags & EP_xIsSelect);
@@ -179,13 +181,13 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_found)
 		}
 		if ((op == TK_AGG_COLUMN || op == TK_COLUMN ||
 		     op == TK_REGISTER || op == TK_TRIGGER) &&
-		    p->pTab != 0) {
+		    p->space_def != 0) {
 			/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
 			 * a TK_COLUMN but was previously evaluated and cached in a register
 			 */
 			int j = p->iColumn;
 			if (j >= 0) {
-				coll = sql_column_collation(p->pTab, j);
+				coll = sql_column_collation(p->space_def, j);
 				*is_found = true;
 			}
 			break;
@@ -2132,10 +2134,10 @@ sqlite3ExprCanBeNull(const Expr * p)
 	case TK_BLOB:
 		return 0;
 	case TK_COLUMN:
-		assert(p->pTab != 0);
+		assert(p->space_def!= 0);
 		return ExprHasProperty(p, EP_CanBeNull) ||
 		       (p->iColumn >= 0
-		        && table_column_is_nullable(p->pTab, p->iColumn));
+		        && table_column_is_nullable(p->space_def, p->iColumn));
 	default:
 		return 1;
 	}
@@ -2435,7 +2437,9 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
 		for (i = 0; i < nExpr && affinity_ok; i++) {
 			Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
 			int iCol = pEList->a[i].pExpr->iColumn;
-			char idxaff = sqlite3TableColumnAffinity(pTab, iCol);	/* RHS table */
+			/* RHS table */
+			char idxaff =
+				sqlite3TableColumnAffinity(pTab->def, iCol);
 			char cmpaff = sqlite3CompareAffinity(pLhs, idxaff);
 			testcase(cmpaff == SQLITE_AFF_BLOB);
 			testcase(cmpaff == SQLITE_AFF_TEXT);
@@ -3175,8 +3179,9 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
 		struct Index *pk = sqlite3PrimaryKeyIndex(tab);
 		assert(pk);
 
+		char affinity = tab->def->fields[pk->aiColumn[0]].affinity;
 		if (pk->nColumn == 1
-		    && tab->aCol[pk->aiColumn[0]].affinity == 'D'
+		    && affinity == 'D'
 		    && pk->aiColumn[0] < nVector) {
 			int reg_pk = rLhs + pk->aiColumn[0];
 			sqlite3VdbeAddOp2(v, OP_MustBeInt, reg_pk, destIfFalse);
@@ -3519,7 +3524,7 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse,	/* The parsing context */
 		sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr,
 				    regOut);
 	} else {
-		sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable,
+		sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable->def,
 						iTabCur, iTabCol, regOut);
 	}
 }
@@ -3529,7 +3534,7 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse,	/* The parsing context */
  */
 void
 sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
-				Table * pTab,	/* The table containing the value */
+				struct space_def *space_def,
 				int iTabCur,	/* The PK cursor */
 				int iCol,	/* Index of the column to extract */
 				int regOut	/* Extract the value into this register */
@@ -3537,7 +3542,7 @@ sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
 {
 	sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut);
 	if (iCol >= 0) {
-		sqlite3ColumnDefault(v, pTab, iCol, regOut);
+		sqlite3ColumnDefault(v, space_def, iCol, regOut);
 	}
 }
 
@@ -3552,7 +3557,7 @@ sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
  */
 int
 sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context */
-			 Table * pTab,	/* Description of the table we are reading from */
+			 struct space_def * space_def,
 			 int iColumn,	/* Index of the table column */
 			 int iTable,	/* The cursor pointing to the table */
 			 int iReg,	/* Store results here */
@@ -3572,7 +3577,7 @@ sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context
 		}
 	}
 	assert(v != 0);
-	sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
+	sqlite3ExprCodeGetColumnOfTable(v, space_def, iTable, iColumn, iReg);
 	if (p5) {
 		sqlite3VdbeChangeP5(v, p5);
 	} else {
@@ -3583,14 +3588,14 @@ sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context
 
 void
 sqlite3ExprCodeGetColumnToReg(Parse * pParse,	/* Parsing and code generating context */
-			      Table * pTab,	/* Description of the table we are reading from */
+			      struct space_def * space_def,
 			      int iColumn,	/* Index of the table column */
 			      int iTable,	/* The cursor pointing to the table */
 			      int iReg	/* Store results here */
     )
 {
 	int r1 =
-	    sqlite3ExprCodeGetColumn(pParse, pTab, iColumn, iTable, iReg, 0);
+	    sqlite3ExprCodeGetColumn(pParse, space_def, iColumn, iTable, iReg, 0);
 	if (r1 != iReg)
 		sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, r1, iReg);
 }
@@ -3777,7 +3782,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 					iTab = pParse->iSelfTab;
 				}
 			}
-			return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+			return sqlite3ExprCodeGetColumn(pParse, pExpr->space_def,
 							pExpr->iColumn, iTab,
 							target, pExpr->op2);
 		}
@@ -4241,23 +4246,21 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			 *   p1==1   ->    old.a         p1==4   ->    new.a
 			 *   p1==2   ->    old.b         p1==5   ->    new.b
 			 */
-			Table *pTab = pExpr->pTab;
+			struct space_def *def = pExpr->space_def;
 			int p1 =
-			    pExpr->iTable * (pTab->def->field_count + 1) + 1 +
+			    pExpr->iTable * (def->field_count + 1) + 1 +
 			    pExpr->iColumn;
 
 			assert(pExpr->iTable == 0 || pExpr->iTable == 1);
 			assert(pExpr->iColumn >= 0
-			       && pExpr->iColumn < (int)pTab->def->field_count);
-			assert(pTab->iPKey < 0
-			       || pExpr->iColumn != pTab->iPKey);
+			       && pExpr->iColumn < (int)def->field_count);
 			assert(p1 >= 0 && p1 <
-					  ((int)pTab->def->field_count * 2 + 2));
+					  ((int)def->field_count * 2 + 2));
 
 			sqlite3VdbeAddOp2(v, OP_Param, p1, target);
 			VdbeComment((v, "%s.%s -> $%d",
 				    (pExpr->iTable ? "new" : "old"),
-				    pExpr->pTab->def->fields[pExpr->iColumn].name,
+				    def->fields[pExpr->iColumn].name,
 				    target));
 
 #ifndef SQLITE_OMIT_FLOATING_POINT
@@ -4267,9 +4270,9 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			 * EVIDENCE-OF: R-60985-57662 SQLite will convert the value back to
 			 * floating point when extracting it from the record.
 			 */
+			char affinity = def->fields[pExpr->iColumn].affinity;
 			if (pExpr->iColumn >= 0
-			    && pTab->aCol[pExpr->iColumn].affinity ==
-			    SQLITE_AFF_REAL) {
+			    && affinity == SQLITE_AFF_REAL) {
 				sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
 			}
 #endif
@@ -5440,8 +5443,8 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
 							 pAggInfo)) >= 0) {
 							pCol =
 							    &pAggInfo->aCol[k];
-							pCol->pTab =
-							    pExpr->pTab;
+							pCol->space_def =
+							    pExpr->space_def;
 							pCol->iTable =
 							    pExpr->iTable;
 							pCol->iColumn =
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 16f72d8..52f86d4 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -299,7 +299,7 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
 					 * unusable. Bail out early in this case.
 					 */
 					struct coll *def_coll;
-					def_coll = sql_column_collation(pParent,
+					def_coll = sql_column_collation(pParent->def,
 									iCol);
 					struct coll *coll;
 					coll = sql_index_collation(pIdx, i);
@@ -527,15 +527,14 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
     )
 {
 	Expr *pExpr;
-	Column *pCol;
 	sqlite3 *db = pParse->db;
 
 	pExpr = sqlite3Expr(db, TK_REGISTER, 0);
 	if (pExpr) {
 		if (iCol >= 0 && iCol != pTab->iPKey) {
-			pCol = &pTab->aCol[iCol];
 			pExpr->iTable = regBase + iCol + 1;
-			pExpr->affinity = pCol->affinity;
+			char affinity = pTab->def->fields[iCol].affinity;
+			pExpr->affinity = affinity;
 			const char *coll_name = "binary";
 			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
 							    coll_name);
@@ -553,14 +552,14 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
  */
 static Expr *
 exprTableColumn(sqlite3 * db,	/* The database connection */
-		Table * pTab,	/* The table whose column is desired */
+		struct space_def *def,
 		int iCursor,	/* The open cursor on the table */
 		i16 iCol	/* The column that is wanted */
     )
 {
 	Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
 	if (pExpr) {
-		pExpr->pTab = pTab;
+		pExpr->space_def = def;
 		pExpr->iTable = iCursor;
 		pExpr->iColumn = iCol;
 	}
@@ -673,7 +672,7 @@ fkScanChildren(Parse * pParse,	/* Parse context */
 			i16 iCol = pIdx->aiColumn[i];
 			assert(iCol >= 0);
 			pLeft = exprTableRegister(pParse, pTab, regData, iCol);
-			pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor,
+			pRight = exprTableColumn(db, pTab->def, pSrc->a[0].iCursor,
 						 iCol);
 			pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
 			pAll = sqlite3ExprAnd(db, pAll, pEq);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index bde0cc1..7c21359 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -90,7 +90,6 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
 		 */
 		int n;
 		int nColumn = index_column_count(pIdx);
-		Table *pTab = pIdx->pTable;
 		pIdx->zColAff =
 		    (char *)sqlite3DbMallocRaw(0, nColumn + 1);
 		if (!pIdx->zColAff) {
@@ -100,7 +99,9 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
 		for (n = 0; n < nColumn; n++) {
 			i16 x = pIdx->aiColumn[n];
 			if (x >= 0) {
-				pIdx->zColAff[n] = pTab->aCol[x].affinity;
+				char affinity = pIdx->pTable->
+					def->fields[x].affinity;
+				pIdx->zColAff[n] = affinity;
 			} else {
 				char aff;
 				assert(x == XN_EXPR);
@@ -154,7 +155,8 @@ sqlite3TableAffinity(Vdbe * v, Table * pTab, int iReg)
 		}
 
 		for (i = 0; i < (int)pTab->def->field_count; i++) {
-			zColAff[i] = pTab->aCol[i].affinity;
+			char affinity = pTab->def->fields[i].affinity;
+			zColAff[i] = affinity;
 		}
 		do {
 			zColAff[i--] = 0;
@@ -1115,7 +1117,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			/* Don't bother checking for NOT NULL on columns that do not change */
 			continue;
 		}
-		if (table_column_is_nullable(pTab, i)
+		if (table_column_is_nullable(pTab->def, i)
 		    || (pTab->tabFlags & TF_Autoincrement
 			&& pTab->iAutoIncPKey == i))
 			continue;	/* This column is allowed to be NULL */
@@ -1805,17 +1807,17 @@ xferOptimization(Parse * pParse,	/* Parser context */
 		return 0;	/* Both tables must have the same INTEGER PRIMARY KEY */
 	}
 	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) {
+		char pdest_affinity = pDest->def->fields[i].affinity;
+		char psrc_affinity = pSrc->def->fields[i].affinity;
+		if (pdest_affinity != psrc_affinity) {
 			return 0;	/* Affinity must be the same on all columns */
 		}
-		if (sql_column_collation(pDest, i) !=
-		    sql_column_collation(pSrc, i)) {
+		if (sql_column_collation(pDest->def, i) !=
+		    sql_column_collation(pSrc->def, i)) {
 			return 0;	/* Collating sequence must be the same on all columns */
 		}
-		if (!table_column_is_nullable(pDest, i)
-		    && table_column_is_nullable(pSrc, i)) {
+		if (!table_column_is_nullable(pDest->def, i)
+		    && table_column_is_nullable(pSrc->def, i)) {
 			return 0;	/* tab2 must be NOT NULL if tab1 is */
 		}
 		/* Default values for second and subsequent columns need to match. */
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 463bb7e..c49817f 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -373,7 +373,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 						     i; k++) {
 						}
 					}
-					bool nullable = table_column_is_nullable(pTab, i);
+					bool nullable =
+						table_column_is_nullable(pTab->def,
+									 i);
 					uint32_t space_id =
 						SQLITE_PAGENO_TO_SPACEID(
 							pTab->tnum);
@@ -691,7 +693,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 									  iKey,
 									  regRow);
 							sqlite3ColumnDefault(v,
-									     pTab,
+									     pTab->def,
 									     iKey,
 									     regRow);
 							sqlite3VdbeAddOp2(v,
@@ -708,7 +710,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 					} else {
 						for (j = 0; j < pFK->nCol; j++) {
 							sqlite3ExprCodeGetColumnOfTable
-							    (v, pTab, 0,
+							    (v, pTab->def, 0,
 							     aiCols ? aiCols[j]
 							     : pFK->aCol[j].
 							     iFrom, regRow + j);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 5d85ef7..f24399c 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -227,7 +227,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 
 	/* Initialize the node to no-match */
 	pExpr->iTable = -1;
-	pExpr->pTab = 0;
+	pExpr->space_def = NULL;
 	ExprSetVVAProperty(pExpr, EP_NoReduce);
 
 	/* Start at the inner-most context and move outward until a match is found */
@@ -301,7 +301,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 			}
 			if (pMatch) {
 				pExpr->iTable = pMatch->iCursor;
-				pExpr->pTab = pMatch->pTab;
+				pExpr->space_def = pMatch->pTab->def;
 				/* RIGHT JOIN not (yet) supported */
 				assert((pMatch->fg.jointype & JT_RIGHT) == 0);
 				if ((pMatch->fg.jointype & JT_LEFT) != 0) {
@@ -365,7 +365,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 						     : (((u32) 1) << iCol));
 					}
 					pExpr->iColumn = (i16) iCol;
-					pExpr->pTab = pTab;
+					pExpr->space_def = pTab->def;
 					isTrigger = 1;
 				}
 			}
@@ -499,9 +499,9 @@ sqlite3CreateColumnExpr(sqlite3 * db, SrcList * pSrc, int iSrc, int iCol)
 	Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
 	if (p) {
 		struct SrcList_item *pItem = &pSrc->a[iSrc];
-		p->pTab = pItem->pTab;
+		p->space_def = pItem->pTab->def;
 		p->iTable = pItem->iCursor;
-		if (p->pTab->iPKey == iCol) {
+		if (pItem->pTab->iPKey == iCol) {
 			p->iColumn = -1;
 		} else {
 			p->iColumn = (ynVar) iCol;
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 34d296d..ff9f18b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1636,7 +1636,7 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
 				break;
 			}
 
-			assert(pTab && pExpr->pTab == pTab);
+			assert(pTab && pExpr->space_def == pTab->def);
 			if (pS) {
 				/* The "table" is actually a sub-select or a view in the FROM clause
 				 * of the SELECT statement. Return the declaration type and origin
@@ -1842,19 +1842,23 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			/* If the column contains an "AS <name>" phrase, use <name> as the name */
 		} else {
 			Expr *pColExpr = p;	/* The expression that is the result column name */
-			Table *pTab;	/* Table associated with this expression */
+			struct space_def *space_def;
 			while (pColExpr->op == TK_DOT) {
 				pColExpr = pColExpr->pRight;
 				assert(pColExpr != 0);
 			}
 			if (pColExpr->op == TK_COLUMN
-			    && ALWAYS(pColExpr->pTab != 0)) {
+			    && ALWAYS(pColExpr->space_def != NULL)) {
 				/* For columns use the column name name */
 				int iCol = pColExpr->iColumn;
-				pTab = pColExpr->pTab;
+				space_def = pColExpr->space_def;
+				Table *pTable =
+					sqlite3LocateTable(pParse, 0,
+							   space_def->name);
+				assert(pTable != NULL);
 				if (iCol < 0)
-					iCol = pTab->iPKey;
-				zName = pTab->def->fields[iCol].name;
+					iCol = pTable->iPKey;
+				zName = space_def->fields[iCol].name;
 			} else if (pColExpr->op == TK_ID) {
 				assert(!ExprHasProperty(pColExpr, EP_IntValue));
 				zName = pColExpr->u.zToken;
@@ -1946,11 +1950,13 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
 		p = a[i].pExpr;
 		type = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
 		szAll += pCol->szEst;
-		pCol->affinity = sqlite3ExprAffinity(p);
 		pTab->def->fields[i].type = type;
 
-		if (pCol->affinity == 0)
-			pCol->affinity = SQLITE_AFF_BLOB;
+		char affinity = sqlite3ExprAffinity(p);
+		if (affinity == 0)
+			affinity = SQLITE_AFF_BLOB;
+		pTab->def->fields[i].affinity = affinity;
+
 		bool unused;
 		struct coll *coll = sql_expr_coll(pParse, p, &unused);
 		if (coll != NULL && pTab->def->fields[i].coll_id == COLL_NONE)
@@ -5931,7 +5937,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 					if (pCol->iSorterColumn >= j) {
 						int r1 = j + regBase;
 						sqlite3ExprCodeGetColumnToReg
-						    (pParse, pCol->pTab,
+						    (pParse, pCol->space_def,
 						     pCol->iColumn,
 						     pCol->iTable, r1);
 						j++;
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 6eddbec..4c2c6fb 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1867,7 +1867,6 @@ struct Savepoint {
  * of this structure.
  */
 struct Column {
-	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 */
 };
@@ -2226,7 +2225,8 @@ struct AggInfo {
 	int mnReg, mxReg;	/* Range of registers allocated for aCol and aFunc */
 	ExprList *pGroupBy;	/* The group by clause */
 	struct AggInfo_col {	/* For each column used in source tables */
-		Table *pTab;	/* Source table */
+		/* Pointer to space definition. */
+		struct space_def *space_def;
 		int iTable;	/* Cursor number of the source table */
 		int iColumn;	/* Column number within the source table */
 		int iSorterColumn;	/* Column number in the sorting index */
@@ -2361,7 +2361,8 @@ struct Expr {
 				 * TK_AGG_FUNCTION: nesting depth
 				 */
 	AggInfo *pAggInfo;	/* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
-	Table *pTab;		/* Table for TK_COLUMN expressions. */
+	/* Pointer for table relative definition. */
+	struct space_def *space_def;
 };
 
 /*
@@ -3520,7 +3521,7 @@ void sqlite3AddCollateType(Parse *, Token *);
 const char *
 column_collation_name(Table *, uint32_t);
 struct coll *
-sql_column_collation(Table *, uint32_t);
+sql_column_collation(struct space_def *, uint32_t);
 const char *
 index_collation_name(Index *, uint32_t);
 struct coll *
@@ -3607,9 +3608,9 @@ int sqlite3WhereOkOnePass(WhereInfo *, int *);
 #define ONEPASS_SINGLE   1	/* ONEPASS valid for a single row update */
 #define ONEPASS_MULTI    2	/* ONEPASS is valid for multiple rows */
 void sqlite3ExprCodeLoadIndexColumn(Parse *, Index *, int, int, int);
-int sqlite3ExprCodeGetColumn(Parse *, Table *, int, int, int, u8);
-void sqlite3ExprCodeGetColumnToReg(Parse *, Table *, int, int, int);
-void sqlite3ExprCodeGetColumnOfTable(Vdbe *, Table *, int, int, int);
+int sqlite3ExprCodeGetColumn(Parse *, struct space_def *, int, int, int, u8);
+void sqlite3ExprCodeGetColumnToReg(Parse *, struct space_def *, int, int, int);
+void sqlite3ExprCodeGetColumnOfTable(Vdbe *, struct space_def *, int, int, int);
 void sqlite3ExprCodeMove(Parse *, int, int, int);
 void sqlite3ExprCacheStore(Parse *, int, int, int);
 void sqlite3ExprCachePush(Parse *);
@@ -3802,7 +3803,7 @@ const char *sqlite3IndexAffinityStr(sqlite3 *, Index *);
 void sqlite3TableAffinity(Vdbe *, Table *, int);
 char sqlite3CompareAffinity(Expr * pExpr, char aff2);
 int sqlite3IndexAffinityOk(Expr * pExpr, char idx_affinity);
-char sqlite3TableColumnAffinity(Table *, int);
+char sqlite3TableColumnAffinity(struct space_def *, int);
 char sqlite3ExprAffinity(Expr * pExpr);
 int sqlite3Atoi64(const char *, i64 *, int);
 int sqlite3DecOrHexToI64(const char *, i64 *);
@@ -3890,7 +3891,7 @@ int sqlite3ResolveExprListNames(NameContext *, ExprList *);
 void sqlite3ResolveSelectNames(Parse *, Select *, NameContext *);
 void sqlite3ResolveSelfReference(Parse *, Table *, int, Expr *, ExprList *);
 int sqlite3ResolveOrderGroupBy(Parse *, Select *, ExprList *, const char *);
-void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
+void sqlite3ColumnDefault(Vdbe *, struct space_def *, int, int);
 void sqlite3AlterFinishAddColumn(Parse *, Token *);
 void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
 char* rename_table(sqlite3 *, const char *, const char *, bool *);
@@ -4141,6 +4142,6 @@ enum on_conflict_action
 table_column_nullable_action(struct Table *tab, uint32_t column);
 
 bool
-table_column_is_nullable(struct Table *tab, uint32_t column);
+table_column_is_nullable(struct space_def *def, uint32_t column);
 
 #endif				/* SQLITEINT_H */
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 2f36423..38f824f 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -69,30 +69,27 @@
  * space.
  */
 void
-sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
+sqlite3ColumnDefault(Vdbe * v, struct space_def * def, int i, int iReg)
 {
-	assert(pTab != 0);
-	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
-	if (!pTab->def->opts.is_view) {
+	assert(def != NULL);
+	if (!def->opts.is_view) {
 		sqlite3_value *pValue = 0;
-		Column *pCol = &pTab->aCol[i];
-		VdbeComment((v, "%s.%s", pTab->def->name,
-			pTab->def->fields[i].name));
-		assert(i < (int)pTab->def->field_count);
+		char affinity = def->fields[i].affinity;
+		VdbeComment((v, "%s.%s", def->name, def->fields[i].name));
+		assert(i < (int)def->field_count);
 
 		Expr *expr = NULL;
 		struct space *space =
-			space_cache_find(SQLITE_PAGENO_TO_SPACEID(pTab->tnum));
+			space_cache_find(def->id);
 		if (space != NULL && space->def->fields != NULL)
 			expr = space->def->fields[i].default_value_expr;
 		sqlite3ValueFromExpr(sqlite3VdbeDb(v),
-				     expr,
-				     pCol->affinity, &pValue);
+				     expr, affinity, &pValue);
 		if (pValue) {
 			sqlite3VdbeAppendP4(v, pValue, P4_MEM);
 		}
 #ifndef SQLITE_OMIT_FLOATING_POINT
-		if (pTab->aCol[i].affinity == SQLITE_AFF_REAL) {
+		if (affinity == SQLITE_AFF_REAL) {
 			sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
 		}
 #endif
@@ -381,7 +378,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	} else {
 		for (i = 0; i < nPk; i++) {
 			assert(pPk->aiColumn[i] >= 0);
-			sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,
+			sqlite3ExprCodeGetColumnOfTable(v, pTab->def, iDataCur,
 							pPk->aiColumn[i],
 							iPk + i);
 		}
@@ -486,7 +483,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 			    || (i < 32 && (oldmask & MASKBIT32(i)) != 0)
 			    || table_column_is_in_pk(pTab, i)) {
 				testcase(oldmask != 0xffffffff && i == 31);
-				sqlite3ExprCodeGetColumnOfTable(v, pTab,
+				sqlite3ExprCodeGetColumnOfTable(v, pTab->def,
 								iDataCur, i,
 								regOld + i);
 			} else {
@@ -529,7 +526,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 				 */
 				testcase(i == 31);
 				testcase(i == 32);
-				sqlite3ExprCodeGetColumnToReg(pParse, pTab, i,
+				sqlite3ExprCodeGetColumnToReg(pParse, pTab->def, i,
 							      iDataCur,
 							      regNew + i);
 			} else {
@@ -570,7 +567,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		 */
 		for (i = 0; i < (int)pTab->def->field_count; i++) {
 			if (aXRef[i] < 0 && i != pTab->iPKey) {
-				sqlite3ExprCodeGetColumnOfTable(v, pTab,
+				sqlite3ExprCodeGetColumnOfTable(v, pTab->def,
 								iDataCur, i,
 								regNew + i);
 			}
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index f76c689..f9927ed 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4724,28 +4724,22 @@ table_column_nullable_action(struct Table *tab, uint32_t column)
  * @return return nullability flag value
  */
 bool
-table_column_is_nullable(struct Table *tab, uint32_t column)
+table_column_is_nullable(struct space_def *def, uint32_t column)
 {
 	/* Temporary hack: until Tarantoool's ephemeral spaces are on-boarded,
 	*  views are not handled properly in Tarantool as well. */
-	if (!(tab->tabFlags | TF_Ephemeral || space_is_view(tab))) {
-		uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(tab->tnum);
-		struct space *space = space_cache_find(space_id);
-
-		assert(space);
-
+	struct space *space = space_cache_find(def->id);
+	if (space != NULL && !space->def->opts.is_view) {
 		struct tuple_format *format = space->format;
-
 		assert(format);
 		assert(format->field_count > column);
-
 		return format->fields[column].nullable_action ==
-			ON_CONFLICT_ACTION_NONE;
+		       ON_CONFLICT_ACTION_NONE;
 	} else {
 		/* tab is ephemeral (in SQLite sense).  */
-		assert(tab->def->fields[column].is_nullable ==
-		       (tab->def->fields[column].nullable_action ==
+		assert(def->fields[column].is_nullable ==
+		       (def->fields[column].nullable_action ==
 			ON_CONFLICT_ACTION_NONE));
-		return tab->def->fields[column].is_nullable;
+		return def->fields[column].is_nullable;
 	}
 }
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 88f4c28..d33fd9c 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -378,7 +378,9 @@ whereScanInit(WhereScan * pScan,	/* The WhereScan object being initialized */
 		if (iColumn == XN_EXPR) {
 			pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
 		} else if (iColumn >= 0) {
-			pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
+			char affinity =
+				pIdx->pTable->def->fields[iColumn].affinity;
+			pScan->idxaff = affinity;
 			pScan->coll = sql_index_collation(pIdx, j);
 			pScan->is_column_seen = true;
 		}
@@ -491,7 +493,7 @@ indexColumnNotNull(Index * pIdx, int iCol)
 	assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));
 	j = pIdx->aiColumn[iCol];
 	if (j >= 0) {
-		return !table_column_is_nullable(pIdx->pTable, j);
+		return !table_column_is_nullable(pIdx->pTable->def, j);
 	} else if (j == (-1)) {
 		return 1;
 	} else {
@@ -2237,7 +2239,8 @@ whereRangeVectorLen(Parse * pParse,	/* Parsing context */
 
 		aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
 		idxaff =
-		    sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
+		    sqlite3TableColumnAffinity(pIdx->pTable->def,
+					       pLhs->iColumn);
 		if (aff != idxaff)
 			break;
 
@@ -3331,8 +3334,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
 				if (isOrderDistinct
 				    && iColumn >= 0
 				    && j >= pLoop->nEq
-				    && table_column_is_nullable(pIndex->pTable,
-								iColumn)) {
+				    && table_column_is_nullable(
+				    	pIndex->pTable->def, iColumn)) {
 					isOrderDistinct = 0;
 				}
 
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 3da7cdb..3459020 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -909,7 +909,7 @@ codeCursorHintFixExpr(Walker * pWalker, Expr * pExpr)
 		if (pExpr->iTable != pHint->iTabCur) {
 			Vdbe *v = pWalker->pParse->pVdbe;
 			int reg = ++pWalker->pParse->nMem;	/* Register for column value */
-			sqlite3ExprCodeGetColumnOfTable(v, pExpr->pTab,
+			sqlite3ExprCodeGetColumnOfTable(v, pExpr->pTab->def,
 							pExpr->iTable,
 							pExpr->iColumn, reg);
 			pExpr->op = TK_REGISTER;
@@ -1261,8 +1261,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 			 * FYI: entries in an index are ordered as follows:
 			 *      NULL, ... NULL, min_value, ...
 			 */
-			if ((j >= 0 && table_column_is_nullable(pIdx->pTable, j))
-			    || j == XN_EXPR) {
+			if ((j >= 0 &&
+			     table_column_is_nullable(pIdx->pTable->def, j)) ||
+			    j == XN_EXPR) {
 				assert(pLoop->nSkip == 0);
 				bSeekPastNull = 1;
 				nExtraReg = 1;
@@ -1308,7 +1309,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 			if (pRangeStart == 0) {
 				j = pIdx->aiColumn[nEq];
 				if ((j >= 0
-				     && table_column_is_nullable(pIdx->pTable, j)) || j == XN_EXPR) {
+				     && table_column_is_nullable(
+				     	pIdx->pTable->def, j)) ||
+				    j == XN_EXPR) {
 					bSeekPastNull = 1;
 				}
 			}
@@ -1387,8 +1390,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 		struct Index *pk = sqlite3PrimaryKeyIndex(pIdx->pTable);
 		assert(pk);
 		int nPkCol = index_column_count(pk);
-		if (nPkCol == 1
-		    && pIdx->pTable->aCol[pk->aiColumn[0]].affinity == 'D') {
+		char affinity =
+			pIdx->pTable->def->fields[pk->aiColumn[0]].affinity;
+		if (nPkCol == 1 && affinity == 'D') {
 			/* Right now INTEGER PRIMARY KEY is the only option to
 			 * get Tarantool's INTEGER column type. Need special handling
 			 * here: try to loosely convert FLOAT to INT. If RHS type
@@ -1725,7 +1729,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 						for (iPk = 0; iPk < nPk; iPk++) {
 							int iCol = pPk->aiColumn[iPk];
 							sqlite3ExprCodeGetColumnToReg
-								(pParse, pTab,
+								(pParse, pTab->def,
 								 iCol, iCur,
 								 r + iPk);
 						}
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index e602111..d1f2cc5 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -1516,7 +1516,7 @@ sqlite3WhereTabFuncArgs(Parse * pParse,	/* Parsing context */
 			return;
 		pColRef->iTable = pItem->iCursor;
 		pColRef->iColumn = k++;
-		pColRef->pTab = pTab;
+		pColRef->space_def = pTab->def;
 		pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
 				     sqlite3ExprDup(pParse->db,
 						    pArgs->a[j].pExpr, 0));
-- 
2.7.4

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 2/7] sql: remove zName and nColumn from SQL
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 2/7] sql: remove zName and nColumn from SQL Kirill Shcherbatov
@ 2018-05-03 10:10     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 10:10 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Same as in the previous letter.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> 1. Removed zName from SQL Column.
> 2. Removed zColumns from SQL Table.
> 3. Refactored Parser to use def_expression directly.
> 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.
> ---
>   src/box/space_def.c     |  29 ++++---
>   src/box/sql.c           |  79 +++++++++++++++--
>   src/box/sql.h           |  32 +++++++
>   src/box/sql/alter.c     |  32 ++++---
>   src/box/sql/analyze.c   |   5 +-
>   src/box/sql/build.c     | 221 +++++++++++++++++++++++-------------------------
>   src/box/sql/delete.c    |   6 +-
>   src/box/sql/expr.c      |  11 +--
>   src/box/sql/fkey.c      |  20 ++---
>   src/box/sql/insert.c    |  55 ++++++------
>   src/box/sql/pragma.c    |  24 ++++--
>   src/box/sql/prepare.c   |  26 +++---
>   src/box/sql/resolve.c   |  16 ++--
>   src/box/sql/select.c    | 112 ++++++++++++++----------
>   src/box/sql/sqliteInt.h |   6 +-
>   src/box/sql/tokenize.c  |   3 +
>   src/box/sql/trigger.c   |   2 +
>   src/box/sql/update.c    |  29 ++++---
>   src/box/sql/where.c     |   6 +-
>   src/box/sql/wherecode.c |   2 +-
>   src/box/sql/whereexpr.c |   4 +-
>   21 files changed, 433 insertions(+), 287 deletions(-)
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 0/7] sql: refactor SQL Parser structures
  2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
                     ` (6 preceding siblings ...)
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 7/7] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-03 10:10   ` Vladislav Shpilevoy
  7 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 10:10 UTC (permalink / raw)
  To: tarantool-patches

Hello.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
> Issue: https://github.com/tarantool/tarantool/issues/3272

1. Please, describe the patchset here.

> 
> Kirill Shcherbatov (7):
>    sql: fix code style in sqlite3Pragma
>    sql: remove zName and nColumn from SQL
>    sql: start using type from space_def
>    sql: start using collations and is_nullable from space_def
>    sql: move names to server
>    sql: start using is_view field from space_def
>    sql: space_def* instead of Table* in Expr
> 
>   src/box/field_def.c     |   1 +
>   src/box/field_def.h     |   5 +
>   src/box/space_def.c     |  29 ++--
>   src/box/sql.c           | 156 +++++++++++++++++----
>   src/box/sql.h           |  32 +++++
>   src/box/sql/alter.c     |  54 ++++---
>   src/box/sql/analyze.c   |  16 ++-
>   src/box/sql/build.c     | 364 ++++++++++++++++++++++++++----------------------
>   src/box/sql/delete.c    |  25 ++--
>   src/box/sql/expr.c      |  65 +++++----
>   src/box/sql/fkey.c      |  51 ++++---
>   src/box/sql/hash.c      |   5 +-
>   src/box/sql/insert.c    |  88 ++++++------
>   src/box/sql/pragma.c    |  96 +++++++------
>   src/box/sql/prepare.c   |  26 ++--
>   src/box/sql/resolve.c   |  33 +++--
>   src/box/sql/select.c    | 185 +++++++++++++-----------
>   src/box/sql/sqliteInt.h |  37 ++---
>   src/box/sql/tokenize.c  |   3 +
>   src/box/sql/treeview.c  |   2 +-
>   src/box/sql/trigger.c   |   6 +-
>   src/box/sql/update.c    |  52 +++----
>   src/box/sql/util.c      |   9 --
>   src/box/sql/vdbe.c      |   2 +-
>   src/box/sql/vdbeaux.c   |  19 ++-
>   src/box/sql/where.c     |  24 ++--
>   src/box/sql/wherecode.c |  23 +--
>   src/box/sql/whereexpr.c |   8 +-
>   28 files changed, 831 insertions(+), 585 deletions(-)
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 1/7] sql: fix code style in sqlite3Pragma
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-05-03 10:10     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 10:10 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Please, fix comments from the previous review.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> ---
>   src/box/sql/pragma.c | 56 +++++++++++++++++++++++++---------------------------
>   1 file changed, 27 insertions(+), 29 deletions(-)
> 
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index e41f69b..4a68cad 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -544,37 +544,35 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>   
>   #ifndef SQLITE_OMIT_FOREIGN_KEY
>   	case PragTyp_FOREIGN_KEY_LIST:{
> -			if (zRight) {
> -				FKey *pFK;
> -				Table *pTab;
> -				pTab = sqlite3HashFind(&db->pSchema->tblHash,
> -						       zRight);
> -				if (pTab != NULL) {
> -					pFK = pTab->pFKey;
> -					if (pFK) {
> -						int i = 0;
> -						pParse->nMem = 8;
> -						while (pFK) {
> -							int j;
> -							for (j = 0;
> -							     j < pFK->nCol;
> -							     j++) {
> -								sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j, pFK->zTo, pTab->aCol[pFK->aCol[j].iFrom].zName, pFK->aCol[j].zCol, actionName(pFK->aAction[1]),	/* ON UPDATE */
> -										     actionName(pFK->aAction[0]),	/* ON DELETE */
> -										     "NONE");
> -								sqlite3VdbeAddOp2
> -								    (v,
> -								     OP_ResultRow,
> -								     1, 8);
> -							}
> -							++i;
> -							pFK = pFK->pNextFrom;
> -						}
> -					}
> -				}
> -			}
> +		if (!zRight)
> +			break;
> +		FKey *pFK;
> +		Table *pTab;
> +		pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
> +		if (pTab == NULL)
>   			break;
> +		pFK = pTab->pFKey;
> +		if (!pFK)
> +			break;
> +		int i = 0;
> +		pParse->nMem = 8;
> +		while (pFK) {
> +			for (int j = 0; j < pFK->nCol; j++) {
> +				const char *name =
> +					pTab->aCol[pFK->aCol[j].iFrom].zName;
> +				sqlite3VdbeMultiLoad(v, 1, "iissssss", i, j,
> +						     pFK->zTo, name,
> +						     pFK->aCol[j].zCol,
> +						     actionName(pFK->aAction[1]),
> +						     actionName(pFK->aAction[0]),
> +						     "NONE");
> +				sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
> +			}
> +			++i;
> +			pFK = pFK->pNextFrom;
>   		}
> +		break;
> +	}
>   #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
>   
>   #ifndef SQLITE_OMIT_FOREIGN_KEY
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 4/7] sql: start using collations and is_nullable from space_def
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 4/7] sql: start using collations and is_nullable " Kirill Shcherbatov
@ 2018-05-03 10:21     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 10:21 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. See 5 comments below.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> Part of #3272.
> ---
>   src/box/sql.c           | 36 +++++++++++++++++++++++++-----------
>   src/box/sql/alter.c     | 11 ++++++++---
>   src/box/sql/build.c     | 20 +++++++++++++-------
>   src/box/sql/fkey.c      |  7 ++-----
>   src/box/sql/select.c    |  6 ++++--
>   src/box/sql/sqliteInt.h |  7 -------
>   src/box/sql/vdbeaux.c   |  5 ++++-
>   7 files changed, 56 insertions(+), 36 deletions(-)
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 2893d70..ef11eb9 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1449,7 +1449,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>   
>   	for (i = 0; i < n; i++) {
>   		const char *t;
> -		struct coll *coll = aCol[i].coll;
> +		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;
> @@ -1461,19 +1463,25 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>   		p = enc->encode_str(p, "name", 4);
>   		p = enc->encode_str(p, field->name, strlen(field->name));
>   		p = enc->encode_str(p, "type", 4);
> +
> +		assert(def->fields[i].is_nullable ==
> +		       (def->fields[i].nullable_action ==
> +			ON_CONFLICT_ACTION_NONE));

1. AFAIR I asked you to wrap this check into a function.
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index bedf602..f830a15 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -307,9 +312,9 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>   	}
>   	memcpy(pNew->aCol, pTab->aCol, sizeof(Column) * pNew->def->field_count);
>   	for (i = 0; i < pNew->def->field_count; i++) {
> -		Column *pCol = &pNew->aCol[i];
> +		/* FIXME: Column *pCol = &pNew->aCol[i]; */

2. Why FIXME ?
> diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
> index c15ad8c..8c015c9 100644
> --- a/src/box/sql/fkey.c
> +++ b/src/box/sql/fkey.c
> @@ -34,6 +34,7 @@
>    * support to compiled SQL statements.
>    */
>   #include <box/coll.h>
> +#include "box/coll_cache.h"

3. Why? It is not used here.
> @@ -535,11 +536,7 @@ 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";
> +			const char *coll_name = "binary";
>   			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
>   							    coll_name);

4. Why you did not just inline "binary" string ?
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 03bfcf9..fa1de9b 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1836,6 +1836,8 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   	pTable->def->fields =
>   		region_alloc(region, nCol*sizeof(pTable->def->fields[0]));
>   	memset(pTable->def->fields, 0, nCol*sizeof(pTable->def->fields[0]));
> +	for (int i = 0; i < nCol; i++)
> +		pTable->def->fields[i].is_nullable = true;

5. Why?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 5/7] sql: move names to server
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 5/7] sql: move names to server Kirill Shcherbatov
@ 2018-05-03 11:08     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 11:08 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

"sql: move names to server" - which names? whose names?

I see, that your code starts to use space name from space_def instead of name from
struct Table - it is not obvious from the commit header. And it is not "move" -
these names are in the server already. You just start to use them.

See below 27 comments.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> Part of #3272.
> ---
>   src/box/sql.c           | 38 ++++++++++++++++++++++-------
>   src/box/sql.h           |  6 ++---
>   src/box/sql/alter.c     | 11 +++++----
>   src/box/sql/analyze.c   | 11 +++++----
>   src/box/sql/build.c     | 61 ++++++++++++++++++++++++++++++-----------------
>   src/box/sql/delete.c    |  6 ++---
>   src/box/sql/fkey.c      | 11 +++++----
>   src/box/sql/hash.c      |  5 ++--
>   src/box/sql/insert.c    |  9 +++----
>   src/box/sql/pragma.c    |  4 ++--
>   src/box/sql/resolve.c   |  7 +++---
>   src/box/sql/select.c    | 63 ++++++++++++++++++++++++-------------------------
>   src/box/sql/sqliteInt.h |  1 -
>   src/box/sql/treeview.c  |  2 +-
>   src/box/sql/trigger.c   |  4 ++--
>   src/box/sql/update.c    |  3 ++-
>   src/box/sql/vdbe.c      |  2 +-
>   src/box/sql/where.c     |  5 ++--
>   src/box/sql/wherecode.c |  3 ++-
>   src/box/sql/whereexpr.c |  2 +-
>   20 files changed, 150 insertions(+), 104 deletions(-)
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index ef11eb9..47f7cb1 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1699,12 +1699,13 @@ 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)
> +static 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 size = sizeof(struct space_def) + 1;
> +	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) {
>   		memset(def, 0, size);
> @@ -1718,19 +1719,40 @@ sql_ephemeral_space_def_new(Parse *parser)
>   		parser->nErr++;
>   		return NULL;
>   	}
> +	memcpy(def->name, name, name_len);
> +	def->name[name_len] = '\0';
>   	def->dict->refs = 1;
>   	def->opts.temporary = true;
>   	return def;
>   }
>   
> +struct space_def *
> +sql_ephemeral_space_def_clone(Parse *parser, struct space_def *old_def)
> +{
> +	struct space_def *new_def = NULL;
> +	new_def = sql_ephemeral_space_def_new(parser, old_def->name);
> +	if (new_def == NULL) {
> +		parser->rc = SQLITE_NOMEM_BKPT;
> +		parser->nErr++;
> +		return NULL;
> +	}
> +	new_def->opts = old_def->opts;
> +	new_def->opts.temporary = true;
> +	new_def->id = old_def->id;
> +	new_def->uid = old_def->uid;
> +	memcpy(new_def->engine_name, old_def->engine_name,
> +	       strlen(old_def->engine_name));

1. I see, that this code can be much simpler and shorter, if you will do just
one memcpy(new_def, old_def, size). And now it becomes at least third place,
where size of a space_def is needed. Lets declare space_def_sizeof function from
space_def.c in space_def.h, make it non-static, and use here and in
sql_ephemeral_space_def_new.
> diff --git a/src/box/sql.h b/src/box/sql.h
> index 410653b..d7cfd70 100644
> --- a/src/box/sql.h
> +++ b/src/box/sql.h
> @@ -152,16 +152,16 @@ sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
>    * @retval not NULL on success.
>    */
>   struct Table *
> -sql_ephemeral_table_new(struct Parse *parser);
> +sql_ephemeral_table_new(struct Parse *parser, const char *name);

2. Fix the comment as well.

>   
>   /**
> - * Create and initialize a new ephemeric space_def object.
> + * Create and initialize a new ephemeric space_def object copy.
>    * @param pParse SQL Parser object.
>    * @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);
> +sql_ephemeral_space_def_clone(struct Parse *parser, struct space_def *old_def);

3. Same. And write a new comment for sql_ephemeral_space_def_new.
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index f830a15..33a4f4d 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -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));

4. Wrong alignment.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index e9c0686..4e0ae87 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -662,7 +660,8 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>   	assert(p->def->opts.temporary == true);
>   #if SQLITE_MAX_COLUMN
>   	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> -		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
> +		sqlite3ErrorMsg(pParse, "too many columns on %s",
> +				p->def->name);

5. Leading white space.
> @@ -1306,7 +1305,7 @@ createTableStmt(sqlite3 * db, Table * p)
>   	}
>   	sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
>   	k = sqlite3Strlen30(zStmt);
> -	identPut(zStmt, &k, p->zName);
> +	identPut(zStmt, &k, (char *)p->def->name);

6. Please, make identPut second argument be const char * to avoid this
conversion.

> @@ -1710,7 +1709,8 @@ parseTableSchemaRecord(Parse * pParse, int iSpaceId, char *zStmt)
>   
>   	sqlite3VdbeAddOp4(v,
>   			  OP_String8, 0, iTop, 0,
> -			  sqlite3DbStrDup(pParse->db, p->zName), P4_DYNAMIC);
> +			  sqlite3DbStrDup(pParse->db, p->def->name),
> +					  P4_DYNAMIC);

7. Wrong alignment.
> @@ -1950,7 +1950,7 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
>   
>   			reg_seq_record = emitNewSysSequenceRecord(pParse,
>   								  reg_seq_id,
> -								  p->zName);
> +								  	p->def->name);

8. Same.
> @@ -2135,6 +2135,11 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * a VIEW it holds the list of column names.
>   			 */
>   			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
> +			struct space_def *old_def = pTable->def;
> +			old_def->opts.temporary = true; /* delete it manually */

9. Comment is out of 66 symbols. Start a comment from a capital letter. Put a
dot at the end of sentence.
> @@ -2142,18 +2147,30 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   								       pSel);
>   			}
>   		} else if (pSelTab) {
> -			assert(pTable->def->opts.temporary == false);
>   			/* CREATE VIEW name AS...  without an argument list.  Construct
>   			 * the column names from the SELECT statement that defines the view.
>   			 */
>   			assert(pTable->aCol == 0);
>   			assert((int)pTable->def->field_count == -1);
> -			struct space_def *def = pSelTab->def;
> -			pSelTab->def = pTable->def;
> -			pSelTab->def->field_count = 0;
> -			pTable->def = def;
> +			assert(pSelTab->def->opts.temporary);
> +
> +			struct space_def *old_def = pTable->def;
> +			struct space_def *new_def =
> +				sql_ephemeral_space_def_clone(pParse, old_def);
> +			if (new_def == NULL) {
> +				nErr++;
> +			} else {
> +				new_def->fields = pSelTab->def->fields;
> +				new_def->field_count = pSelTab->def->field_count;

10. Out of 80 symbols.

> +				pTable->def = new_def;
> +				if (sql_table_def_rebuild(db, pTable) != 0)

11. Why you can not instead of clone() + rebuild() just do one space_def_new() ?
Or even space_def_dup, if old_def is not ephemeral.
> diff --git a/src/box/sql/hash.c b/src/box/sql/hash.c
> index cedcb7d..79f0840 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((void *)elem->pKey);

12. Unnecessary cast.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 06635ee..bde0cc1 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -1145,7 +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,
> +				    sqlite3MPrintf(db, "%s.%s",

13. Leading white space.
> @@ -1404,7 +1405,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>   					x = pPk->aiColumn[i];
>   					sqlite3VdbeAddOp3(v, OP_Column,
>   							  iThisCur, x, regR + i);
> -					VdbeComment((v, "%s.%s", pTab->zName,
> +					VdbeComment((v, "%s.%s", pTab->def->name,
>   						pTab->def->fields[
>   							pPk->aiColumn[i]].name));

14. Wrong alignment.
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index 109c410..5d85ef7 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -239,7 +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 != 0 &&
> +					pTab->def->name != NULL);

15. Leading white space.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index fa1de9b..199caf0 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1765,7 +1765,8 @@ generateColumnNames(Parse * pParse,	/* Parser context */
>   			} else if (fullNames) {
>   				char *zName = 0;
>   				zName =
> -				    sqlite3MPrintf(db, "%s.%s", pTab->zName,
> +				    sqlite3MPrintf(db, "%s.%s",

16. Same.
> @@ -1972,6 +1962,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 is allocated on region.

17. Return'ed'. No 'is'. And please, use doxygen style: @param, @retval etc.
> @@ -4691,12 +4681,14 @@ selectExpander(Walker * pWalker, Select * p)
>   	for (i = 0, pFrom = pTabList->a; i < pTabList->nSrc; i++, pFrom++) {
>   		Table *pTab;
>   		assert(pFrom->fg.isRecursive == 0 || pFrom->pTab != 0);
> -		if (pFrom->fg.isRecursive)
> +		if (pFrom->fg.isRecursive) {
>   			continue;
> +		}

18. Unnecessary diff.

>   		assert(pFrom->pTab == 0);
>   #ifndef SQLITE_OMIT_CTE
> -		if (withExpand(pWalker, pFrom))
> +		if (withExpand(pWalker, pFrom)) {
>   			return WRC_Abort;
> +		}

19. Same.
> @@ -4707,17 +4699,24 @@ selectExpander(Walker * pWalker, Select * p)
>   			assert(pFrom->pTab == 0);
>   			if (sqlite3WalkSelect(pWalker, pSel))
>   				return WRC_Abort;
> +			char *name = "sqlite_sq_DEADBEAFDEADBEAF";

20. Write a comment why it is needed.

>   			pFrom->pTab = pTab =
> -				sql_ephemeral_table_new(pParse);
> +				sql_ephemeral_table_new(pParse, name);
>   			if (pTab == NULL)
>   				return WRC_Abort;
> +			/* rewrite old name with correct pointer */
> +			name = sqlite3MPrintf(db, "sqlite_sq_%p", (void *)pTab);
> +			sprintf(pTab->def->name, "%s", name);
> +			sqlite3DbFree(db, name);

21. Use tt_sprintf instead of sqlite3MPrintf + sqlite3DbFree. And you did not check
if mprintf returned NULL.

> +
>   			pTab->nTabRef = 1;
> -			pTab->zName =
> -			    sqlite3MPrintf(db, "sqlite_sq_%p", (void *)pTab);
>   			while (pSel->pPrior) {
>   				pSel = pSel->pPrior;
>   			}
>   			sqlite3ColumnsFromExprList(pParse, pSel->pEList, pTab);
> +			if (sql_table_def_rebuild(db, pTab) != 0)
> +				return WRC_Abort;

22. Why do you need rebuild? AFAIK ephemeral struct Table dies together with
the parser.
> @@ -4727,12 +4726,13 @@ selectExpander(Walker * pWalker, Select * p)
>   			assert(pFrom->pTab == 0);
>   			pFrom->pTab = pTab =
>   			    sqlite3LocateTable(pParse, 0, pFrom->zName);
> -			if (pTab == NULL)
> +			if (pTab == NULL) {
>   				return WRC_Abort;
> +			}

23. Same as 18.
> @@ -5390,14 +5390,13 @@ sqlite3Select(Parse * pParse,		/* The parser context */
>   		Table *pTab = pItem->pTab;
>   		if (pSub == 0)
>   			continue;
> -

24. Same.

> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 464feee..ad00537 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -75,7 +75,8 @@ sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
>   	if (!pTab->pSelect) {
>   		sqlite3_value *pValue = 0;
>   		Column *pCol = &pTab->aCol[i];
> -		VdbeComment((v, "%s.%s", pTab->zName, pTab->def->fields[i].name));
> +		VdbeComment((v, "%s.%s", pTab->def->name,
> +			pTab->def->fields[i].name));

25. Same as 14.

> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> index fc0f84c..88f4c28 100644
> --- a/src/box/sql/where.c
> +++ b/src/box/sql/where.c
> @@ -1639,7 +1639,8 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC)
>   	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);
> +			   pItem->zAlias ? pItem->zAlias :

26. Same as 15.
> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
> index 233fde0..3da7cdb 100644
> --- a/src/box/sql/wherecode.c
> +++ b/src/box/sql/wherecode.c
> @@ -1158,7 +1158,8 @@ 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\"",

27. Same.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 6/7] sql: start using is_view field from space_def
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 6/7] sql: start using is_view field from space_def Kirill Shcherbatov
@ 2018-05-03 11:16     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 11:16 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

"start using is_view field from space_def" instead of what? It is
not obvious that the previous flag was Table->pSelect != NULL.

See 3 comments below.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> Part of #3272.
> ---
>   src/box/sql.c        |  2 ++
>   src/box/sql/build.c  | 24 +++++++++++++++++-------
>   src/box/sql/delete.c |  9 ++++++---
>   src/box/sql/expr.c   |  3 ++-
>   src/box/sql/select.c |  2 ++
>   src/box/sql/update.c |  3 ++-
>   6 files changed, 31 insertions(+), 12 deletions(-)
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 47f7cb1..9f5a124 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1511,6 +1511,8 @@ int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
>   	bool is_view = false;
>   	if (pTable != NULL)
>   		is_view = pTable->pSelect != NULL;
> +	assert(!pTable || pTable->def->opts.is_view == is_view);

1. You said, that you start using is_view, but here you continue to
use pSelect. Why? 🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔 Lets move pSelect into
assert, and use is_view from table space_def. And how pTable can be NULL here?

2. Remove (void)pTable;. The pTable argument is not unused now.

> @@ -1933,7 +1937,8 @@ 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);
>   		}

3. Unnecessary {}.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [tarantool-patches] Re: [PATCH v4 7/7] sql: space_def* instead of Table* in Expr
  2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 7/7] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-03 11:32     ` Vladislav Shpilevoy
  0 siblings, 0 replies; 26+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-03 11:32 UTC (permalink / raw)
  To: tarantool-patches, Kirill Shcherbatov

Hello. Write here why it is needed, and what this patch
allows to do in the next step.

See 10 comments below.

On 28/04/2018 21:26, Kirill Shcherbatov wrote:
> Part of #3272.
> ---
>   src/box/field_def.c     |  1 +
>   src/box/field_def.h     |  5 ++++
>   src/box/sql.c           | 10 ++++----
>   src/box/sql/build.c     | 48 ++++++++++++++++++++------------------
>   src/box/sql/delete.c    |  4 ++--
>   src/box/sql/expr.c      | 61 ++++++++++++++++++++++++++-----------------------
>   src/box/sql/fkey.c      | 13 +++++------
>   src/box/sql/insert.c    | 24 ++++++++++---------
>   src/box/sql/pragma.c    |  8 ++++---
>   src/box/sql/resolve.c   | 10 ++++----
>   src/box/sql/select.c    | 26 +++++++++++++--------
>   src/box/sql/sqliteInt.h | 21 +++++++++--------
>   src/box/sql/update.c    | 29 +++++++++++------------
>   src/box/sql/vdbeaux.c   | 20 ++++++----------
>   src/box/sql/where.c     | 13 +++++++----
>   src/box/sql/wherecode.c | 18 +++++++++------
>   src/box/sql/whereexpr.c |  2 +-
>   17 files changed, 167 insertions(+), 146 deletions(-)
> 
> diff --git a/src/box/field_def.c b/src/box/field_def.c
> index 010b3b7..63aab46 100644
> --- a/src/box/field_def.c
> +++ b/src/box/field_def.c
> @@ -100,6 +100,7 @@ const struct opt_def field_def_reg[] = {
>   
>   const struct field_def field_def_default = {
>   	.type = FIELD_TYPE_ANY,
> +	.affinity = 0,

1. Use enum for affinities.
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 119940c..4e20098 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -179,13 +181,13 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_found)
>   		}
>   		if ((op == TK_AGG_COLUMN || op == TK_COLUMN ||
>   		     op == TK_REGISTER || op == TK_TRIGGER) &&
> -		    p->pTab != 0) {
> +		    p->space_def != 0) {
>   			/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
>   			 * a TK_COLUMN but was previously evaluated and cached in a register
>   			 */
>   			int j = p->iColumn;
>   			if (j >= 0) {
> -				coll = sql_column_collation(p->pTab, j);
> +				coll = sql_column_collation(p->space_def, j);
>   				*is_found = true;
>   			}
>   			break;
> @@ -2132,10 +2134,10 @@ sqlite3ExprCanBeNull(const Expr * p)
>   	case TK_BLOB:
>   		return 0;
>   	case TK_COLUMN:
> -		assert(p->pTab != 0);
> +		assert(p->space_def!= 0);

2. Use explicit != NULL, and put white space around binary operators.
> @@ -3529,7 +3534,7 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse,	/* The parsing context */
>    */
>   void
>   sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
> -				Table * pTab,	/* The table containing the value */
> +				struct space_def *space_def,

3. Please, convert the function into Tarantool code style, and
write a comment. Same about all functions, whose signature is changed.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index bde0cc1..7c21359 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -1805,17 +1807,17 @@ xferOptimization(Parse * pParse,	/* Parser context */
>   		return 0;	/* Both tables must have the same INTEGER PRIMARY KEY */
>   	}
>   	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) {
> +		char pdest_affinity = pDest->def->fields[i].affinity;
> +		char psrc_affinity = pSrc->def->fields[i].affinity;
> +		if (pdest_affinity != psrc_affinity) {
>   			return 0;	/* Affinity must be the same on all columns */
>   		}

4. Unnecessary {}.

> -		if (sql_column_collation(pDest, i) !=
> -		    sql_column_collation(pSrc, i)) {
> +		if (sql_column_collation(pDest->def, i) !=
> +		    sql_column_collation(pSrc->def, i)) {
>   			return 0;	/* Collating sequence must be the same on all columns */
>   		}

5. Same.

> -		if (!table_column_is_nullable(pDest, i)
> -		    && table_column_is_nullable(pSrc, i)) {
> +		if (!table_column_is_nullable(pDest->def, i)
> +		    && table_column_is_nullable(pSrc->def, i)) {
>   			return 0;	/* tab2 must be NOT NULL if tab1 is */
>   		}

6. Same.
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 463bb7e..c49817f 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -373,7 +373,9 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>   						     i; k++) {
>   						}
>   					}
> -					bool nullable = table_column_is_nullable(pTab, i);
> +					bool nullable =
> +						table_column_is_nullable(pTab->def,
> +									 i);

7. Now this function can not be names table_column.... It does not take
table as an argument. Please, rename it to space_def.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 34d296d..ff9f18b 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1636,7 +1636,7 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
>   				break;
>   			}
>   
> -			assert(pTab && pExpr->pTab == pTab);
> +			assert(pTab && pExpr->space_def == pTab->def);

8. Please, use explicit != NULL.
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 2f36423..38f824f 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -69,30 +69,27 @@
>    * space.
>    */
>   void
> -sqlite3ColumnDefault(Vdbe * v, Table * pTab, int i, int iReg)
> +sqlite3ColumnDefault(Vdbe * v, struct space_def * def, int i, int iReg)
>   {
> -	assert(pTab != 0);
> -	assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
> -	if (!pTab->def->opts.is_view) {
> +	assert(def != NULL);
> +	if (!def->opts.is_view) {
>   		sqlite3_value *pValue = 0;
> -		Column *pCol = &pTab->aCol[i];
> -		VdbeComment((v, "%s.%s", pTab->def->name,
> -			pTab->def->fields[i].name));
> -		assert(i < (int)pTab->def->field_count);
> +		char affinity = def->fields[i].affinity;
> +		VdbeComment((v, "%s.%s", def->name, def->fields[i].name));
> +		assert(i < (int)def->field_count);
>   
>   		Expr *expr = NULL;
>   		struct space *space =
> -			space_cache_find(SQLITE_PAGENO_TO_SPACEID(pTab->tnum));
> +			space_cache_find(def->id);

9. Why do you still need space_cache_find, if you already have
space_def and default_value_expr in it?
> diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
> index f76c689..f9927ed 100644
> --- a/src/box/sql/vdbeaux.c
> +++ b/src/box/sql/vdbeaux.c
> @@ -4724,28 +4724,22 @@ table_column_nullable_action(struct Table *tab, uint32_t column)
>    * @return return nullability flag value
>    */
>   bool
> -table_column_is_nullable(struct Table *tab, uint32_t column)
> +table_column_is_nullable(struct space_def *def, uint32_t column)
>   {
>   	/* Temporary hack: until Tarantoool's ephemeral spaces are on-boarded,
>   	*  views are not handled properly in Tarantool as well. */
> -	if (!(tab->tabFlags | TF_Ephemeral || space_is_view(tab))) {
> -		uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(tab->tnum);
> -		struct space *space = space_cache_find(space_id);
> -
> -		assert(space);
> -
> +	struct space *space = space_cache_find(def->id);

10. You do not need space_cache_find. Space_def is enough here, it has
nullable action too.

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2018-05-03 11:38 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-25 16:52 [tarantool-patches] [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Kirill Shcherbatov
2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 1/4] sql: Fix code style in sqlite3Pragma Kirill Shcherbatov
2018-04-26 11:47   ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 2/4] sql: Remove zName and nColumn from SQL Kirill Shcherbatov
2018-04-25 17:10   ` [tarantool-patches] " Kirill Shcherbatov
2018-04-26 12:12     ` Vladislav Shpilevoy
2018-04-26 11:47   ` Vladislav Shpilevoy
2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 3/4] sql: Removed type " Kirill Shcherbatov
2018-04-25 16:52 ` [tarantool-patches] [PATCH v3 4/4] sql: Region-based allocations Kirill Shcherbatov
2018-04-26 11:47   ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-26 11:47 ` [tarantool-patches] Re: [PATCH v3 0/4] sql: Removed Column fields to server with region allocations Vladislav Shpilevoy
2018-04-28 18:26 ` [tarantool-patches] [PATCH v4 0/7] sql: refactor SQL Parser structures Kirill Shcherbatov
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 1/7] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
2018-05-03 10:10     ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 2/7] sql: remove zName and nColumn from SQL Kirill Shcherbatov
2018-05-03 10:10     ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 3/7] sql: start using type from space_def Kirill Shcherbatov
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 4/7] sql: start using collations and is_nullable " Kirill Shcherbatov
2018-05-03 10:21     ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 5/7] sql: move names to server Kirill Shcherbatov
2018-05-03 11:08     ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 6/7] sql: start using is_view field from space_def Kirill Shcherbatov
2018-05-03 11:16     ` [tarantool-patches] " Vladislav Shpilevoy
2018-04-28 18:26   ` [tarantool-patches] [PATCH v4 7/7] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
2018-05-03 11:32     ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-03 10:10   ` [tarantool-patches] Re: [PATCH v4 0/7] sql: refactor SQL Parser structures Vladislav Shpilevoy

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