Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures
@ 2018-05-11  8:49 Kirill Shcherbatov
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-11  8:49 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 (3):
  sql: fix code style in sqlite3Pragma
  sql: remove SQL fields from Table and Column
  sql: space_def* instead of Table* in Expr

 src/box/field_def.c     |   1 +
 src/box/field_def.h     |  20 +++
 src/box/space_def.c     |  29 ++--
 src/box/sql.c           | 134 +++++++++++++----
 src/box/sql.h           |  33 +++++
 src/box/sql/alter.c     |  55 ++++---
 src/box/sql/analyze.c   |  16 +-
 src/box/sql/build.c     | 378 ++++++++++++++++++++++++++----------------------
 src/box/sql/delete.c    |  25 ++--
 src/box/sql/expr.c      | 119 ++++++++-------
 src/box/sql/fkey.c      |  51 +++----
 src/box/sql/hash.c      |   5 +-
 src/box/sql/hash.h      |   2 +-
 src/box/sql/insert.c    | 106 ++++++++------
 src/box/sql/pragma.c    |  96 ++++++------
 src/box/sql/prepare.c   |  45 +++---
 src/box/sql/resolve.c   |  32 ++--
 src/box/sql/select.c    | 183 +++++++++++++----------
 src/box/sql/sqliteInt.h |  78 +++++-----
 src/box/sql/tokenize.c  |   5 +-
 src/box/sql/treeview.c  |   2 +-
 src/box/sql/trigger.c   |   7 +-
 src/box/sql/update.c    |  58 ++++----
 src/box/sql/util.c      |   9 --
 src/box/sql/vdbe.c      |   2 +-
 src/box/sql/vdbeaux.c   |  25 +---
 src/box/sql/where.c     |  25 ++--
 src/box/sql/wherecode.c |  23 +--
 src/box/sql/whereexpr.c |   8 +-
 29 files changed, 899 insertions(+), 673 deletions(-)

-- 
2.7.4

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

* [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma
  2018-05-11  8:49 [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures Kirill Shcherbatov
@ 2018-05-11  8:49 ` Kirill Shcherbatov
  2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column Kirill Shcherbatov
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-11  8:49 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..b9038b7 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 == NULL)
+			break;
+		FKey *pFK;
+		Table *pTab;
+		pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
+		if (pTab == NULL)
 			break;
+		pFK = pTab->pFKey;
+		if (pFK == NULL)
+			break;
+		int i = 0;
+		pParse->nMem = 8;
+		while (pFK != NULL) {
+			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] 13+ messages in thread

* [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column
  2018-05-11  8:49 [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures Kirill Shcherbatov
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-05-11  8:49 ` Kirill Shcherbatov
  2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 3/3] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
  2018-05-11  8:58 ` [tarantool-patches] Re: [PATCH v5 0/3] sql: refactor SQL Parser structures Vladislav Shpilevoy
  3 siblings, 1 reply; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-11  8:49 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

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

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

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

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

* [tarantool-patches] [PATCH v5 3/3] sql: space_def* instead of Table* in Expr
  2018-05-11  8:49 [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures Kirill Shcherbatov
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column Kirill Shcherbatov
@ 2018-05-11  8:49 ` Kirill Shcherbatov
  2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-05-11  8:58 ` [tarantool-patches] Re: [PATCH v5 0/3] sql: refactor SQL Parser structures Vladislav Shpilevoy
  3 siblings, 1 reply; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-11  8:49 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

This patch allows to remove Checks from SQL to
server as sqlite3ResolveSelfReference requires
Expr structure pointer.

Part of #3272.
---
 src/box/field_def.c     |   1 +
 src/box/field_def.h     |  14 ++++++
 src/box/sql.c           |  10 ++---
 src/box/sql/build.c     |  48 +++++++++++---------
 src/box/sql/delete.c    |   4 +-
 src/box/sql/expr.c      | 115 +++++++++++++++++++++++++++---------------------
 src/box/sql/fkey.c      |  13 +++---
 src/box/sql/insert.c    |  39 +++++++++-------
 src/box/sql/pragma.c    |   8 ++--
 src/box/sql/resolve.c   |  10 ++---
 src/box/sql/select.c    |  26 ++++++-----
 src/box/sql/sqliteInt.h |  41 +++++------------
 src/box/sql/update.c    |  35 +++++++--------
 src/box/sql/vdbeaux.c   |  28 +++---------
 src/box/sql/where.c     |  13 +++---
 src/box/sql/wherecode.c |  19 +++++---
 src/box/sql/whereexpr.c |   2 +-
 17 files changed, 219 insertions(+), 207 deletions(-)

diff --git a/src/box/field_def.c b/src/box/field_def.c
index 010b3b7..cdae5bc 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 = SQLITE_AFF_UNDEFINED,
 	.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 a42beab..b210756 100644
--- a/src/box/field_def.h
+++ b/src/box/field_def.h
@@ -70,6 +70,15 @@ enum on_conflict_action {
 	on_conflict_action_MAX
 };
 
+enum affinity_type {
+	SQLITE_AFF_UNDEFINED = 0,
+	SQLITE_AFF_BLOB = 'A',
+	SQLITE_AFF_TEXT = 'B',
+	SQLITE_AFF_NUMERIC = 'C',
+	SQLITE_AFF_INTEGER = 'D',
+	SQLITE_AFF_REAL = 'E',
+};
+
 /** \endcond public */
 
 extern const char *field_type_strs[];
@@ -102,6 +111,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.
+	 */
+	enum affinity_type 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 7d48cdc..5e8c96d 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));
@@ -1529,7 +1529,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);
 
@@ -1566,7 +1565,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 a02fe89..f082d01 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -703,7 +703,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 {
@@ -714,14 +714,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;
 		}
@@ -1034,8 +1034,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);
+			}
 		}
 	}
 	sqlite3DbFree(db, zColl);
@@ -1049,10 +1051,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
@@ -1067,9 +1069,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;
 	}
 
@@ -1325,18 +1327,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);
@@ -1845,6 +1848,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) {
@@ -3098,7 +3102,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..8b34c57 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 != NULL) {
 			/* 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,11 @@ sqlite3ExprCanBeNull(const Expr * p)
 	case TK_BLOB:
 		return 0;
 	case TK_COLUMN:
-		assert(p->pTab != 0);
+		assert(p->space_def != NULL);
 		return ExprHasProperty(p, EP_CanBeNull) ||
 		       (p->iColumn >= 0
-		        && table_column_is_nullable(p->pTab, p->iColumn));
+		        && space_def_column_is_nullable(p->space_def,
+							p->iColumn));
 	default:
 		return 1;
 	}
@@ -2435,7 +2438,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 +3180,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,45 +3525,48 @@ 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);
 	}
 }
 
-/*
+/**
  * Generate code to extract the value of the iCol-th column of a table.
+ * @param v  The VDBE under construction.
+ * @param space_def Space definition.
+ * @param iTabCur The PK cursor.
+ * @param iCol Index of the column to extract.
+ * @param regOut  Extract the value into this register.
  */
 void
-sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
-				Table * pTab,	/* The table containing the value */
-				int iTabCur,	/* The PK cursor */
-				int iCol,	/* Index of the column to extract */
-				int regOut	/* Extract the value into this register */
-    )
+sqlite3ExprCodeGetColumnOfTable(Vdbe * v, struct space_def *space_def,
+				int iTabCur, int iCol, int regOut)
 {
 	sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut);
 	if (iCol >= 0) {
-		sqlite3ColumnDefault(v, pTab, iCol, regOut);
+		sqlite3ColumnDefault(v, space_def, iCol, regOut);
 	}
 }
 
-/*
+/**
  * Generate code that will extract the iColumn-th column from
  * table pTab and store the column value in a register.
  *
- * An effort is made to store the column value in register iReg.  This
- * is not garanteeed for GetColumn() - the result can be stored in
- * any register.  But the result is guaranteed to land in register iReg
- * for GetColumnToReg().
+ * An effort is made to store the column value in register iReg.
+ * This is not garanteeed for GetColumn() - the result can be
+ * stored in any register.  But the result is guaranteed to land
+ * in register iReg for GetColumnToReg().
+ * @param pParse Parsing and code generating context.
+ * @param space_def Space definition.
+ * @param iColumn Index of the table column.
+ * @param iTable The cursor pointing to the table.
+ * @param iReg Store results here.
+ * @param p5 P5 value for OP_Column + FLAGS.
+ * @return iReg value.
  */
 int
-sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context */
-			 Table * pTab,	/* Description of the table we are reading from */
-			 int iColumn,	/* Index of the table column */
-			 int iTable,	/* The cursor pointing to the table */
-			 int iReg,	/* Store results here */
-			 u8 p5	/* P5 value for OP_Column + FLAGS */
-    )
+sqlite3ExprCodeGetColumn(Parse * pParse, struct space_def * space_def,
+			 int iColumn, int iTable, int iReg, u8 p5)
 {
 	Vdbe *v = pParse->pVdbe;
 	int i;
@@ -3572,7 +3581,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 {
@@ -3581,16 +3590,22 @@ sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context
 	return iReg;
 }
 
+/**
+ * Generate code that will extract the iColumn-th column from
+ * table pTab and store the column value in a register, copy the
+ * result.
+ * @param pParse Parsing and code generating context.
+ * @param space_def Space definition.
+ * @param iColumn Index of the table column.
+ * @param iTable The cursor pointing to the table.
+ * @param iReg Store results here.
+ */
 void
-sqlite3ExprCodeGetColumnToReg(Parse * pParse,	/* Parsing and code generating context */
-			      Table * pTab,	/* Description of the table we are reading from */
-			      int iColumn,	/* Index of the table column */
-			      int iTable,	/* The cursor pointing to the table */
-			      int iReg	/* Store results here */
-    )
+sqlite3ExprCodeGetColumnToReg(Parse * pParse, struct space_def * space_def,
+			      int iColumn, int iTable, int iReg)
 {
 	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 +3792,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 +4256,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 +4280,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 +5453,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 916b346..f14a60d 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -298,7 +298,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);
@@ -526,15 +526,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;
 			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
 							    "binary");
 		} else {
@@ -551,14 +550,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;
 	}
@@ -671,7 +670,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 c272ae1..8ecfe10 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 (space_def_column_is_nullable(pTab->def, i)
 		    || (pTab->tabFlags & TF_Autoincrement
 			&& pTab->iAutoIncPKey == i))
 			continue;	/* This column is allowed to be NULL */
@@ -1796,19 +1798,22 @@ 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) {
-			return 0;	/* Affinity must be the same on all columns */
-		}
-		if (sql_column_collation(pDest, i) !=
-		    sql_column_collation(pSrc, 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)) {
-			return 0;	/* tab2 must be NOT NULL if tab1 is */
-		}
+		char pdest_affinity = pDest->def->fields[i].affinity;
+		char psrc_affinity = pSrc->def->fields[i].affinity;
+		/* Affinity must be the same on all columns. */
+		if (pdest_affinity != psrc_affinity)
+			return 0;
+		/*
+		 * Collating sequence must be the same on all
+		 * columns.
+		 */
+		if (sql_column_collation(pDest->def, i) !=
+		    sql_column_collation(pSrc->def, i))
+			return 0;
+		/* The tab2 must be NOT NULL if tab1 is */
+		if (!space_def_column_is_nullable(pDest->def, i)
+		    && space_def_column_is_nullable(pSrc->def, i))
+			return 0;
 		/* Default values for second and subsequent columns need to match. */
 		if (i > 0) {
 			uint32_t src_space_id =
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 250c402..0207ee8 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 =
+						space_def_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 f95ef27..35adb10 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 */
@@ -300,7 +300,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) {
@@ -364,7 +364,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 						     : (((u32) 1) << iCol));
 					}
 					pExpr->iColumn = (i16) iCol;
-					pExpr->pTab = pTab;
+					pExpr->space_def = pTab->def;
 					isTrigger = 1;
 				}
 			}
@@ -498,9 +498,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 32a8e08..5bd958a 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 != NULL && 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
@@ -1846,19 +1846,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;
@@ -1950,11 +1954,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)
@@ -5933,7 +5939,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 4fba008..ad7cc54 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 */
 };
@@ -1879,26 +1878,6 @@ struct Column {
 #define SQLITE_SO_DESC      1	/* Sort in ascending order */
 #define SQLITE_SO_UNDEFINED -1	/* No sort order specified */
 
-/*
- * Column affinity types.
- *
- * These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and
- * 't' for SQLITE_AFF_TEXT.  But we can save a little space and improve
- * the speed a little by numbering the values consecutively.
- *
- * But rather than start with 0 or 1, we begin with 'A'.  That way,
- * when multiple affinity types are concatenated into a string and
- * used as the P4 operand, they will be more readable.
- *
- * Note also that the numeric types are grouped together so that testing
- * for a numeric type is a single comparison.  And the BLOB type is first.
- */
-#define SQLITE_AFF_BLOB     'A'
-#define SQLITE_AFF_TEXT     'B'
-#define SQLITE_AFF_NUMERIC  'C'
-#define SQLITE_AFF_INTEGER  'D'
-#define SQLITE_AFF_REAL     'E'
-
 #define sqlite3IsNumericAffinity(X)  ((X)>=SQLITE_AFF_NUMERIC)
 
 /*
@@ -2226,7 +2205,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 +2341,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 +3501,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 +3588,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 +3783,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 +3871,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 *);
@@ -4140,7 +4121,7 @@ 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);
+space_def_column_is_nullable(struct space_def *def, uint32_t column);
 
 /**
  * Initialize a new parser object.
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 5f5807c..9ae77e0 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));
-		if (space != NULL && space->def->fields != NULL)
-			expr = space->def->fields[i].default_value_expr;
+		assert(def->fields != NULL && i < (int)def->field_count);
+		if (def->fields != NULL)
+			expr = 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 0ccca77..3054fc3 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4724,28 +4724,10 @@ 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)
+space_def_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 tuple_format *format = space->format;
-
-		assert(format);
-		assert(format->field_count > column);
-
-		return nullable_action_to_is_nullable(
-			format->fields[column].nullable_action);
-	} else {
-		/* tab is ephemeral (in SQLite sense).  */
-		assert(tab->def->fields[column].is_nullable ==
-		       nullable_action_to_is_nullable(
-			tab->def->fields[column].nullable_action));
-		return tab->def->fields[column].is_nullable;
-	}
+	assert(def->fields[column].is_nullable ==
+	       nullable_action_to_is_nullable(
+		       def->fields[column].nullable_action));
+	return def->fields[column].is_nullable;
 }
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 9ab6295..c878a97 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 !space_def_column_is_nullable(pIdx->pTable->def, j);
 	} else if (j == (-1)) {
 		return 1;
 	} else {
@@ -2236,7 +2238,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;
 
@@ -3330,8 +3333,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
 				if (isOrderDistinct
 				    && iColumn >= 0
 				    && j >= pLoop->nEq
-				    && table_column_is_nullable(pIndex->pTable,
-								iColumn)) {
+				    && space_def_column_is_nullable(
+					pIndex->pTable->def, iColumn)) {
 					isOrderDistinct = 0;
 				}
 
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 9d4055a..1263e05 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;
@@ -1260,8 +1260,10 @@ 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 &&
+				space_def_column_is_nullable(pIdx->pTable->def,
+							     j)) ||
+			    j == XN_EXPR) {
 				assert(pLoop->nSkip == 0);
 				bSeekPastNull = 1;
 				nExtraReg = 1;
@@ -1307,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) {
+				     && space_def_column_is_nullable(
+					pIdx->pTable->def, j)) ||
+				    j == XN_EXPR) {
 					bSeekPastNull = 1;
 				}
 			}
@@ -1386,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
@@ -1724,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] 13+ messages in thread

* [tarantool-patches] Re: [PATCH v5 0/3] sql: refactor SQL Parser structures
  2018-05-11  8:49 [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures Kirill Shcherbatov
                   ` (2 preceding siblings ...)
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 3/3] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-11  8:58 ` Vladislav Shpilevoy
  2018-05-11 19:40   ` [tarantool-patches] Re[2]: [tarantool-patches] " Kirill Shcherbatov
  3 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-11  8:58 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches



On 11/05/2018 11:49, Kirill Shcherbatov wrote:
> Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
> Issue: https://github.com/tarantool/tarantool/issues/3272

Hello. Describe the changes here, and why they are needed.

> 
> Kirill Shcherbatov (3):
>    sql: fix code style in sqlite3Pragma
>    sql: remove SQL fields from Table and Column
>    sql: space_def* instead of Table* in Expr
> 
>   src/box/field_def.c     |   1 +
>   src/box/field_def.h     |  20 +++
>   src/box/space_def.c     |  29 ++--
>   src/box/sql.c           | 134 +++++++++++++----
>   src/box/sql.h           |  33 +++++
>   src/box/sql/alter.c     |  55 ++++---
>   src/box/sql/analyze.c   |  16 +-
>   src/box/sql/build.c     | 378 ++++++++++++++++++++++++++----------------------
>   src/box/sql/delete.c    |  25 ++--
>   src/box/sql/expr.c      | 119 ++++++++-------
>   src/box/sql/fkey.c      |  51 +++----
>   src/box/sql/hash.c      |   5 +-
>   src/box/sql/hash.h      |   2 +-
>   src/box/sql/insert.c    | 106 ++++++++------
>   src/box/sql/pragma.c    |  96 ++++++------
>   src/box/sql/prepare.c   |  45 +++---
>   src/box/sql/resolve.c   |  32 ++--
>   src/box/sql/select.c    | 183 +++++++++++++----------
>   src/box/sql/sqliteInt.h |  78 +++++-----
>   src/box/sql/tokenize.c  |   5 +-
>   src/box/sql/treeview.c  |   2 +-
>   src/box/sql/trigger.c   |   7 +-
>   src/box/sql/update.c    |  58 ++++----
>   src/box/sql/util.c      |   9 --
>   src/box/sql/vdbe.c      |   2 +-
>   src/box/sql/vdbeaux.c   |  25 +---
>   src/box/sql/where.c     |  25 ++--
>   src/box/sql/wherecode.c |  23 +--
>   src/box/sql/whereexpr.c |   8 +-
>   29 files changed, 899 insertions(+), 673 deletions(-)
> 

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

* [tarantool-patches] Re[2]: [tarantool-patches] [PATCH v5 0/3] sql: refactor SQL Parser structures
  2018-05-11  8:58 ` [tarantool-patches] Re: [PATCH v5 0/3] sql: refactor SQL Parser structures Vladislav Shpilevoy
@ 2018-05-11 19:40   ` Kirill Shcherbatov
  0 siblings, 0 replies; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-11 19:40 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Nikita Pettik, Vladislav Shpilevoy

[-- Attachment #1: Type: text/plain, Size: 812 bytes --]


>On 11/05/2018 11:49, Kirill Shcherbatov wrote:
>> Branch:  http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-names
>> Issue:  https://github.com/tarantool/tarantool/issues/3272
>
>Hello. Describe the changes here, and why they are needed.

Refactored SQL Column and Table structures to use space_def instead of native fields.
All temporal memory is allocated on region to prevent memory leaks and easely manage memory
with frequent insertions on SQL expression parsing.
Introduced space_def pointer in Expr instead of Table pointer.
The ResolveSelfReference function requires Table pointer to refer to, but it doesn't exist in our scenarios yet.
With correctly-initialized consistent space_def structure this would no problem.
This preliminary tasks should make able to move SQL Checks in Server code.

[-- Attachment #2: Type: text/html, Size: 1447 bytes --]

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

* [tarantool-patches] Re: [PATCH v5 1/3] sql: fix code style in sqlite3Pragma
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 1/3] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-05-11 20:59   ` Vladislav Shpilevoy
  0 siblings, 0 replies; 13+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-11 20:59 UTC (permalink / raw)
  To: tarantool-patches

Hello. Thanks for the patch. I have pushed my review fixes on the branch.
Look at them, digest, and squash.

On 11/05/2018 11:49, 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..b9038b7 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 == NULL)
> +			break;
> +		FKey *pFK;
> +		Table *pTab;
> +		pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
> +		if (pTab == NULL)
>   			break;
> +		pFK = pTab->pFKey;
> +		if (pFK == NULL)
> +			break;
> +		int i = 0;
> +		pParse->nMem = 8;
> +		while (pFK != NULL) {
> +			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] 13+ messages in thread

* [tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 2/3] sql: remove SQL fields from Table and Column Kirill Shcherbatov
@ 2018-05-11 20:59   ` Vladislav Shpilevoy
  2018-05-14 11:20     ` Kirill Shcherbatov
  0 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-11 20:59 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Thanks for the patch! See below 43 comments.

On 11/05/2018 11:49, Kirill Shcherbatov wrote:
> 1. Removed zName, is_nullable, collation, type
> from SQL Column.
> 2. Removed zColumns, zName from SQL Table.
> 3. Refactored Parser to use def_expression directly.
> 4. Introduced is_view flag.
> 4. Introduced sql_table_def_rebuild intended for collect
> fragmented with sql_field_retrieve space_def into memory
> located in one allocation.
> 
> Part of #3272, #3218.
> ---
>   src/box/field_def.h     |   6 +
>   src/box/space_def.c     |  29 +++--
>   src/box/sql.c           | 128 +++++++++++++++----
>   src/box/sql.h           |  33 +++++
>   src/box/sql/alter.c     |  55 +++++---
>   src/box/sql/analyze.c   |  16 ++-
>   src/box/sql/build.c     | 334 ++++++++++++++++++++++++++----------------------
>   src/box/sql/delete.c    |  21 +--
>   src/box/sql/expr.c      |  14 +-
>   src/box/sql/fkey.c      |  38 +++---
>   src/box/sql/hash.c      |   5 +-
>   src/box/sql/hash.h      |   2 +-
>   src/box/sql/insert.c    |  67 +++++-----
>   src/box/sql/pragma.c    |  34 +++--
>   src/box/sql/prepare.c   |  45 +++----
>   src/box/sql/resolve.c   |  22 ++--
>   src/box/sql/select.c    | 159 +++++++++++++----------
>   src/box/sql/sqliteInt.h |  37 ++++--
>   src/box/sql/tokenize.c  |   5 +-
>   src/box/sql/treeview.c  |   2 +-
>   src/box/sql/trigger.c   |   7 +-
>   src/box/sql/update.c    |  33 ++---
>   src/box/sql/util.c      |   9 --
>   src/box/sql/vdbe.c      |   2 +-
>   src/box/sql/vdbeaux.c   |   9 +-
>   src/box/sql/where.c     |  12 +-
>   src/box/sql/wherecode.c |   4 +-
>   src/box/sql/whereexpr.c |   6 +-
>   28 files changed, 675 insertions(+), 459 deletions(-)
> 
> diff --git a/src/box/field_def.h b/src/box/field_def.h
> index dfc1950..a42beab 100644
> --- a/src/box/field_def.h
> +++ b/src/box/field_def.h
> @@ -116,6 +116,12 @@ struct field_def {
>   	struct Expr *default_value_expr;
>   };
>   
> +static inline bool
> +nullable_action_to_is_nullable(enum on_conflict_action nullable_action)
> +{
> +	return nullable_action == ON_CONFLICT_ACTION_NONE;
> +}

1. Lets remove 'to': nullable_action_is_nullable. It is too long too, but I
can not think up another.
> 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. Same as in the previous review: garbage diff.

> @@ -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.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 166bb71..7d48cdc 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1681,3 +1697,65 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
>   
>   	return space->def->fields[fieldno].default_value_expr;
>   }
> +
> +struct space_def *
> +sql_ephemeral_space_def_new(Parse *parser, const char *name)
> +{
> +	struct space_def *def = NULL;
> +	struct region *region = &fiber()->gc;
> +	size_t name_len = name != NULL ? strlen(name) : 0;
> +	size_t size = sizeof(struct space_def) + name_len + 1;

4. Use space_def_sizeof for this please.

> +	def = (struct space_def *)region_alloc(region, size);
> +	if (def == NULL) {
> +		diag_set(OutOfMemory, sizeof(struct tuple_dictionary),
> +			"region_alloc", "sql_ephemeral_space_def_new");
> +		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->nErr++;
> +		return NULL;
> +	} else {

5. If you return on def == NULL, then either move all the other code
under 'else' body, or remove the 'else'.

> +		memset(def, 0, size);
> +	}
> +	memcpy(def->name, name, name_len);
> +	def->name[name_len] = '\0';
> +	def->opts.temporary = true;
> +	return def;
> +}
> +
> diff --git a/src/box/sql.h b/src/box/sql.h
> index db92d80..ac8d07a 100644
> --- a/src/box/sql.h
> +++ b/src/box/sql.h
> @@ -65,6 +65,7 @@ sql_get();
>   struct Expr;
>   struct Parse;
>   struct Select;
> +struct Table;
>   
>   /**
>    * Perform parsing of provided expression. This is done by
> @@ -143,6 +144,38 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer);
>   void
>   sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
>   
> +/**
> + * Create and initialize a new ephemeric SQL Table object.

6. Same as in the previous review - ephemeric -> ephemeral.

> + * @param parser SQL Parser object.
> + * @param name Table to create name.
> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */
> +struct Table *
> +sql_ephemeral_table_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Create and initialize a new ephemeric space_def object.

7. Same.

> + * @param parser SQL Parser object.
> + * @param name Table to create name.
> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */
> +struct space_def *
> +sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Rebuild struct def in Table with memory allocated on a single
> + * malloc. Fields and strings are expected to be allocated with
> + * sqlite3DbMalloc.

8. In the function body: "/* All allocations are on region. */".
Please, fix the comment.
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index 24f0965..85404ac 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -195,7 +199,12 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
>   				"Cannot add a REFERENCES column with non-NULL default value");
>   		return;
>   	}
> -	if (pCol->notNull && !pDflt) {
> +	assert(pNew->def->fields[pNew->def->field_count - 1].is_nullable ==
> +	       nullable_action_to_is_nullable(
> +		pNew->def->fields[pNew->def->field_count - 1].nullable_action));
> +
> +	if (pNew->def->fields[pNew->def->field_count - 1].nullable_action

9. Please, do not use non-boolean variables as booleans.
Use (action != ON_CONFLICT_ACTION_NONE) instead of just (action).

> +	    && !pDflt) {

10. pDflt == NULL.
> @@ -281,25 +293,30 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>   	pNew = (Table *) sqlite3DbMallocZero(db, sizeof(Table));
>   	if (!pNew)
>   		goto exit_begin_add_column;
> +	pNew->def = space_def_dup(pTab->def);
> +	if (pNew->def == NULL) {

11. Forgot to set sqlite3OomFault(db). I know, that this code is
unreachable, but in the future it will be not.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index a2b712a..a02fe89 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -291,15 +291,8 @@ sqlite3CommitInternalChanges()
>   void
>   sqlite3DeleteColumnNames(sqlite3 * db, Table * pTable)
>   {
> -	int i;
> -	Column *pCol;
>   	assert(pTable != 0);
> -	if ((pCol = pTable->aCol) != 0) {
> -		for (i = 0; i < pTable->nCol; i++, pCol++) {
> -			sqlite3DbFree(db, pCol->zName);
> -		}
> -		sqlite3DbFree(db, pTable->aCol);
> -	}
> +	sqlite3DbFree(db, pTable->aCol);

12. Nice. Now this function consists of a single DbFree - lets just
inline it and remove sqlite3DeleteColumnNames.

> @@ -494,31 +484,19 @@ sqlite3PrimaryKeyIndex(Table * pTab)
>   
>   /**
>    * Create and initialize a new SQL Table object.
> + * All memory is allocated on region.

13. Not all - the table itself is on the heap.

> @@ -626,30 +605,35 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
>   static struct field_def *
>   sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
>   {
> -	sqlite3 *db = parser->db;
>   	struct field_def *field;
>   	assert(table->def != NULL);
> -	assert(table->def->exact_field_count >= (uint32_t)table->nCol);
>   	assert(id < SQLITE_MAX_COLUMN);
>   
>   	if (id >= table->def->exact_field_count) {
> -		uint32_t columns = table->def->exact_field_count;
> -		columns = (columns > 0) ? 2 * columns : 1;
> -		field = sqlite3DbRealloc(db, table->def->fields,
> -					 columns * sizeof(table->def->fields[0]));
> +		uint32_t columns_new = table->def->exact_field_count;
> +		columns_new = (columns_new > 0) ? 2 * columns_new : 1;
> +		struct region *region = &fiber()->gc;
> +		field = region_alloc(region, columns_new *
> +				     sizeof(table->def->fields[0]));
>   		if (field == NULL) {
> -			parser->rc = SQLITE_NOMEM_BKPT;
> +			diag_set(OutOfMemory, columns_new * sizeof(table->def->fields[0]),
> +				"region_alloc", "sql_field_retrieve");

14. Out of 80 symbols.

> +			parser->rc = SQL_TARANTOOL_ERROR;
>   			parser->nErr++;
>   			return NULL;
>   		}
>   
> -		for (uint32_t i = columns / 2; i < columns; i++) {
> +		for (uint32_t i = 0; i < table->def->exact_field_count; i++) {
> +			memcpy(&field[i], &table->def->fields[i],
> +			       sizeof(struct field_def));

15. Why you can not do one memcpy(field, table->def->fields,
				sizeof(*field) * table->def->exact_field_count); ?

> +		}
> +		for (uint32_t i = columns_new / 2; i < columns_new; i++) {
>   			memcpy(&field[i], &field_def_default,
>   			       sizeof(struct field_def));
> @@ -675,42 +659,44 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>   	sqlite3 *db = pParse->db;
>   	if ((p = pParse->pNewTable) == 0)
>   		return;
> +	assert(p->def->opts.temporary);
>   #if SQLITE_MAX_COLUMN
> -	if (p->nCol + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> -		sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
> +	if ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
> +		sqlite3ErrorMsg(pParse, "too many columns on %s",
> +				p->def->name);
>   		return;
>   	}
>   #endif
> -	if (sql_field_retrieve(pParse, p, (uint32_t) p->nCol) == NULL)
> +	if (sql_field_retrieve(pParse, p, (uint32_t) p->def->field_count) == NULL)

16. Out of 80 symbols.

>   		return;
> -	z = sqlite3DbMallocRaw(db, pName->n + 1);
> +	struct region *region = &fiber()->gc;
> +	z = region_alloc(region, pName->n + 1);
>   	if (z == 0)

17. Missed diag_set + pParse->nErr + pParse->rc.

> @@ -756,9 +741,11 @@ sqlite3AddNotNull(Parse * pParse, int onError)
>   {
>   	Table *p;
>   	p = pParse->pNewTable;
> -	if (p == 0 || NEVER(p->nCol < 1))
> +	if (p == 0 || NEVER(p->def->field_count < 1))
>   		return;
> -	p->aCol[p->nCol - 1].notNull = (u8) onError;
> +	p->def->fields[p->def->field_count - 1].nullable_action = (u8)onError;
> +	p->def->fields[p->def->field_count - 1].is_nullable =
> +		(onError == ON_CONFLICT_ACTION_NONE);

18. Now you have nullable_action_to_is_nullable.
> @@ -871,38 +858,31 @@ void
>   sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
>   {
>   	Table *p;
> -	Column *pCol;
>   	sqlite3 *db = pParse->db;
>   	p = pParse->pNewTable;
> +	assert(p->def->opts.temporary);
>   	if (p != 0) {
> -		pCol = &(p->aCol[p->nCol - 1]);
>   		if (!sqlite3ExprIsConstantOrFunction
>   		    (pSpan->pExpr, db->init.busy)) {
>   			sqlite3ErrorMsg(pParse,
>   					"default value of column [%s] is not constant",
> -					pCol->zName);
> +					p->def->fields[p->def->field_count - 1].name);
>   		} else {
> -			/* A copy of pExpr is used instead of the original, as pExpr contains
> -			 * tokens that point to volatile memory. The 'span' of the expression
> -			 * is required by pragma table_info.
> -			 */
> -			Expr x;
>   			assert(p->def != NULL);
>   			struct field_def *field =
> -				&p->def->fields[p->nCol - 1];
> -			sql_expr_free(db, field->default_value_expr, false);
> -
> -			memset(&x, 0, sizeof(x));
> -			x.op = TK_SPAN;
> -			x.u.zToken = sqlite3DbStrNDup(db, (char *)pSpan->zStart,
> -						      (int)(pSpan->zEnd -
> -							    pSpan->zStart));
> -			x.pLeft = pSpan->pExpr;
> -			x.flags = EP_Skip;
> -
> -			field->default_value_expr =
> -				sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
> -			sqlite3DbFree(db, x.u.zToken);
> +				&p->def->fields[p->def->field_count - 1];
> +			struct region *region = &fiber()->gc;
> +			uint32_t default_length = (int)(pSpan->zEnd - pSpan->zStart);
> +			field->default_value = region_alloc(region,
> +							    default_length + 1);
> +			if (field->default_value == NULL) {

19. Lets use diag + SQL_TARANTOOL_ERROR.

> +				pParse->rc = SQLITE_NOMEM_BKPT;
> +				pParse->nErr++;
> +				return;
> +			}
> +			strncpy(field->default_value, (char *)pSpan->zStart,

20. Why do you case to char*? zStart is already const char* and strncpy takes it ok.
> @@ -1087,8 +1067,10 @@ sql_column_collation(Table *table, uint32_t column)
>   	 * SQL specific structures.
>   	 */
>   	if (space == NULL || space_index(space, 0) == NULL) {
> -		assert(column < (uint32_t)table->nCol);
> -		return table->aCol[column].coll;
> +		assert(column < (uint32_t)table->def->field_count);
> +		struct coll *coll =
> +			coll_by_id(table->def->fields[column].coll_id);
> +		return coll;

21. It is simpler and shorter to return coll_by_id() with no saving
into the variable.
> @@ -2149,11 +2142,16 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * normally holds CHECK constraints on an ordinary table, but for
>   			 * a VIEW it holds the list of column names.
>   			 */
> -			sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
> -						   &pTable->nCol,
> -						   &pTable->aCol);
> +			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
> +			struct space_def *old_def = pTable->def;
> +			/* Delete it manually. */
> +			old_def->opts.temporary = true;
> +			if (sql_table_def_rebuild(db, pTable) != 0)
> +				nErr++;
> +			space_def_delete(old_def);

22. On error pTable->def is not reset, so here you would delete actually
old pTable->def, that is on a region. Do this space_def_delete in 'else' branch.
> @@ -2163,12 +2161,31 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * the column names from the SELECT statement that defines the view.
>   			 */
>   			assert(pTable->aCol == 0);
> -			pTable->nCol = pSelTab->nCol;
> +			assert((int)pTable->def->field_count == -1);
> +			assert(pSelTab->def->opts.temporary);
> +
> +			struct space_def *old_def = pTable->def;
> +			struct space_def *new_def = NULL;

23. Useless initialization - you reset this variable on the next line.

> +			new_def = sql_ephemeral_space_def_new(pParse, old_def->name);

24. Out of 80 symbols.

> +			if (new_def == NULL) {
> +				nErr++;
> +			} else {
> +				memcpy(new_def, old_def, sizeof(struct space_def));

25. Same.

> +				new_def->dict = NULL;
> +				new_def->opts.temporary = true;
> +				new_def->fields = pSelTab->def->fields;
> +				new_def->field_count = pSelTab->def->field_count;

26. Same.
> @@ -2193,10 +2210,22 @@ sqliteViewResetAll(sqlite3 * db)
>   	for (i = sqliteHashFirst(&db->pSchema->tblHash); i;
>   	     i = sqliteHashNext(i)) {
>   		Table *pTab = sqliteHashData(i);
> -		if (pTab->pSelect) {
> +		assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
> +		if (pTab->def->opts.is_view) {
>   			sqlite3DeleteColumnNames(db, pTab);
> +			struct space_def *old_def = pTab->def;
> +			assert(old_def->opts.temporary == false);
> +			/* Ignore fields allocated on region */

27. As I can see, here pTab is the normal view, that has normal
non-temporary space_def on malloc. Not 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);

28. It is not guaranteed. You must check for def == NULL and set
sqlite3OomFault(db). And do not reset pTab def, if you can not create a new one.
> @@ -2966,7 +2996,9 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   	 */
>   	if (pList == 0) {
>   		Token prevCol;
> -		sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol - 1].zName);
> +		sqlite3TokenInit(&prevCol,
> +				 pTab->def->
> +					 fields[pTab->def->field_count - 1].name);

29. Please, do not wrap '->'. And it is out of 80 symbols anyway.
> diff --git a/src/box/sql/hash.c b/src/box/sql/hash.c
> index cedcb7d..b109cca 100644
> --- a/src/box/sql/hash.c
> +++ b/src/box/sql/hash.c
> @@ -69,6 +69,7 @@ sqlite3HashClear(Hash * pH)
>   	while (elem) {
>   		HashElem *next_elem = elem->next;
>   		sqlite3_free(elem);
> +		free(elem->pKey);
>   		elem = next_elem;
>   	}
>   	pH->count = 0;
> @@ -292,7 +293,7 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
>   			removeElementGivenHash(pH, elem, h);
>   		} else {
>   			elem->data = data;
> -			elem->pKey = pKey;
> +			elem->pKey = strdup(pKey);

30. strdup can fail. See the sqlite3HashInsert what to do in such a case.
> @@ -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);

31. Same.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 939b5e3..c272ae1 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -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);

32. Out of 80.
> @@ -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)) {

33. Same.
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 2ab8751..926c3dd 100644
> --- a/src/box/sql/prepare.c
> +++ b/src/box/sql/prepare.c
> @@ -456,3 +435,21 @@ sqlite3_prepare_v2(sqlite3 * db,	/* Database handle. */
>   	assert(rc == SQLITE_OK || ppStmt == 0 || *ppStmt == 0);	/* VERIFY: F13021 */
>   	return rc;
>   }
> +
> +void
> +sql_parser_free(Parse *parser)
> +{
> +	if (parser == NULL)
> +		return;

34. It is never == NULL as I can see.

> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 5a50413..32a8e08 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -1661,14 +1661,15 @@ columnTypeImpl(NameContext * pNC, Expr * pExpr,
>   			} else if (pTab->pSchema) {
>   				/* A real table */
>   				assert(!pS);
> -				assert(iCol >= 0 && iCol < pTab->nCol);
> +				assert(iCol >= 0 && iCol <
> +						    (int)pTab->def->field_count);

35. Out of 80.
> @@ -1819,11 +1818,25 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   		testcase(aCol == 0);
>   	} else {
>   		nCol = 0;
> -		aCol = 0;
> +		aCol = NULL;
>   	}
>   	assert(nCol == (i16) nCol);
> -	*pnCol = nCol;
> -	*paCol = aCol;
> +
> +	/*
> +	 * This should be a table without resolved columns.
> +	 * sqlite3ViewGetColumnNames could use it to resolve
> +	 * names for existent table.

36. existent -> existing.

> +	 */
> +	assert(pTable->def->fields == NULL);

37. Lets better check def->opts.is_temporary. field == NULL means nothing.

> +	struct region *region = &fiber()->gc;
> +	pTable->def->fields =
> +		region_alloc(region, nCol * sizeof(pTable->def->fields[0]));

38. region_alloc can fail - set
diag + pParse->rc = SQL_TARANTOOL_ERROR + pParse->nErr++;

or

sqlite3OomFault().

Unfortunately we still have no standard way to handle OOM in SQL.
> @@ -1874,22 +1887,28 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   			if (cnt > 3)
>   				sqlite3_randomness(sizeof(cnt), &cnt);
>   		}
> -		pCol->zName = zName;
> -		if (zName && sqlite3HashInsert(&ht, zName, pCol) == pCol) {
> +		uint32_t name_len = (uint32_t)strlen(zName);
> +		if (zName != NULL && sqlite3HashInsert(&ht, zName, pCol) == pCol)
>   			sqlite3OomFault(db);
> +		pTable->def->fields[i].name =
> +			region_alloc(region, name_len + 1);
> +		if (pTable->def->fields[i].name == NULL) {
> +			sqlite3OomFault(db);
> +		} else {
> +			memcpy(pTable->def->fields[i].name, zName, name_len);
> +			pTable->def->fields[i].name[name_len] = '\0';
>   		}
>   	}
>   	sqlite3HashClear(&ht);
> -	if (db->mallocFailed) {
> -		for (j = 0; j < i; j++) {
> -			sqlite3DbFree(db, aCol[j].zName);
> -		}
> +	int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;
> +	if (rc != SQLITE_OK) {
>   		sqlite3DbFree(db, aCol);
> -		*paCol = 0;
> -		*pnCol = 0;
> -		return SQLITE_NOMEM_BKPT;
> +		pTable->def->fields = NULL;
> +		pTable->def->field_count = 0;

39. pTable->def is on region and is not deleted together with table. You
can skip this cleaning.
> @@ -4683,18 +4701,24 @@ selectExpander(Walker * pWalker, Select * p)
>   			assert(pFrom->pTab == 0);
>   			if (sqlite3WalkSelect(pWalker, pSel))
>   				return WRC_Abort;
> +			/* Will overwritten with pointer as unique identifier. */

40. Out of 66.

41. Will *be* overwritten.

> +			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
>   			pFrom->pTab = pTab =
> -			    sqlite3DbMallocZero(db, sizeof(Table));
> -			if (pTab == 0)
> +				sql_ephemeral_table_new(pParse, name);
> +			if (pTab == NULL)
>   				return WRC_Abort;
> +			/* rewrite old name with correct pointer */

42. Same in the previous review - please, start a sentence from a capital
letter, and finish with dot.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 8bb45c9..4fba008 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1867,15 +1867,6 @@ struct Savepoint {
>    * of this structure.
>    */
>   struct Column {
> -	char *zName;		/* Name of this column */
> -	enum field_type type;	/* Column type. */
> -	/** Collating sequence. */
> -	struct coll *coll;
> -	/**
> -	 * An ON_CONFLICT_ACTION code for handling a NOT NULL
> -	 * constraint.
> -	 */
> -	enum on_conflict_action notNull;

Niceeee.
> @@ -4153,4 +4142,24 @@ table_column_nullable_action(struct Table *tab, uint32_t column);
>   bool
>   table_column_is_nullable(struct Table *tab, uint32_t column);
>   
> +/**
> + * Initialize a new parser object.
> + * @param parser object to initialize.
> + */
> +static inline void
> +sql_parser_create(struct Parse *parser)
> +{
> +	memset(parser, 0, PARSE_HDR_SZ);
> +	memset(PARSE_TAIL(parser), 0, PARSE_TAIL_SZ);
> +	struct region *region = &fiber()->gc;
> +	parser->region_initial_size = region_used(region);
> +}
> +
> +/**
> + * Release the parser object resources.
> + * @param parser object to release.
> + */
> +void
> +sql_parser_free(struct Parse *parser);

43. Out convention is 'create/destroy'. Please, respect it.

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

* [tarantool-patches] Re: [PATCH v5 3/3] sql: space_def* instead of Table* in Expr
  2018-05-11  8:49 ` [tarantool-patches] [PATCH v5 3/3] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-11 20:59   ` Vladislav Shpilevoy
  2018-05-14 11:20     ` Kirill Shcherbatov
  0 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-11 20:59 UTC (permalink / raw)
  To: tarantool-patches, Kirill Shcherbatov

Hello. Thanks for the patch! See 12 comments below.

On 11/05/2018 11:49, Kirill Shcherbatov wrote:
> This patch allows to remove Checks from SQL to
> server as sqlite3ResolveSelfReference requires
> Expr structure pointer.
> 
> Part of #3272.
> ---
>   src/box/field_def.c     |   1 +
>   src/box/field_def.h     |  14 ++++++
>   src/box/sql.c           |  10 ++---
>   src/box/sql/build.c     |  48 +++++++++++---------
>   src/box/sql/delete.c    |   4 +-
>   src/box/sql/expr.c      | 115 +++++++++++++++++++++++++++---------------------
>   src/box/sql/fkey.c      |  13 +++---
>   src/box/sql/insert.c    |  39 +++++++++-------
>   src/box/sql/pragma.c    |   8 ++--
>   src/box/sql/resolve.c   |  10 ++---
>   src/box/sql/select.c    |  26 ++++++-----
>   src/box/sql/sqliteInt.h |  41 +++++------------
>   src/box/sql/update.c    |  35 +++++++--------
>   src/box/sql/vdbeaux.c   |  28 +++---------
>   src/box/sql/where.c     |  13 +++---
>   src/box/sql/wherecode.c |  19 +++++---
>   src/box/sql/whereexpr.c |   2 +-
>   17 files changed, 219 insertions(+), 207 deletions(-)
> 
> diff --git a/src/box/field_def.h b/src/box/field_def.h
> index a42beab..b210756 100644
> --- a/src/box/field_def.h
> +++ b/src/box/field_def.h
> @@ -70,6 +70,15 @@ enum on_conflict_action {
>   	on_conflict_action_MAX
>   };
>   
> +enum affinity_type {
> +	SQLITE_AFF_UNDEFINED = 0,
> +	SQLITE_AFF_BLOB = 'A',
> +	SQLITE_AFF_TEXT = 'B',
> +	SQLITE_AFF_NUMERIC = 'C',
> +	SQLITE_AFF_INTEGER = 'D',
> +	SQLITE_AFF_REAL = 'E',

1. Please, do not use SQLITE prefix in Tarantool. Lets name it with
AFFINITY prefix instead of SQLITE_AFF.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index a02fe89..f082d01 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -1049,10 +1051,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)

2. Fix the comment as well.

3. Now this function is useless. You always can get the collation from
coll_by_id(def->fields[column].coll_id).

> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 119940c..8b34c57 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -3175,8 +3180,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'

4. Lets do not inline affinity values: use AFFINITY_INTEGER.
> @@ -3519,45 +3525,48 @@ 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);
>   	}
>   }
>   
> -/*
> +/**
>    * Generate code to extract the value of the iCol-th column of a table.
> + * @param v  The VDBE under construction.

5. To many white spaces.

> + * @param space_def Space definition.
> + * @param iTabCur The PK cursor.
> + * @param iCol Index of the column to extract.
> + * @param regOut  Extract the value into this register.
>    */
>   void
> -sqlite3ExprCodeGetColumnOfTable(Vdbe * v,	/* The VDBE under construction */
> -				Table * pTab,	/* The table containing the value */
> -				int iTabCur,	/* The PK cursor */
> -				int iCol,	/* Index of the column to extract */
> -				int regOut	/* Extract the value into this register */
> -    )
> +sqlite3ExprCodeGetColumnOfTable(Vdbe * v, struct space_def *space_def,

6. Do not put white space after '*' in a declaration. Here and in other places.

> +				int iTabCur, int iCol, int regOut)
>   {
>   	sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut);
>   	if (iCol >= 0) {
> -		sqlite3ColumnDefault(v, pTab, iCol, regOut);
> +		sqlite3ColumnDefault(v, space_def, iCol, regOut);
>   	}
>   }
>   
> -/*
> +/**
>    * Generate code that will extract the iColumn-th column from
>    * table pTab and store the column value in a register.
>    *
> - * An effort is made to store the column value in register iReg.  This
> - * is not garanteeed for GetColumn() - the result can be stored in
> - * any register.  But the result is guaranteed to land in register iReg
> - * for GetColumnToReg().
> + * An effort is made to store the column value in register iReg.
> + * This is not garanteeed for GetColumn() - the result can be
> + * stored in any register.  But the result is guaranteed to land
> + * in register iReg for GetColumnToReg().
> + * @param pParse Parsing and code generating context.
> + * @param space_def Space definition.
> + * @param iColumn Index of the table column.
> + * @param iTable The cursor pointing to the table.
> + * @param iReg Store results here.
> + * @param p5 P5 value for OP_Column + FLAGS.
> + * @return iReg value.
>    */
>   int
> -sqlite3ExprCodeGetColumn(Parse * pParse,	/* Parsing and code generating context */
> -			 Table * pTab,	/* Description of the table we are reading from */
> -			 int iColumn,	/* Index of the table column */
> -			 int iTable,	/* The cursor pointing to the table */
> -			 int iReg,	/* Store results here */
> -			 u8 p5	/* P5 value for OP_Column + FLAGS */
> -    )
> +sqlite3ExprCodeGetColumn(Parse * pParse, struct space_def * space_def,
> +			 int iColumn, int iTable, int iReg, u8 p5)

7. Same.

> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 250c402..0207ee8 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 =
> +						space_def_column_is_nullable(
> +							pTab->def, i);

8. For flags use 'is_' prefix, please. I know, it is not your code, but if
you modify it, then lets do it correctly.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 32a8e08..5bd958a 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 4fba008..ad7cc54 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2226,7 +2205,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;

9. Please, use /** prefix for comments out of function body.
> @@ -2361,7 +2341,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. */

10. Same.
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 5f5807c..9ae77e0 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)

11. Same as 5.
> diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
> index 0ccca77..3054fc3 100644
> --- a/src/box/sql/vdbeaux.c
> +++ b/src/box/sql/vdbeaux.c
> @@ -4724,28 +4724,10 @@ 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)
> +space_def_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 tuple_format *format = space->format;
> -
> -		assert(format);
> -		assert(format->field_count > column);
> -
> -		return nullable_action_to_is_nullable(
> -			format->fields[column].nullable_action);
> -	} else {
> -		/* tab is ephemeral (in SQLite sense).  */
> -		assert(tab->def->fields[column].is_nullable ==
> -		       nullable_action_to_is_nullable(
> -			tab->def->fields[column].nullable_action));
> -		return tab->def->fields[column].is_nullable;
> -	}
> +	assert(def->fields[column].is_nullable ==
> +	       nullable_action_to_is_nullable(
> +		       def->fields[column].nullable_action));
> +	return def->fields[column].is_nullable;

12. Perfect. Now this function is useless. It can be inlined and removed.

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

* [tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column
  2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
@ 2018-05-14 11:20     ` Kirill Shcherbatov
  2018-05-14 13:39       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-14 11:20 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

> 1. Lets remove 'to': nullable_action_is_nullable. It is too long too, but I
> can not think up anotherstatic inline bool
-nullable_action_to_is_nullable(enum on_conflict_action nullable_action)
+nullable_action_is_nullable(enum on_conflict_action nullable_action)

> 4. Use space_def_sizeof for this please.
diff --git a/src/box/space_def.c b/src/box/space_def.c
index 77c0e02..1fa3345 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -47,20 +47,7 @@ const struct opt_def space_opts_reg[] = {
 	OPT_END,
 };
 
-/**
- * Size of the space_def.
- * @param name_len Length of the space name.
- * @param fields Fields array of space format.
- * @param field_count Space field count.
- * @param[out] names_offset Offset from the beginning of a def to
- *             a field names memory.
- * @param[out] fields_offset Offset from the beginning of a def to
- *             a fields array.
- * @param[out] def_expr_offset Offset from the beginning of a def
- *             to a def_value_expr array.
- * @retval Size in bytes.
- */
-static inline size_t
+size_t
 space_def_sizeof(uint32_t name_len, const struct field_def *fields,
 		 uint32_t field_count, uint32_t *names_offset,
 		 uint32_t *fields_offset, uint32_t *def_expr_offset)

diff --git a/src/box/space_def.h b/src/box/space_def.h
index 4add1e7..52447b6 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -157,6 +157,24 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
 	      const struct space_opts *opts, const struct field_def *fields,
 	      uint32_t field_count);
 
+/**
+ * Size of the space_def.
+ * @param name_len Length of the space name.
+ * @param fields Fields array of space format.
+ * @param field_count Space field count.
+ * @param[out] names_offset Offset from the beginning of a def to
+ *             a field names memory.
+ * @param[out] fields_offset Offset from the beginning of a def to
+ *             a fields array.
+ * @param[out] def_expr_offset Offset from the beginning of a def
+ *             to a def_value_expr array.
+ * @retval Size in bytes.
+ */
+size_t
+space_def_sizeof(uint32_t name_len, const struct field_def *fields,
+		 uint32_t field_count, uint32_t *names_offset,
+		 uint32_t *fields_offset, uint32_t *def_expr_offset);
+

diff --git a/src/box/sql.c b/src/box/sql.c
index 7d48cdc..d8a3ef3 100644

@@ -1704,7 +1704,8 @@ sql_ephemeral_space_def_new(Parse *parser, const char *name)
 	struct space_def *def = NULL;
 	struct region *region = &fiber()->gc;
 	size_t name_len = name != NULL ? strlen(name) : 0;
-	size_t size = sizeof(struct space_def) + name_len + 1;
+	uint32_t dummy;
+	size_t size = space_def_sizeof(name_len, NULL, 0, &dummy, &dummy, &dummy);
 	def = (struct space_def *)region_alloc(region, size);
 	if (def == NULL) {
 		diag_set(OutOfMemory, sizeof(struct tuple_dictionary),

> 5. If you return on def == NULL, then either move all the other code
> under 'else' body, or remove the 'else'.

@@ -1712,9 +1713,9 @@ sql_ephemeral_space_def_new(Parse *parser, const char *name)
 		parser->rc = SQL_TARANTOOL_ERROR;
 		parser->nErr++;
 		return NULL;
-	} else {
-		memset(def, 0, size);
 	}
+
+	memset(def, 0, size);

> 6. Same as in the previous review - ephemeric -> ephemeral.
+++ b/src/box/sql.h
@@ -145,7 +145,7 @@ void
 sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
 
 /**
- * Create and initialize a new ephemeric SQL Table object.
+ * Create and initialize a new ephemeral SQL Table object.

> 7. Same.
@@ -155,7 +155,7 @@ struct Table *
sql_ephemeral_table_new(struct Parse *parser, const char *name);

/**
- * Create and initialize a new ephemeric space_def object.
+ * Create and initialize a new ephemeral space_def object.

> 8. In the function body: "/* All allocations are on region. */".
> Please, fix the comment.

@@ -1742,8 +1743,6 @@ int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
 	assert(pTable->def->opts.temporary);
-
-	/* All allocations are on region. */
> 10. pDflt == NULL.
+++ b/src/box/sql/alter.c
@@ -200,11 +200,12 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 		return;
 	}
 	assert(pNew->def->fields[pNew->def->field_count - 1].is_nullable ==
-	       nullable_action_to_is_nullable(
-		pNew->def->fields[pNew->def->field_count - 1].nullable_action));
+		       nullable_action_is_nullable(
+			       pNew->def->fields[pNew->def->field_count -
+						 1].nullable_action));
 
-	if (pNew->def->fields[pNew->def->field_count - 1].nullable_action
-	    && !pDflt) {
+	if (!pNew->def->fields[pNew->def->field_count - 1].is_nullable
+	    && pDflt == NULL) {

> 11. Forgot to set sqlite3OomFault(db). I know, that this code is
> unreachable, but in the future it will be not.
@@ -296,6 +297,7 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
 	pNew->def = space_def_dup(pTab->def);
 	if (pNew->def == NULL) {
 		sqlite3DbFree(db, pNew);
+		sqlite3OomFault(db);

> 12. Nice. Now this function consists of a single DbFree - lets just
> inline it and remove sqlite3DeleteColumnNames.
@@ -381,7 +370,7 @@ deleteTable(sqlite3 * db, Table * pTable)
 	/* Delete the Table structure itself.
 	 */
 	sqlite3HashClear(&pTable->idxHash);
-	sqlite3DeleteColumnNames(db, pTable);
+	sqlite3DbFree(db, pTable->aCol);

@@ -2212,10 +2213,9 @@ sqliteViewResetAll(sqlite3 * db)
 		Table *pTab = sqliteHashData(i);
 		assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
 		if (pTab->def->opts.is_view) {
-			sqlite3DeleteColumnNames(db, pTab);
+			sqlite3DbFree(db, pTab->aCol);

+++ b/src/box/sql/sqliteInt.h
@@ -3503,7 +3503,6 @@ int sqlite3InitCallback(void *, int, char **, char **);
 void sqlite3CommitInternalChanges();
-void sqlite3DeleteColumnNames(sqlite3 *, Table *);

+++ b/src/box/sql/build.c
@@ -285,17 +285,6 @@ sqlite3CommitInternalChanges()
 }
 
 /*
- * Delete memory allocated for the column names of a table or view (the
- * Table.aCol[] array).
- */
-void
-sqlite3DeleteColumnNames(sqlite3 * db, Table * pTable)
-{
-	assert(pTable != 0);
-	sqlite3DbFree(db, pTable->aCol);
-}


> 14. Out of 80 symbols.
> 15. Why you can not do one memcpy(field, table->def->fields,
> 				sizeof(*field) * table->def->exact_field_count); ?@@ -616,17 +605,15 @@ sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
 		field = region_alloc(region, columns_new *
 				     sizeof(table->def->fields[0]));
 		if (field == NULL) {
-			diag_set(OutOfMemory, columns_new * sizeof(table->def->fields[0]),
+			diag_set(OutOfMemory, columns_new *
+				sizeof(table->def->fields[0]),
 				"region_alloc", "sql_field_retrieve");
 			parser->rc = SQL_TARANTOOL_ERROR;
 			parser->nErr++;
 			return NULL;
 		}
-
-		for (uint32_t i = 0; i < table->def->exact_field_count; i++) {
-			memcpy(&field[i], &table->def->fields[i],
-			       sizeof(struct field_def));
-		}
+		memcpy(field, table->def->fields,
+		       sizeof(*field) * table->def->exact_field_count);


> 16. Out of 80 symbols.
> 17. Missed diag_set + pParse->nErr + pParse->rc.
@@ -667,12 +654,18 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
 		return;
 	}
 #endif
-	if (sql_field_retrieve(pParse, p, (uint32_t) p->def->field_count) == NULL)
+	if (sql_field_retrieve(pParse, p,
+			       (uint32_t) p->def->field_count) == NULL)
 		return;
 	struct region *region = &fiber()->gc;
 	z = region_alloc(region, pName->n + 1);
-	if (z == 0)
+	if (z == NULL) {
+		diag_set(OutOfMemory, pName->n + 1,
+			 "region_alloc", "sqlite3AddColumn");
+		pParse->rc = SQL_TARANTOOL_ERROR;
+		pParse->nErr++;
 		return;
+	}

> 18. Now you have nullable_action_to_is_nullable.
@@ -745,7 +738,7 @@ sqlite3AddNotNull(Parse * pParse, int onError)
 		return;
 	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);
+		nullable_action_is_nullable(onError);

> 19. Lets use diag + SQL_TARANTOOL_ERROR.
> 20. Why do you case to char*? zStart is already const char* and strncpy takes it ok
@@ -876,11 +869,14 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
 			field->default_value = region_alloc(region,
 							    default_length + 1);
 			if (field->default_value == NULL) {
-				pParse->rc = SQLITE_NOMEM_BKPT;
+				diag_set(OutOfMemory, default_length + 1,
+					 "region_alloc",
+					 "sqlite3AddDefaultValue");
+				pParse->rc = SQL_TARANTOOL_ERROR;
 				pParse->nErr++;
 				return;
 			}
-			strncpy(field->default_value, (char *)pSpan->zStart,
+			strncpy(field->default_value, pSpan->zStart,
 				default_length);

> 21. It is simpler and shorter to return coll_by_id() with no saving
> into the variable.
> 22. On error pTable->def is not reset, so here you would delete actually
> old pTable->def, that is on a region. Do this space_def_delete in 'else' branch.
@@ -2142,13 +2136,16 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * normally holds CHECK constraints on an ordinary table, but for
 			 * a VIEW it holds the list of column names.
 			 */
-			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
 			struct space_def *old_def = pTable->def;
-			/* Delete it manually. */
+			/* Manage def memory manually. */
 			old_def->opts.temporary = true;
-			if (sql_table_def_rebuild(db, pTable) != 0)
+			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
+			if (sql_table_def_rebuild(db, pTable) != 0) {
+				old_def->opts.temporary = false;
 				nErr++;
-			space_def_delete(old_def);
+			} else {
+				space_def_delete(old_def);
+			}


> 28. It is not guaranteed. You must check for def == NULL and set
> sqlite3OomFault(db). And do not reset pTab def, if you can not create a new one.
@@ -2223,8 +2223,12 @@ sqliteViewResetAll(sqlite3 * db)
 						  strlen(old_def->engine_name),
 						  &old_def->opts,
 						  NULL, 0);
-			assert(pTab->def);
-			space_def_delete(old_def);
+			if (pTab->def == NULL) {
+				sqlite3OomFault(db);
+				pTab->def = old_def;
+			} else {
+				space_def_delete(old_def);
+			}

> 29. Please, do not wrap '->'. And it is out of 80 symbols anyway.
@@ -2997,8 +3001,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	if (pList == 0) {
 		Token prevCol;
 		sqlite3TokenInit(&prevCol,
-				 pTab->def->
-					 fields[pTab->def->field_count - 1].name);
+				 pTab->def->fields[
+				 	pTab->def->field_count - 1].name);

> 30. strdup can fail. See the sqlite3HashInsert what to do in such a case.
+++ b/src/box/sql/hash.c
@@ -293,7 +293,8 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
 			removeElementGivenHash(pH, elem, h);
 		} else {
 			elem->data = data;
-			elem->pKey = strdup(pKey);
+			assert(elem->pKey != NULL);
+			assert(strcmp(elem->pKey, pKey) == 0);
 		}
 		return old_data;
 	}
@@ -303,6 +304,10 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
 	if (new_elem == 0)
 		return data;
 	new_elem->pKey = strdup(pKey);
+	if (new_elem->pKey == NULL) {
+		sqlite3_free(new_elem);
+		return data;
+	}
 	new_elem->data = data;
 	pH->count++;


>> +void
>> +sql_parser_free(Parse *parser)
>> +{
>> +	if (parser == NULL)
>> +		
> 34. It is never == NULL as I can see.
 void
-sql_parser_free(Parse *parser)
+sql_parser_destroy(Parse *parser)
 {
-	if (parser == NULL)
-		return;
+	assert(parser != NULL);

> 36. existent -> existing.
+++ b/src/box/sql/select.c
@@ -1825,12 +1825,16 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
-	 * names for existent table.
+	 * names for existing table.

> 37. Lets better check def->opts.is_temporary. field == NULL means nothing.
sqlite3ViewGetColumnNames calls sqlite3ColumnsFromExprList with non-temporal table in 
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
	if (pSel) {
branch.
It is ok, as we manually manage memory in this context.

> 38. region_alloc can fail - set
> sqlite3OomFault().
 	struct region *region = &fiber()->gc;
 	pTable->def->fields =
 		region_alloc(region, nCol * sizeof(pTable->def->fields[0]));
+	if (pTable->def->fields == NULL) {
+		sqlite3OomFault(db);
+		goto cleanup;
+	}
 	memset(pTable->def->fields, 0, nCol * sizeof(pTable->def->fields[0]));
 	/* NULL nullable_action should math is_nullable flag. */
 	for (int i = 0; i < nCol; i++)
@@ -1899,6 +1903,7 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 			pTable->def->fields[i].name[name_len] = '\0';
 		}
 	}
+cleanup:
 	sqlite3HashClear(&ht);
 	int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;


> 40. Out of 66.
> 41. Will *be* overwritten.
> 42. Same in the previous review - please, start a sentence from a capital
> letter, and finish with dot.
-			/* Will overwritten with pointer as unique identifier. */
+			/*
+			 * Will be overwritten with pointer as unique
+			 * identifier.
+			 * */
 			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
 			pFrom->pTab = pTab =
 				sql_ephemeral_table_new(pParse, name);
 			if (pTab == NULL)
 				return WRC_Abort;
-			/* rewrite old name with correct pointer */
+			/* Rewrite old name with correct pointer. */

> 43. Out convention is 'create/destroy'. Please, respect it.
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -671,6 +671,6 @@ sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
 		return -1;
 	}
 	*result = parser.parsed_expr;
-	sql_parser_free(&parser);
+	sql_parser_destroy(&parser);

+++ b/src/box/sql/trigger.c
@@ -935,7 +935,7 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 
 	assert(!pSubParse->pZombieTab);
 	assert(!pSubParse->pTriggerPrg && !pSubParse->nMaxArg);
-	sql_parser_free(pSubParse);
+	sql_parser_destroy(pSubParse);

+++ b/src/box/sql/sqliteInt.h
@@ -4160,6 +4159,6 @@ sql_parser_create(struct Parse *parser)
  * @param parser object to release.
  */
 void
-sql_parser_free(struct Parse *parser);
+sql_parser_destroy(struct Parse *parser);

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

* [tarantool-patches] Re: [PATCH v5 3/3] sql: space_def* instead of Table* in Expr
  2018-05-11 20:59   ` [tarantool-patches] " Vladislav Shpilevoy
@ 2018-05-14 11:20     ` Kirill Shcherbatov
  0 siblings, 0 replies; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-14 11:20 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

> 1. Please, do not use SQLITE prefix in Tarantool. Lets name it with
> AFFINITY prefix instead of SQLITE_AFF.
 enum affinity_type {
-       SQLITE_AFF_UNDEFINED = 0,
-       SQLITE_AFF_BLOB = 'A',
-       SQLITE_AFF_TEXT = 'B',
-       SQLITE_AFF_NUMERIC = 'C',
-       SQLITE_AFF_INTEGER = 'D',
-       SQLITE_AFF_REAL = 'E',
+       AFFINITY_UNDEFINED = 0,
+       AFFINITY_BLOB = 'A',
+       AFFINITY_TEXT = 'B',
+       AFFINITY_NUMERIC = 'C',
+       AFFINITY_INTEGER = 'D',
+       AFFINITY_REAL = 'E',
 };


> 2. Fix the comment as well> 3. Now this function is useless. You always can get the collation from
> coll_by_id(def->fields[column].coll_id).
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index f2d325c..33815b8 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1032,7 +1032,7 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 			assert(pIdx->nColumn == 1);
 			if (pIdx->aiColumn[0] == i) {
 				pIdx->coll_array[0] =
-					sql_column_collation(p->def, i);
+					coll_by_id(p->def->fields[i].coll_id);
 			}
 		}
 	}
@@ -1040,39 +1040,6 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
 }
 
 /**
- * Return collation of given column from space_def	.
- *
- * @param table Table which is used to fetch column.
- * @param column Number of column.
- * @retval Pointer to collation.
- */
-struct coll *
-sql_column_collation(struct space_def *def, uint32_t column)
-{
-	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
-	 * from struct space. To be more precise when:
-	 * 1. space is ephemeral. Thus, its id is zero and
-	 *    it can't be found in space cache.
-	 * 2. space is a view. Hence, it lacks any functional
-	 *    parts such as indexes or fields.
-	 * 3. space is under construction. So, the same as p.1
-	 *    it can't be found in space cache.
-	 * In cases mentioned above collation is fetched from
-	 * SQL specific structures.
-	 */
-	if (space == NULL || space_index(space, 0) == NULL) {
-		assert(column < (uint32_t)def->field_count);
-		return coll_by_id(def->fields[column].coll_id);
-	}
-
-	return space->format->fields[column].coll;
-}
-
-/**
  * Return name of given column collation from index.
  *
  * @param idx Index which is used to fetch column.
@@ -3107,7 +3074,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 				goto exit_create_index;
 			}
 		} else if (j >= 0) {
-			coll = sql_column_collation(pTab->def, j);
+			coll = coll_by_id(pTab->def->fields[j].coll_id);
 		} else {
 			coll = NULL;
 		}
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 51c50e0..59f239d 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -34,6 +34,7 @@
  * for generating VDBE code that evaluates expressions in SQLite.
  */
 #include <box/coll.h>
+#include "box/coll_cache.h"
 #include "sqliteInt.h"
 #include "box/session.h"
 
@@ -187,7 +188,8 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_found)
 			 */
 			int j = p->iColumn;
 			if (j >= 0) {
-				coll = sql_column_collation(p->space_def, j);
+				coll = coll_by_id(
+					p->space_def->fields[j].coll_id);
 				*is_found = true;
 			}
 			break;
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 7c83924..c7b1cda 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"
@@ -298,8 +299,8 @@ 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,
-									iCol);
+					def_coll = coll_by_id(
+						pParent->def->fields[iCol].coll_id);
 					struct coll *coll;
 					coll = sql_index_collation(pIdx, i);
 					if (def_coll != coll)
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index b388735..710a388 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -37,6 +37,7 @@
 #include "tarantoolInt.h"
 #include "box/session.h"
 #include "box/schema.h"
+#include "box/coll_cache.h"
 #include "bit/bit.h"
 
 /*
@@ -1809,8 +1810,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
 		 * Collating sequence must be the same on all
 		 * columns.
 		 */
-		if (sql_column_collation(pDest->def, i) !=
-		    sql_column_collation(pSrc->def, i))
+		if (coll_by_id(pDest->def->fields[i].coll_id) !=
+			coll_by_id(pSrc->def->fields[i].coll_id))
 			return 0;
 		/* The tab2 must be NOT NULL if tab1 is */
 		if (!space_def_column_is_nullable(pDest->def, i)
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 02489b7..7cca3d5 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3499,8 +3499,6 @@ void sqlite3AddCollateType(Parse *, Token *);
 
 const char *
 column_collation_name(Table *, uint32_t);
-struct coll *
-sql_column_collation(struct space_def *, uint32_t);
 const char *
 index_collation_name(Index *, uint32_t);
 struct coll *

> 4. Lets do not inline affinity values: use AFFINITY_INTEGER.
@@ -3184,7 +3184,7 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
-		    && affinity == 'D'
+		    && affinity == AFFINITY_INTEGER

@@ -1303,7 +1303,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
-				if (pTab->zColAff[pIdx->aiColumn[0]] == 'D') {
+				if (pTab->zColAff[pIdx->aiColumn[0]] ==
+				    AFFINITY_INTEGER) {

@@ -1392,7 +1392,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
-		if (nPkCol == 1 && affinity == 'D') {
+		if (nPkCol == 1 && affinity == AFFINITY_INTEGER) {


> 8. For flags use 'is_' prefix, please. I know, it is not your code, but if
> you modify it, then lets do it correctly.@@ -373,7 +373,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
-					bool nullable =
+					bool is_nullable =


> 9. Please, use /** prefix for comments out of function body.
> 10. Same.
-		/* Pointer to space definition. */
+		/** Pointer to space definition. */

-	/* Pointer for table relative definition. */
+	/** Pointer for table relative definition. */


> 12. Perfect. Now this function is useless. It can be inlined and removed.
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 9057301..9a8f045 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2139,8 +2139,7 @@ sqlite3ExprCanBeNull(const Expr * p)
 		assert(p->space_def != NULL);
 		return ExprHasProperty(p, EP_CanBeNull) ||
 		       (p->iColumn >= 0
-		        && space_def_column_is_nullable(p->space_def,
-							p->iColumn));
+		        && p->space_def->fields[p->iColumn].is_nullable);
 	default:
 		return 1;
 	}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 71567cf..1a883e2 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -1120,7 +1120,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			/* Don't bother checking for NOT NULL on columns that do not change */
 			continue;
 		}
-		if (space_def_column_is_nullable(pTab->def, i)
+		if (pTab->def->fields[i].is_nullable
 		    || (pTab->tabFlags & TF_Autoincrement
 			&& pTab->iAutoIncPKey == i))
 			continue;	/* This column is allowed to be NULL */
@@ -1815,8 +1815,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
 			coll_by_id(pSrc->def->fields[i].coll_id))
 			return 0;
 		/* The tab2 must be NOT NULL if tab1 is */
-		if (!space_def_column_is_nullable(pDest->def, i)
-		    && space_def_column_is_nullable(pSrc->def, i))
+		if (!pDest->def->fields[i].is_nullable
+		    && pSrc->def->fields[i].is_nullable)
 			return 0;
 		/* Default values for second and subsequent columns need to match. */
 		if (i > 0) {
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 4d7b834..c5ae5cb 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -374,8 +374,8 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 						}
 					}
 					bool is_nullable =
-						space_def_column_is_nullable(
-							pTab->def, i);
+						pTab->def->fields[i].
+							is_nullable;
 					uint32_t space_id =
 						SQLITE_PAGENO_TO_SPACEID(
 							pTab->tnum);
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 55a1a87..75699ae 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4117,9 +4117,6 @@ extern int sqlite3InitDatabase(sqlite3 * db);
 enum on_conflict_action
 table_column_nullable_action(struct Table *tab, uint32_t column);
 
-bool
-space_def_column_is_nullable(struct space_def *def, uint32_t column);
-
 /**
  * Initialize a new parser object.
  * @param parser object to initialize.
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 807e017..4e3b236 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4712,22 +4712,3 @@ table_column_nullable_action(struct Table *tab, uint32_t column)
 
 	return field.nullable_action;
 }
-
-/**
- * Return nullable flag value of given column in given table.
- * FIXME: This is implemented in expensive way. For each invocation table lookup
- * is performed. In future, first param will be replaced with pointer to struct
- * space.
- *
- * @param tab  pointer to the table
- * @param column column number for which action to be returned
- * @return return nullability flag value
- */
-bool
-space_def_column_is_nullable(struct space_def *def, uint32_t column)
-{
-	assert(def->fields[column].is_nullable ==
-	       nullable_action_is_nullable(
-		       def->fields[column].nullable_action));
-	return def->fields[column].is_nullable;
-}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 8e77e3e..f3a10c0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -493,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 !space_def_column_is_nullable(pIdx->pTable->def, j);
+		return !pIdx->pTable->def->fields[j].is_nullable;
 	} else if (j == (-1)) {
 		return 1;
 	} else {
@@ -3333,8 +3333,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
 				if (isOrderDistinct
 				    && iColumn >= 0
 				    && j >= pLoop->nEq
-				    && space_def_column_is_nullable(
-					pIndex->pTable->def, iColumn)) {
+				    && pIndex->pTable->def->fields[iColumn].
+					is_nullable) {
 					isOrderDistinct = 0;
 				}
 
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 0bc6b39..a3db23b 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1261,8 +1261,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 			 *      NULL, ... NULL, min_value, ...
 			 */
 			if ((j >= 0 &&
-				space_def_column_is_nullable(pIdx->pTable->def,
-							     j)) ||
+				pIdx->pTable->def->fields[j].is_nullable) ||
 			    j == XN_EXPR) {
 				assert(pLoop->nSkip == 0);
 				bSeekPastNull = 1;
@@ -1308,9 +1307,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 #endif
 			if (pRangeStart == 0) {
 				j = pIdx->aiColumn[nEq];
-				if ((j >= 0
-				     && space_def_column_is_nullable(
-					pIdx->pTable->def, j)) ||
+				if ((j >= 0 &&
+				     pIdx->pTable->def->fields[j].is_nullable) ||
 				    j == XN_EXPR) {
 					bSeekPastNull = 1;
 				}

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

* [tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column
  2018-05-14 11:20     ` Kirill Shcherbatov
@ 2018-05-14 13:39       ` Vladislav Shpilevoy
  2018-05-15 15:56         ` Kirill Shcherbatov
  0 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-14 13:39 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Thanks for fixes. See below 11 comments.
> 
> /**
> - * Create and initialize a new ephemeric space_def object.
> + * Create and initialize a new ephemeral space_def object.
> 
>> 8. In the function body: "/* All allocations are on region. */".
>> Please, fix the comment.
> 
> @@ -1742,8 +1743,6 @@ int
>   sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
>   {
>   	assert(pTable->def->opts.temporary);
> -
> -	/* All allocations are on region. */


1. Wrong fix. As I can see sql_table_def_rebuild does not depend on allocation
way of its arguments. So the function declaration comment must be fixed too.

And please, do not skip my comments (you skipped 9, 13, ...).
> 
>> 16. Out of 80 symbols.
>> 17. Missed diag_set + pParse->nErr + pParse->rc.
> @@ -667,12 +654,18 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
>   		return;
>   	}
>   #endif
> -	if (sql_field_retrieve(pParse, p, (uint32_t) p->def->field_count) == NULL)
> +	if (sql_field_retrieve(pParse, p,
> +			       (uint32_t) p->def->field_count) == NULL)
>   		return;
>   	struct region *region = &fiber()->gc;
>   	z = region_alloc(region, pName->n + 1);
> -	if (z == 0)
> +	if (z == NULL) {
> +		diag_set(OutOfMemory, pName->n + 1,
> +			 "region_alloc", "sqlite3AddColumn");

2. OutOfMemory takes variable name in the last argument. Not caller function name.
>> 19. Lets use diag + SQL_TARANTOOL_ERROR.
>> 20. Why do you case to char*? zStart is already const char* and strncpy takes it ok
> @@ -876,11 +869,14 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
>   			field->default_value = region_alloc(region,
>   							    default_length + 1);
>   			if (field->default_value == NULL) {
> -				pParse->rc = SQLITE_NOMEM_BKPT;
> +				diag_set(OutOfMemory, default_length + 1,
> +					 "region_alloc",
> +					 "sqlite3AddDefaultValue");

3. Same.
> 
>> 21. It is simpler and shorter to return coll_by_id() with no saving
>> into the variable.

4. Looks like you removed sql_column_collation. It would be good to see the diff
under the comment.

>> 22. On error pTable->def is not reset, so here you would delete actually
>> old pTable->def, that is on a region. Do this space_def_delete in 'else' branch.
> @@ -2142,13 +2136,16 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   			 * normally holds CHECK constraints on an ordinary table, but for
>   			 * a VIEW it holds the list of column names.
>   			 */
> -			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
>   			struct space_def *old_def = pTable->def;
> -			/* Delete it manually. */
> +			/* Manage def memory manually. */
>   			old_def->opts.temporary = true;
> -			if (sql_table_def_rebuild(db, pTable) != 0)
> +			sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);

5. Out of 80 symbols. Please, do self-review of a patch before sending.

6. I looked at sqlite3ColumnsFromExprList, and looks like it does not use
opts.temporary flag. So you can remove this opts.temporary flag manipulation, it is not?

Below you skipped 5 comments in row((

>> +            struct space_def *old_def = pTable->def;
>> +            struct space_def *new_def = NULL;

> 23. Useless initialization - you reset this variable on the next line. 

Here I meant this:

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 33815b8a6..8c7e9d646 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2133,8 +2133,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
                         assert(pSelTab->def->opts.temporary);
  
                         struct space_def *old_def = pTable->def;
-                       struct space_def *new_def;
-                       new_def =
+                       struct space_def *new_def =
                                 sql_ephemeral_space_def_new(pParse,
                                                             old_def->name);
                         if (new_def == NULL) {

>> 29. Please, do not wrap '->'. And it is out of 80 symbols anyway.
> @@ -2997,8 +3001,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   	if (pList == 0) {
>   		Token prevCol;
>   		sqlite3TokenInit(&prevCol,
> -				 pTab->def->
> -					 fields[pTab->def->field_count - 1].name);
> +				 pTab->def->fields[
> +				 	pTab->def->field_count - 1].name);

7. Apply this:

@@ -2971,10 +2970,9 @@ sqlite3CreateIndex(Parse * pParse,       /* All information about this parse */
          */
         if (pList == 0) {
                 Token prevCol;
-               sqlite3TokenInit(&prevCol,
-                                pTab->def->fields[
-                                                  pTab->def->field_count - 1].
-                                                  name);
+               struct field_def *field =
+                       &pTab->def->fields[pTab->def->field_count - 1];
+               sqlite3TokenInit(&prevCol, field->name);
                 pList = sqlite3ExprListAppend(pParse, 0,
                                               sqlite3ExprAlloc(db, TK_ID,
                                                                &prevCol, 0));

> 
>> 38. region_alloc can fail - set
>> sqlite3OomFault().
>   	struct region *region = &fiber()->gc;
>   	pTable->def->fields =
>   		region_alloc(region, nCol * sizeof(pTable->def->fields[0]));
> +	if (pTable->def->fields == NULL) {
> +		sqlite3OomFault(db);
> +		goto cleanup;
> +	}
>   	memset(pTable->def->fields, 0, nCol * sizeof(pTable->def->fields[0]));
>   	/* NULL nullable_action should math is_nullable flag. */
>   	for (int i = 0; i < nCol; i++)
> @@ -1899,6 +1903,7 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
>   			pTable->def->fields[i].name[name_len] = '\0';
>   		}
>   	}
> +cleanup:

8. When you added cleanup label, you can reuse it for another OOM on line 1904.

9. Please, do not ignore comments.

>> -        *pnCol = 0;
>> -        return SQLITE_NOMEM_BKPT;
>> +        pTable->def->fields = NULL;
>> +        pTable->def->field_count = 0;

>39. pTable->def is on region and is not deleted together with table. You
>can skip this cleaning. 

Looks like it can be on malloc sometimes, so you should write a comment about it -
why do you need this cleaning.

>> 40. Out of 66.
>> 41. Will *be* overwritten.
>> 42. Same in the previous review - please, start a sentence from a capital
>> letter, and finish with dot.
> -			/* Will overwritten with pointer as unique identifier. */
> +			/*
> +			 * Will be overwritten with pointer as unique
> +			 * identifier.
> +			 * */

10. Still out of 66. And wrong end: '* */' must be '*/'.

>   			const char *name = "sqlite_sq_DEADBEAFDEADBEAF";
>   			pFrom->pTab = pTab =
>   				sql_ephemeral_table_new(pParse, name);
>   			if (pTab == NULL)
>   				return WRC_Abort;
> -			/* rewrite old name with correct pointer */
> +			/* Rewrite old name with correct pointer. */

11. Out of 66.

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

* [tarantool-patches] Re: [PATCH v5 2/3] sql: remove SQL fields from Table and Column
  2018-05-14 13:39       ` Vladislav Shpilevoy
@ 2018-05-15 15:56         ` Kirill Shcherbatov
  0 siblings, 0 replies; 13+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 15:56 UTC (permalink / raw)
  To: tarantool-patches

> 1. Wrong fix. As I can see sql_table_def_rebuild does not depend on allocation
> way of its arguments. So the function declaration comment must be fixed too.@@ -166,8 +166,7 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
 
 /**
  * Rebuild struct def in Table with memory allocated on a single
- * malloc. Fields and strings are expected to be allocated with
- * sqlite3DbMalloc.
+ * malloc.

> 2. OutOfMemory takes variable name in the last argument. Not caller function name.
> 3. Same.
@@ -661,7 +661,7 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
                diag_set(OutOfMemory, pName->n + 1,
-                        "region_alloc", "sqlite3AddColumn");
+                        "region_alloc", "z");
                pParse->rc = SQL_TARANTOOL_ERROR;
                pParse->nErr++;
                return;
@@ -871,7 +871,7 @@ sqlite3AddDefaultValue(Parse * pParse, ExprSpan * pSpan)
                                diag_set(OutOfMemory, default_length + 1,
                                         "region_alloc",
-                                        "sqlite3AddDefaultValue");
+                                        "field->default_value");

> 4. Looks like you removed sql_column_collation. It would be good to see the diff
> under the comment.
@@ -1053,45 +1030,13 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
-                       if (pIdx->aiColumn[0] == i)
-                               pIdx->coll_array[0] = sql_column_collation(p, i);
+                       if (pIdx->aiColumn[0] == i) {
+                               pIdx->coll_array[0] =
+                                       coll_by_id(p->def->fields[i].coll_id);
+                       }
                }

@@ -3066,7 +3074,7 @@ sqlite3CreateIndex(Parse * pParse,        /* All information about this parse */
-                       coll = sql_column_collation(pTab, j);
+                       coll = coll_by_id(pTab->def->fields[j].coll_id);

@@ -179,13 +182,14 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_found)
-                               coll = sql_column_collation(p->pTab, j);
+                               coll = coll_by_id(
+                                       p->space_def->fields[j].coll_id);


@@ -298,14 +299,14 @@ sqlite3FkLocateIndex(Parse * pParse,      /* Parse context to store any error in */
-                                       def_coll = sql_column_collation(pParent,
-                                                                       iCol);
+                                       def_coll = coll_by_id(
+                                               pParent->def->fields[iCol].coll_id);

@@ -298,14 +299,14 @@ sqlite3FkLocateIndex(Parse * pParse,      /* Parse context to store any error in */
-                                       def_coll = sql_column_collation(pParent,
-                                                                       iCol);
+                                       def_coll = coll_by_id(
+                                               pParent->def->fields[iCol].coll_id);

@@ -1784,26 +1795,29 @@ xferOptimization(Parse * pParse,        /* Parser context */
-               if (sql_column_collation(pDest, i) !=
-                   sql_column_collation(pSrc, i)) {
+               if (coll_by_id(pDest->def->fields[i].coll_id) !=
+                       coll_by_id(pSrc->def->fields[i].coll_id))

@@ -3529,8 +3499,6 @@ void sqlite3AddCollateType(Parse *, Token *);
-struct coll *
-sql_column_collation(Table *, uint32_t);

> 5. Out of 80 symbols. Please, do self-review of a patch before sending.
@@ -2110,7 +2110,8 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
-                       sqlite3ColumnsFromExprList(pParse, pTable->pCheck, pTable);
+                       sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
+                                                  pTable);

> 6. I looked at sqlite3ColumnsFromExprList, and looks like it does not use
> opts.temporary flag. So you can remove this opts.temporary flag manipulation, it is not?
@@ -1742,7 +1742,6 @@ sql_ephemeral_table_new(Parse *parser, const char *name)
 int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
-       assert(pTable->def->opts.temporary);
        struct space_def *old_def = pTable->def;

@@ -2108,12 +2108,9 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
                         * a VIEW it holds the list of column names.
                         */
                        struct space_def *old_def = pTable->def;
-                       /* Manage def memory manually. */
-                       old_def->opts.temporary = true;
                        sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
                                                   pTable);
                        if (sql_table_def_rebuild(db, pTable) != 0) {
-                               old_def->opts.temporary = false;


> 
> Here I meant this:
@@ -2131,8 +2131,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
-                       struct space_def *new_def;
-                       new_def =
+                       struct space_def *new_def =
                                sql_ephemeral_space_def_new(pParse,


> 7. Apply this:
@@ -2969,10 +2968,9 @@ sqlite3CreateIndex(Parse * pParse,       /* All information about this parse */
         */
        if (pList == 0) {
                Token prevCol;
-               sqlite3TokenInit(&prevCol,
-                                pTab->def->fields[
-                                                  pTab->def->field_count - 1].
-                                                  name);
+               struct field_def *field =
+                       &pTab->def->fields[pTab->def->field_count - 1];
+               sqlite3TokenInit(&prevCol, field->name);


> 8. When you added cleanup label, you can reuse it for another OOM on line 1904.
@@ -1842,7 +1842,7 @@ sqlite3ColumnsFromExprList(Parse * pParse,        /* Parsing context */
-       for (i = 0, pCol = aCol; i < nCol && !db->mallocFailed; i++, pCol++) {
+       for (i = 0, pCol = aCol; i < nCol; i++, pCol++) {

@@ -1902,6 +1902,7 @@ sqlite3ColumnsFromExprList(Parse * pParse,        /* Parsing context */
                if (pTable->def->fields[i].name == NULL) {
                        sqlite3OomFault(db);
+                       goto cleanup;
                } else {

> 9. Please, do not ignore comments.
> Looks like it can be on malloc sometimes, so you should write a comment about it -
> why do you need this cleaning.

@@ -1912,6 +1912,10 @@ cleanup:
        sqlite3HashClear(&ht);
        int rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_OK;
        if (rc != SQLITE_OK) {
+               /*
+                * pTable->def could be not temporal in
+                * sqlite3ViewGetColumnNames so we need clean-up.
+                */

> 10. Still out of 66. And wrong end: '* */' must be '*/'.@@ -4714,9 +4718,9 @@ selectExpander(Walker * pWalker, Select * p)
-                        * Will be overwritten with pointer as unique
-                        * identifier.
-                        * */
+                        * Will be overwritten with pointer as
+                        * unique identifier.
+                        */

> 11. Out of 66.
@@ -4729,15 +4732,17 @@ selectExpander(Walker * pWalker, Select * p)
-                       /* Rewrite old name with correct pointer. */
+                       /*
+                        * Rewrite old name with correct pointer.
+                        */

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

end of thread, other threads:[~2018-05-15 15:56 UTC | newest]

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

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