Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server
@ 2018-05-15 17:03 Kirill Shcherbatov
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 1/4] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 17:03 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

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

Kirill Shcherbatov (4):
  sql: fix code style in sqlite3Pragma
  sql: remove SQL fields from Table and Column
  sql: space_def* instead of Table* in Expr
  sql: remove Checks to server

 src/box/alter.cc            |  11 +
 src/box/field_def.c         |   1 +
 src/box/field_def.h         |  20 ++
 src/box/key_def.cc          |   2 +-
 src/box/opt_def.c           |  16 +-
 src/box/opt_def.h           |   8 +-
 src/box/space_def.c         | 135 +++++++++--
 src/box/space_def.h         |  24 ++
 src/box/sql.c               | 225 +++++++++++++++---
 src/box/sql.h               | 125 +++++++++-
 src/box/sql/alter.c         |  59 +++--
 src/box/sql/analyze.c       |  18 +-
 src/box/sql/build.c         | 553 +++++++++++++++++++++++---------------------
 src/box/sql/delete.c        |  31 +--
 src/box/sql/expr.c          | 215 ++++++++---------
 src/box/sql/fkey.c          |  71 +++---
 src/box/sql/hash.c          |  10 +-
 src/box/sql/hash.h          |   2 +-
 src/box/sql/insert.c        | 138 ++++++-----
 src/box/sql/parse.y         |  86 +++----
 src/box/sql/pragma.c        |  96 ++++----
 src/box/sql/pragma.h        |   2 -
 src/box/sql/prepare.c       |  52 +++--
 src/box/sql/resolve.c       |  71 +++---
 src/box/sql/select.c        | 245 ++++++++++++--------
 src/box/sql/sqliteInt.h     |  81 ++-----
 src/box/sql/tokenize.c      |  12 +-
 src/box/sql/treeview.c      |   2 +-
 src/box/sql/trigger.c       |  20 +-
 src/box/sql/update.c        |  60 ++---
 src/box/sql/util.c          |   9 -
 src/box/sql/vdbe.c          |  28 +--
 src/box/sql/vdbeaux.c       |  34 ---
 src/box/sql/vdbemem.c       |  18 +-
 src/box/sql/where.c         |  27 ++-
 src/box/sql/wherecode.c     |  53 +++--
 src/box/sql/whereexpr.c     |  19 +-
 test/sql-tap/check.test.lua |  13 +-
 38 files changed, 1540 insertions(+), 1052 deletions(-)

-- 
2.7.4

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

* [tarantool-patches] [PATCH v6 1/4] sql: fix code style in sqlite3Pragma
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
@ 2018-05-15 17:03 ` Kirill Shcherbatov
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 2/4] sql: remove SQL fields from Table and Column Kirill Shcherbatov
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 17:03 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

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

diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index e41f69b..2f2878b 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -544,37 +544,33 @@ 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;
+		Table *pTab = sqlite3HashFind(&db->pSchema->tblHash, zRight);
+		if (pTab == NULL)
 			break;
+		FKey *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] 20+ messages in thread

* [tarantool-patches] [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 1/4] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
@ 2018-05-15 17:03 ` Kirill Shcherbatov
  2018-05-17 17:25   ` [tarantool-patches] " n.pettik
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 3/4] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 17:03 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     |  38 ++---
 src/box/space_def.h     |  18 +++
 src/box/sql.c           | 126 +++++++++++++----
 src/box/sql.h           |  32 +++++
 src/box/sql/alter.c     |  57 +++++---
 src/box/sql/analyze.c   |  16 ++-
 src/box/sql/build.c     | 362 ++++++++++++++++++++++++++----------------------
 src/box/sql/delete.c    |  21 +--
 src/box/sql/expr.c      |  14 +-
 src/box/sql/fkey.c      |  38 +++--
 src/box/sql/hash.c      |  10 +-
 src/box/sql/hash.h      |   2 +-
 src/box/sql/insert.c    |  69 ++++-----
 src/box/sql/pragma.c    |  34 +++--
 src/box/sql/prepare.c   |  44 +++---
 src/box/sql/resolve.c   |  22 +--
 src/box/sql/select.c    | 176 ++++++++++++++---------
 src/box/sql/sqliteInt.h |  38 +++--
 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 +-
 29 files changed, 726 insertions(+), 486 deletions(-)

diff --git a/src/box/field_def.h b/src/box/field_def.h
index dfc1950..cfb0d13 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_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..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)
@@ -70,11 +57,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,10 +104,10 @@ 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);
 				assert(e != NULL);
@@ -201,10 +189,10 @@ 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);
 				assert(e != NULL);
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);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
diff --git a/src/box/sql.c b/src/box/sql.c
index 166bb71..8c7a45e 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_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_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,63 @@ 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;
+	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),
+			"region_alloc", "sql_ephemeral_space_def_new");
+		parser->rc = SQL_TARANTOOL_ERROR;
+		parser->nErr++;
+		return NULL;
+	}
+
+	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)
+{
+	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..3c26492 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -65,6 +65,7 @@ sql_get();
 struct Expr;
 struct Parse;
 struct Select;
+struct Table;
 
 /**
  * Perform parsing of provided expression. This is done by
@@ -143,6 +144,37 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer);
 void
 sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
 
+/**
+ * Create and initialize a new ephemeral 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 ephemeral 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.
+ * @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..11d4dc7 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,13 @@ 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_is_nullable(
+			       pNew->def->fields[pNew->def->field_count -
+						 1].nullable_action));
+
+	if (!pNew->def->fields[pNew->def->field_count - 1].is_nullable
+	    && pDflt == NULL) {
 		sqlite3ErrorMsg(pParse,
 				"Cannot add a NOT NULL column with default value NULL");
 		return;
@@ -227,7 +237,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 +258,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 +294,31 @@ 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);
+		sqlite3OomFault(db);
+		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..6139e65 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -285,24 +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)
-{
-	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);
-	}
-}
-
-/*
  * Return true if given column is part of primary key.
  * If field number is less than 63, corresponding bit
  * in column mask is tested. Otherwise, check whether 64-th bit
@@ -388,17 +370,14 @@ deleteTable(sqlite3 * db, Table * pTable)
 	/* Delete the Table structure itself.
 	 */
 	sqlite3HashClear(&pTable->idxHash);
-	sqlite3DeleteColumnNames(db, pTable);
-	sqlite3DbFree(db, pTable->zName);
+	sqlite3DbFree(db, pTable->aCol);
 	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 +473,19 @@ sqlite3PrimaryKeyIndex(Table * pTab)
 
 /**
  * Create and initialize a new SQL Table object.
+ * All memory except table object itself 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 +535,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 +594,33 @@ sqlite3StartTable(Parse *pParse, Token *pName, int noErr)
 static struct field_def *
 sql_field_retrieve(Parse *parser, Table *table, uint32_t id)
 {
-	sqlite3 *db = parser->db;
 	struct field_def *field;
 	assert(table->def != NULL);
-	assert(table->def->exact_field_count >= (uint32_t)table->nCol);
 	assert(id < SQLITE_MAX_COLUMN);
 
 	if (id >= table->def->exact_field_count) {
-		uint32_t columns = table->def->exact_field_count;
-		columns = (columns > 0) ? 2 * columns : 1;
-		field = sqlite3DbRealloc(db, table->def->fields,
-					 columns * sizeof(table->def->fields[0]));
+		uint32_t columns_new = table->def->exact_field_count;
+		columns_new = (columns_new > 0) ? 2 * columns_new : 1;
+		struct region *region = &fiber()->gc;
+		field = region_alloc(region, columns_new *
+				     sizeof(table->def->fields[0]));
 		if (field == NULL) {
-			parser->rc = SQLITE_NOMEM_BKPT;
+			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++) {
+		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));
 		}
 
 		table->def->fields = field;
-		table->def->exact_field_count = columns;
+		table->def->exact_field_count = columns_new;
 	}
 
 	field = &table->def->fields[id];
@@ -675,42 +646,50 @@ 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);
-	if (z == 0)
+	struct region *region = &fiber()->gc;
+	z = region_alloc(region, pName->n + 1);
+	if (z == NULL) {
+		diag_set(OutOfMemory, pName->n + 1,
+			 "region_alloc", "z");
+		pParse->rc = SQL_TARANTOOL_ERROR;
+		pParse->nErr++;
 		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 +697,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 +707,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 +734,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 =
+		nullable_action_is_nullable(onError);
 }
 
 /*
@@ -871,38 +851,34 @@ 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) {
+				diag_set(OutOfMemory, default_length + 1,
+					 "region_alloc",
+					 "field->default_value");
+				pParse->rc = SQL_TARANTOOL_ERROR;
+				pParse->nErr++;
+				return;
+			}
+			strncpy(field->default_value, pSpan->zStart,
+				default_length);
+			field->default_value[default_length] = '\0';
 		}
 	}
 	sql_expr_free(db, pSpan->pExpr, false);
@@ -942,12 +918,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 +938,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 +949,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 +1013,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 +1033,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 +1063,8 @@ 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);
+		return coll_by_id(table->def->fields[column].coll_id);
 	}
 
 	return space->format->fields[column].coll;
@@ -1306,10 +1282,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 +1294,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 +1302,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 +1318,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 +1348,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 +1368,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 +1409,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 +1424,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 +1638,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 +1704,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 +1831,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
@@ -1862,18 +1839,23 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	 */
 	if (db->init.busy)
 		p->tnum = db->init.newTnum;
+	p->def->id = SQLITE_PAGENO_TO_SPACEID(p->tnum);
 
-	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 +1890,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 +1920,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 +1934,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 +1951,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 +1971,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 +1981,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 +2034,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 +2088,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 +2106,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 +2121,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 +2137,17 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * normally holds CHECK constraints on an ordinary table, but for
 			 * a VIEW it holds the list of column names.
 			 */
+			struct space_def *old_def = pTable->def;
 			sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
-						   &pTable->nCol,
-						   &pTable->aCol);
+						   pTable);
+			if (sql_table_def_rebuild(db, pTable) != 0) {
+				nErr++;
+			} else {
+				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 +2157,34 @@ 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 =
+				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 +2209,25 @@ sqliteViewResetAll(sqlite3 * db)
 	for (i = sqliteHashFirst(&db->pSchema->tblHash); i;
 	     i = sqliteHashNext(i)) {
 		Table *pTab = sqliteHashData(i);
-		if (pTab->pSelect) {
-			sqlite3DeleteColumnNames(db, pTab);
+		assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
+		if (pTab->def->opts.is_view) {
+			sqlite3DbFree(db, pTab->aCol);
+			struct space_def *old_def = pTab->def;
+			assert(old_def->opts.temporary == false);
+			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);
+			if (pTab->def == NULL) {
+				sqlite3OomFault(db);
+				pTab->def = old_def;
+			} else {
+				space_def_delete(old_def);
+			}
 			pTab->aCol = 0;
-			pTab->nCol = 0;
 		}
 	}
 }
@@ -2469,13 +2500,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 +2539,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 +2935,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 +2972,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 +2985,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 +2998,9 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	 */
 	if (pList == 0) {
 		Token prevCol;
-		sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol - 1].zName);
+		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));
@@ -4018,10 +4052,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 +4215,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..e83e0d8 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,8 @@ sqlite3HashInsert(Hash * pH, const char *pKey, void *data)
 			removeElementGivenHash(pH, elem, h);
 		} else {
 			elem->data = data;
-			elem->pKey = pKey;
+			assert(elem->pKey != NULL);
+			assert(strcmp(elem->pKey, pKey) == 0);
 		}
 		return old_data;
 	}
@@ -301,7 +303,11 @@ 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);
+	if (new_elem->pKey == NULL) {
+		sqlite3_free(new_elem);
+		return data;
+	}
 	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..24b0ce6 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,15 @@ 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 +448,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 +467,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 +488,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 +524,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 +613,11 @@ 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 +685,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 +736,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 +763,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 +884,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 +1098,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 +1147,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 +1202,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 +1287,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 +1316,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 +1396,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 +1791,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 2f2878b..6293599 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;
@@ -557,7 +561,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,
@@ -612,11 +616,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 =
@@ -675,7 +680,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..f949709 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_destroy(&sParse);
 	rc = sqlite3ApiExit(db, rc);
 	assert((rc & db->errMask) == rc);
 	return rc;
@@ -456,3 +435,20 @@ sqlite3_prepare_v2(sqlite3 * db,	/* Database handle. */
 	assert(rc == SQLITE_OK || ppStmt == 0 || *ppStmt == 0);	/* VERIFY: F13021 */
 	return rc;
 }
+
+void
+sql_parser_destroy(Parse *parser)
+{
+	assert(parser != NULL);
+	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..e08d709 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,13 +1818,31 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
 		testcase(aCol == 0);
 	} else {
 		nCol = 0;
-		aCol = 0;
+		aCol = NULL;
 	}
 	assert(nCol == (i16) nCol);
-	*pnCol = nCol;
-	*paCol = aCol;
 
-	for (i = 0, pCol = aCol; i < nCol && !db->mallocFailed; i++, pCol++) {
+	/*
+	 * This should be a table without resolved columns.
+	 * sqlite3ViewGetColumnNames could use it to resolve
+	 * names for existing table.
+	 */
+	assert(pTable->def->fields == NULL);
+	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++)
+		pTable->def->fields[i].is_nullable = true;
+	pTable->def->field_count = (uint32_t)nCol;
+	pTable->aCol = aCol;
+
+	for (i = 0, pCol = aCol; i < nCol; i++, pCol++) {
 		/* Get an appropriate name for the column
 		 */
 		p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
@@ -1845,7 +1862,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 +1891,34 @@ 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);
+			goto cleanup;
+		} else {
+			memcpy(pTable->def->fields[i].name, zName, name_len);
+			pTable->def->fields[i].name[name_len] = '\0';
 		}
 	}
+cleanup:
 	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) {
+		/*
+		 * pTable->def could be not temporal in
+		 * sqlite3ViewGetColumnNames so we need clean-up.
+		 */
 		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 +1947,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 +1976,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 +1995,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 +4526,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 +4591,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 +4711,29 @@ selectExpander(Walker * pWalker, Select * p)
 			assert(pFrom->pTab == 0);
 			if (sqlite3WalkSelect(pWalker, pSel))
 				return WRC_Abort;
+			/*
+			 * Will be 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 +4748,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 +4762,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 +4860,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 +4876,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 +5306,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 +5412,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 +5533,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 +5568,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 +5582,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..772fa26 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);
@@ -3513,9 +3503,8 @@ int sqlite3InitCallback(void *, int, char **, char **);
 void sqlite3Pragma(Parse *, Token *, Token *, Token *, int);
 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 +3964,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 +4141,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_destroy(struct Parse *parser);
+
 #endif				/* SQLITEINT_H */
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index c77aa9b..279c3af 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_destroy(&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..0845d22 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_destroy(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..aad1b37 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_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_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] 20+ messages in thread

* [tarantool-patches] [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 1/4] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 2/4] sql: remove SQL fields from Table and Column Kirill Shcherbatov
@ 2018-05-15 17:03 ` Kirill Shcherbatov
  2018-05-16 12:33   ` [tarantool-patches] " Vladislav Shpilevoy
       [not found]   ` <26E4269B-2BCB-42C3-8216-D51E290E4723@corp.mail.ru>
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server Kirill Shcherbatov
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 17:03 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           |  20 +++---
 src/box/sql/alter.c     |   2 +-
 src/box/sql/analyze.c   |   2 +-
 src/box/sql/build.c     | 100 +++++++++++-------------------
 src/box/sql/delete.c    |   4 +-
 src/box/sql/expr.c      | 158 ++++++++++++++++++++++++++----------------------
 src/box/sql/fkey.c      |  18 +++---
 src/box/sql/insert.c    |  47 ++++++++------
 src/box/sql/pragma.c    |  10 +--
 src/box/sql/resolve.c   |  12 ++--
 src/box/sql/select.c    |  26 +++++---
 src/box/sql/sqliteInt.h |  44 +++-----------
 src/box/sql/update.c    |  35 +++++------
 src/box/sql/vdbe.c      |  26 ++++----
 src/box/sql/vdbeaux.c   |  37 ------------
 src/box/sql/vdbemem.c   |  18 +++---
 src/box/sql/where.c     |  15 +++--
 src/box/sql/wherecode.c |  35 ++++++-----
 src/box/sql/whereexpr.c |   6 +-
 21 files changed, 293 insertions(+), 337 deletions(-)

diff --git a/src/box/field_def.c b/src/box/field_def.c
index 010b3b7..4d39d03 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 = AFFINITY_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 cfb0d13..7da06c0 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 {
+	AFFINITY_UNDEFINED = 0,
+	AFFINITY_BLOB = 'A',
+	AFFINITY_TEXT = 'B',
+	AFFINITY_NUMERIC = 'C',
+	AFFINITY_INTEGER = 'D',
+	AFFINITY_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 8c7a45e..357cbf9 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1402,17 +1402,17 @@ static const char *convertSqliteAffinity(int affinity, bool allow_nulls)
 	switch (affinity) {
 	default:
 		assert(false);
-	case SQLITE_AFF_BLOB:
+	case AFFINITY_BLOB:
 		return "scalar";
-	case SQLITE_AFF_TEXT:
+	case AFFINITY_TEXT:
 		return "string";
-	case SQLITE_AFF_NUMERIC:
-	case SQLITE_AFF_REAL:
+	case AFFINITY_NUMERIC:
+	case AFFINITY_REAL:
 	  /* Tarantool workaround: to make comparators able to compare, e.g.
 	     double and int use generic type. This might be a performance issue.  */
 	  /* return "number"; */
 		return "scalar";
-	case SQLITE_AFF_INTEGER:
+	case AFFINITY_INTEGER:
 	  /* See comment above.  */
 	  /* return "integer"; */
 		return "scalar";
@@ -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 == AFFINITY_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/alter.c b/src/box/sql/alter.c
index 11d4dc7..bd8009c 100644
--- a/src/box/sql/alter.c
+++ b/src/box/sql/alter.c
@@ -218,7 +218,7 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
 		sqlite3_value *pVal = 0;
 		int rc;
 		rc = sqlite3ValueFromExpr(db, pDflt,
-					  SQLITE_AFF_BLOB, &pVal);
+					  AFFINITY_BLOB, &pVal);
 		assert(rc == SQLITE_OK || rc == SQLITE_NOMEM);
 		if (rc != SQLITE_OK) {
 			assert(db->mallocFailed == 1);
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 2722c08..5dee1e8 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -1042,7 +1042,7 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
 
 		/* Add the entry to the stat1 table. */
 		callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
-		assert("BBB"[0] == SQLITE_AFF_TEXT);
+		assert("BBB"[0] == AFFINITY_TEXT);
 		sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp,
 				  "BBB", 0);
 		sqlite3VdbeAddOp2(v, OP_IdxInsert, iStatCur, regTemp);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 6139e65..718809d 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -696,7 +696,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 = AFFINITY_BLOB;
 		pCol->szEst = 1;
 		column_def->type = FIELD_TYPE_SCALAR;
 	} else {
@@ -707,14 +707,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  = AFFINITY_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;
 		}
@@ -770,7 +770,7 @@ char
 sqlite3AffinityType(const char *zIn, u8 * pszEst)
 {
 	u32 h = 0;
-	char aff = SQLITE_AFF_NUMERIC;
+	char aff = AFFINITY_NUMERIC;
 	const char *zChar = 0;
 
 	assert(zIn != 0);
@@ -778,31 +778,31 @@ sqlite3AffinityType(const char *zIn, u8 * pszEst)
 		h = (h << 8) + sqlite3UpperToLower[(*zIn) & 0xff];
 		zIn++;
 		if (h == (('c' << 24) + ('h' << 16) + ('a' << 8) + 'r')) {	/* CHAR */
-			aff = SQLITE_AFF_TEXT;
+			aff = AFFINITY_TEXT;
 			zChar = zIn;
 		} else if (h == (('c' << 24) + ('l' << 16) + ('o' << 8) + 'b')) {	/* CLOB */
-			aff = SQLITE_AFF_TEXT;
+			aff = AFFINITY_TEXT;
 		} else if (h == (('t' << 24) + ('e' << 16) + ('x' << 8) + 't')) {	/* TEXT */
-			aff = SQLITE_AFF_TEXT;
+			aff = AFFINITY_TEXT;
 		} else if (h == (('b' << 24) + ('l' << 16) + ('o' << 8) + 'b')	/* BLOB */
-			   &&(aff == SQLITE_AFF_NUMERIC
-			      || aff == SQLITE_AFF_REAL)) {
-			aff = SQLITE_AFF_BLOB;
+			   &&(aff == AFFINITY_NUMERIC
+			      || aff == AFFINITY_REAL)) {
+			aff = AFFINITY_BLOB;
 			if (zIn[0] == '(')
 				zChar = zIn;
 #ifndef SQLITE_OMIT_FLOATING_POINT
 		} else if (h == (('r' << 24) + ('e' << 16) + ('a' << 8) + 'l')	/* REAL */
-			   &&aff == SQLITE_AFF_NUMERIC) {
-			aff = SQLITE_AFF_REAL;
+			   &&aff == AFFINITY_NUMERIC) {
+			aff = AFFINITY_REAL;
 		} else if (h == (('f' << 24) + ('l' << 16) + ('o' << 8) + 'a')	/* FLOA */
-			   &&aff == SQLITE_AFF_NUMERIC) {
-			aff = SQLITE_AFF_REAL;
+			   &&aff == AFFINITY_NUMERIC) {
+			aff = AFFINITY_REAL;
 		} else if (h == (('d' << 24) + ('o' << 16) + ('u' << 8) + 'b')	/* DOUB */
-			   &&aff == SQLITE_AFF_NUMERIC) {
-			aff = SQLITE_AFF_REAL;
+			   &&aff == AFFINITY_NUMERIC) {
+			aff = AFFINITY_REAL;
 #endif
 		} else if ((h & 0x00FFFFFF) == (('i' << 16) + ('n' << 8) + 't')) {	/* INT */
-			aff = SQLITE_AFF_INTEGER;
+			aff = AFFINITY_INTEGER;
 			break;
 		}
 	}
@@ -813,7 +813,7 @@ sqlite3AffinityType(const char *zIn, u8 * pszEst)
 	if (pszEst) {
 		*pszEst = 1;	/* default size is approx 4 bytes
 		*/
-		if (aff < SQLITE_AFF_NUMERIC) {
+		if (aff < AFFINITY_NUMERIC) {
 			if (zChar) {
 				while (zChar[0]) {
 					if (sqlite3Isdigit(zChar[0])) {
@@ -1030,47 +1030,16 @@ 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] =
+					coll_by_id(p->def->fields[i].coll_id);
+			}
 		}
 	}
 	sqlite3DbFree(db, zColl);
 }
 
 /**
- * Return collation of given column from table.
- *
- * @param table Table which is used to fetch column.
- * @param column Number of column.
- * @retval Pointer to collation.
- */
-struct coll *
-sql_column_collation(Table *table, uint32_t column)
-{
-	assert(table != NULL);
-	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(table->tnum);
-	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)table->def->field_count);
-		return coll_by_id(table->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.
@@ -1319,18 +1288,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 - AFFINITY_BLOB >= 0);
+		assert(affinity - AFFINITY_BLOB < ArraySize(azType));
+		testcase(affinity == AFFINITY_BLOB);
+		testcase(affinity == AFFINITY_TEXT);
+		testcase(affinity == AFFINITY_NUMERIC);
+		testcase(affinity == AFFINITY_INTEGER);
+		testcase(affinity == AFFINITY_REAL);
+
+		zType = azType[affinity - AFFINITY_BLOB];
 		len = sqlite3Strlen30(zType);
-		assert(pCol->affinity == SQLITE_AFF_BLOB
-		       || pCol->affinity == sqlite3AffinityType(zType, 0));
+		assert(affinity == AFFINITY_BLOB
+		       || affinity == sqlite3AffinityType(zType, 0));
 		memcpy(&zStmt[k], zType, len);
 		k += len;
 		assert(k <= n);
@@ -3100,7 +3070,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 				goto exit_create_index;
 			}
 		} else if (j >= 0) {
-			coll = sql_column_collation(pTab, j);
+			coll = coll_by_id(pTab->def->fields[j].coll_id);
 		} 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..9a8f045 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"
 
@@ -46,10 +47,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 :
+	       AFFINITY_INTEGER;
 }
 
 /*
@@ -90,7 +92,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 +182,14 @@ 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 = coll_by_id(
+					p->space_def->fields[j].coll_id);
 				*is_found = true;
 			}
 			break;
@@ -244,15 +248,15 @@ sqlite3CompareAffinity(Expr * pExpr, char aff2)
 		 */
 		if (sqlite3IsNumericAffinity(aff1)
 		    || sqlite3IsNumericAffinity(aff2)) {
-			return SQLITE_AFF_NUMERIC;
+			return AFFINITY_NUMERIC;
 		} else {
-			return SQLITE_AFF_BLOB;
+			return AFFINITY_BLOB;
 		}
 	} else if (!aff1 && !aff2) {
 		/* Neither side of the comparison is a column.  Compare the
 		 * results directly.
 		 */
-		return SQLITE_AFF_BLOB;
+		return AFFINITY_BLOB;
 	} else {
 		/* One side is a column, the other is not. Use the columns affinity. */
 		assert(aff1 == 0 || aff2 == 0);
@@ -281,7 +285,7 @@ comparisonAffinity(Expr * pExpr)
 		    sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr,
 					   aff);
 	} else if (NEVER(aff == 0)) {
-		aff = SQLITE_AFF_BLOB;
+		aff = AFFINITY_BLOB;
 	}
 	return aff;
 }
@@ -297,10 +301,10 @@ sqlite3IndexAffinityOk(Expr * pExpr, char idx_affinity)
 {
 	char aff = comparisonAffinity(pExpr);
 	switch (aff) {
-	case SQLITE_AFF_BLOB:
+	case AFFINITY_BLOB:
 		return 1;
-	case SQLITE_AFF_TEXT:
-		return idx_affinity == SQLITE_AFF_TEXT;
+	case AFFINITY_TEXT:
+		return idx_affinity == AFFINITY_TEXT;
 	default:
 		return sqlite3IsNumericAffinity(idx_affinity);
 	}
@@ -2132,10 +2136,10 @@ 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));
+		        && p->space_def->fields[p->iColumn].is_nullable);
 	default:
 		return 1;
 	}
@@ -2155,7 +2159,7 @@ int
 sqlite3ExprNeedsNoAffinityChange(const Expr * p, char aff)
 {
 	u8 op;
-	if (aff == SQLITE_AFF_BLOB)
+	if (aff == AFFINITY_BLOB)
 		return 1;
 	while (p->op == TK_UPLUS || p->op == TK_UMINUS) {
 		p = p->pLeft;
@@ -2165,15 +2169,15 @@ sqlite3ExprNeedsNoAffinityChange(const Expr * p, char aff)
 		op = p->op2;
 	switch (op) {
 	case TK_INTEGER:{
-			return aff == SQLITE_AFF_INTEGER
-			    || aff == SQLITE_AFF_NUMERIC;
+			return aff == AFFINITY_INTEGER
+			    || aff == AFFINITY_NUMERIC;
 		}
 	case TK_FLOAT:{
-			return aff == SQLITE_AFF_REAL
-			    || aff == SQLITE_AFF_NUMERIC;
+			return aff == AFFINITY_REAL
+			    || aff == AFFINITY_NUMERIC;
 		}
 	case TK_STRING:{
-			return aff == SQLITE_AFF_TEXT;
+			return aff == AFFINITY_TEXT;
 		}
 	case TK_BLOB:{
 			return 1;
@@ -2181,8 +2185,8 @@ sqlite3ExprNeedsNoAffinityChange(const Expr * p, char aff)
 	case TK_COLUMN:{
 			assert(p->iTable >= 0);	/* p cannot be part of a CHECK constraint */
 			return p->iColumn < 0
-			    && (aff == SQLITE_AFF_INTEGER
-				|| aff == SQLITE_AFF_NUMERIC);
+			    && (aff == AFFINITY_INTEGER
+				|| aff == AFFINITY_NUMERIC);
 		}
 	default:{
 			return 0;
@@ -2435,20 +2439,22 @@ 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);
+			testcase(cmpaff == AFFINITY_BLOB);
+			testcase(cmpaff == AFFINITY_TEXT);
 			switch (cmpaff) {
-			case SQLITE_AFF_BLOB:
+			case AFFINITY_BLOB:
 				break;
-			case SQLITE_AFF_TEXT:
+			case AFFINITY_TEXT:
 				/* sqlite3CompareAffinity() only returns TEXT if one side or the
 				 * other has no affinity and the other side is TEXT.  Hence,
 				 * the only way for cmpaff to be TEXT is for idxaff to be TEXT
 				 * and for the term on the LHS of the IN to have no affinity.
 				 */
-				assert(idxaff == SQLITE_AFF_TEXT);
+				assert(idxaff == AFFINITY_TEXT);
 				break;
 			default:
 				affinity_ok = sqlite3IsNumericAffinity(idxaff);
@@ -2830,7 +2836,7 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 
 				affinity = sqlite3ExprAffinity(pLeft);
 				if (!affinity) {
-					affinity = SQLITE_AFF_BLOB;
+					affinity = AFFINITY_BLOB;
 				}
 				if (pKeyInfo) {
 					bool unused;
@@ -3175,8 +3181,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 == AFFINITY_INTEGER
 		    && pk->aiColumn[0] < nVector) {
 			int reg_pk = rLhs + pk->aiColumn[0];
 			sqlite3VdbeAddOp2(v, OP_MustBeInt, reg_pk, destIfFalse);
@@ -3519,45 +3526,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 +3582,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 +3591,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 +3793,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 +4257,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 +4281,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 == AFFINITY_REAL) {
 				sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
 			}
 #endif
@@ -5440,8 +5454,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..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,
-									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)
@@ -526,20 +527,19 @@ 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 {
 			pExpr->iTable = regBase;
-			pExpr->affinity = SQLITE_AFF_INTEGER;
+			pExpr->affinity = AFFINITY_INTEGER;
 		}
 	}
 	return pExpr;
@@ -551,14 +551,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 +671,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 24b0ce6..1a883e2 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"
 
 /*
@@ -90,7 +91,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 +100,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);
@@ -109,7 +111,7 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * pIdx)
 				    sqlite3ExprAffinity(pIdx->aColExpr->a[n].
 							pExpr);
 				if (aff == 0)
-					aff = SQLITE_AFF_BLOB;
+					aff = AFFINITY_BLOB;
 				pIdx->zColAff[n] = aff;
 			}
 		}
@@ -155,11 +157,12 @@ 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;
-		} while (i >= 0 && zColAff[i] == SQLITE_AFF_BLOB);
+		} while (i >= 0 && zColAff[i] == AFFINITY_BLOB);
 		pTab->zColAff = zColAff;
 	}
 	i = sqlite3Strlen30(zColAff);
@@ -1117,7 +1120,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 (pTab->def->fields[i].is_nullable
 		    || (pTab->tabFlags & TF_Autoincrement
 			&& pTab->iAutoIncPKey == i))
 			continue;	/* This column is allowed to be NULL */
@@ -1300,7 +1303,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			 * not as affinity. Emit code for type checking */
 			if (nIdxCol == 1) {
 				reg_pk = regNewData + 1 + pIdx->aiColumn[0];
-				if (pTab->zColAff[pIdx->aiColumn[0]] == 'D') {
+				if (pTab->zColAff[pIdx->aiColumn[0]] ==
+				    AFFINITY_INTEGER) {
 					int skip_if_null = sqlite3VdbeMakeLabel(v);
 					if ((pTab->tabFlags & TF_Autoincrement) != 0) {
 						sqlite3VdbeAddOp2(v, OP_IsNull,
@@ -1798,19 +1802,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 (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 (!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) {
 			uint32_t src_space_id =
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 6293599..c5ae5cb 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 is_nullable =
+						pTab->def->fields[i].
+							is_nullable;
 					uint32_t space_id =
 						SQLITE_PAGENO_TO_SPACEID(
 							pTab->tnum);
@@ -388,7 +390,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 					sqlite3VdbeMultiLoad(v, 1, "issisi",
 							     i, name,
 							     field_type_strs[type],
-							     nullable == 0,
+							     is_nullable == false,
 							     expr_str, k);
 					sqlite3VdbeAddOp2(v, OP_ResultRow, 1,
 							  6);
@@ -689,7 +691,7 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 									  iKey,
 									  regRow);
 							sqlite3ColumnDefault(v,
-									     pTab,
+									     pTab->def,
 									     iKey,
 									     regRow);
 							sqlite3VdbeAddOp2(v,
@@ -706,7 +708,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..bf729b7 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) {
@@ -347,7 +347,7 @@ lookupName(Parse * pParse,	/* The parsing context */
 					cnt++;
 					if (iCol < 0) {
 						pExpr->affinity =
-						    SQLITE_AFF_INTEGER;
+						    AFFINITY_INTEGER;
 					} else if (pExpr->iTable == 0) {
 						testcase(iCol == 31);
 						testcase(iCol == 32);
@@ -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 e08d709..48aaffc 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
@@ -1850,19 +1850,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;
@@ -1960,11 +1964,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 = AFFINITY_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)
@@ -5948,7 +5954,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 772fa26..75699ae 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,27 +1878,7 @@ 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)
+#define sqlite3IsNumericAffinity(X)  ((X)>=AFFINITY_NUMERIC)
 
 /*
  * The SQLITE_AFF_MASK values masks off the significant bits of an
@@ -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;
 };
 
 /*
@@ -3518,8 +3499,6 @@ void sqlite3AddCollateType(Parse *, Token *);
 
 const char *
 column_collation_name(Table *, uint32_t);
-struct coll *
-sql_column_collation(Table *, uint32_t);
 const char *
 index_collation_name(Index *, uint32_t);
 struct coll *
@@ -3606,9 +3585,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 *);
@@ -3801,7 +3780,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 *);
@@ -3889,7 +3868,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 *);
@@ -4138,9 +4117,6 @@ extern int sqlite3InitDatabase(sqlite3 * db);
 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);
-
 /**
  * Initialize a new parser object.
  * @param parser object to initialize.
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 5f5807c..635b2d6 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 == AFFINITY_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/vdbe.c b/src/box/sql/vdbe.c
index dc779b4..e4951c2 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -325,9 +325,9 @@ applyAffinity(
 	char affinity       /* The affinity to be applied */
 	)
 {
-	if (affinity>=SQLITE_AFF_NUMERIC) {
-		assert(affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
-			|| affinity==SQLITE_AFF_NUMERIC);
+	if (affinity>=AFFINITY_NUMERIC) {
+		assert(affinity==AFFINITY_INTEGER || affinity==AFFINITY_REAL
+			|| affinity==AFFINITY_NUMERIC);
 		if ((pRec->flags & MEM_Int)==0) { /*OPTIMIZATION-IF-FALSE*/
 			if ((pRec->flags & MEM_Real)==0) {
 				if (pRec->flags & MEM_Str) applyNumericAffinity(pRec,1);
@@ -335,7 +335,7 @@ applyAffinity(
 				sqlite3VdbeIntegerAffinity(pRec);
 			}
 		}
-	} else if (affinity==SQLITE_AFF_TEXT) {
+	} else if (affinity==AFFINITY_TEXT) {
 		/* Only attempt the conversion to TEXT if there is an integer or real
 		 * representation (blob and NULL do not get converted) but no string
 		 * representation.  It would be harmless to repeat the conversion if
@@ -1887,7 +1887,7 @@ case OP_AddImm: {            /* in1 */
 case OP_MustBeInt: {            /* jump, in1 */
 	pIn1 = &aMem[pOp->p1];
 	if ((pIn1->flags & MEM_Int)==0) {
-		applyAffinity(pIn1, SQLITE_AFF_NUMERIC);
+		applyAffinity(pIn1, AFFINITY_NUMERIC);
 		VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
 		if ((pIn1->flags & MEM_Int)==0) {
 			if (pOp->p2==0) {
@@ -1938,12 +1938,12 @@ case OP_RealAffinity: {                  /* in1 */
  * A NULL value is not changed by this routine.  It remains NULL.
  */
 case OP_Cast: {                  /* in1 */
-	assert(pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL);
-	testcase( pOp->p2==SQLITE_AFF_TEXT);
-	testcase( pOp->p2==SQLITE_AFF_BLOB);
-	testcase( pOp->p2==SQLITE_AFF_NUMERIC);
-	testcase( pOp->p2==SQLITE_AFF_INTEGER);
-	testcase( pOp->p2==SQLITE_AFF_REAL);
+	assert(pOp->p2>=AFFINITY_BLOB && pOp->p2<=AFFINITY_REAL);
+	testcase( pOp->p2==AFFINITY_TEXT);
+	testcase( pOp->p2==AFFINITY_BLOB);
+	testcase( pOp->p2==AFFINITY_NUMERIC);
+	testcase( pOp->p2==AFFINITY_INTEGER);
+	testcase( pOp->p2==AFFINITY_REAL);
 	pIn1 = &aMem[pOp->p1];
 	memAboutToChange(p, pIn1);
 	rc = ExpandBlob(pIn1);
@@ -2104,7 +2104,7 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
 	} else {
 		/* Neither operand is NULL.  Do a comparison. */
 		affinity = pOp->p5 & SQLITE_AFF_MASK;
-		if (affinity>=SQLITE_AFF_NUMERIC) {
+		if (affinity>=AFFINITY_NUMERIC) {
 			if ((flags1 | flags3)&MEM_Str) {
 				if ((flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str) {
 					applyNumericAffinity(pIn1,0);
@@ -2124,7 +2124,7 @@ case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
 				res = 0;
 				goto compare_op;
 			}
-		} else if (affinity==SQLITE_AFF_TEXT) {
+		} else if (affinity==AFFINITY_TEXT) {
 			if ((flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0) {
 				testcase( pIn1->flags & MEM_Int);
 				testcase( pIn1->flags & MEM_Real);
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index aad1b37..4e3b236 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -4712,40 +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
-table_column_is_nullable(struct Table *tab, 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_is_nullable(
-			format->fields[column].nullable_action);
-	} else {
-		/* tab is ephemeral (in SQLite sense).  */
-		assert(tab->def->fields[column].is_nullable ==
-			       nullable_action_is_nullable(
-				       tab->def->fields[column].nullable_action));
-		return tab->def->fields[column].is_nullable;
-	}
-}
diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
index 9dd254f..099068e 100644
--- a/src/box/sql/vdbemem.c
+++ b/src/box/sql/vdbemem.c
@@ -588,9 +588,9 @@ sqlite3VdbeMemCast(Mem * pMem, u8 aff)
 	if (pMem->flags & MEM_Null)
 		return;
 	switch (aff) {
-	case SQLITE_AFF_BLOB:{	/* Really a cast to BLOB */
+	case AFFINITY_BLOB:{	/* Really a cast to BLOB */
 			if ((pMem->flags & MEM_Blob) == 0) {
-				sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT);
+				sqlite3ValueApplyAffinity(pMem, AFFINITY_TEXT);
 				assert(pMem->flags & MEM_Str
 				       || pMem->db->mallocFailed);
 				if (pMem->flags & MEM_Str)
@@ -600,23 +600,23 @@ sqlite3VdbeMemCast(Mem * pMem, u8 aff)
 			}
 			break;
 		}
-	case SQLITE_AFF_NUMERIC:{
+	case AFFINITY_NUMERIC:{
 			sqlite3VdbeMemNumerify(pMem);
 			break;
 		}
-	case SQLITE_AFF_INTEGER:{
+	case AFFINITY_INTEGER:{
 			sqlite3VdbeMemIntegerify(pMem);
 			break;
 		}
-	case SQLITE_AFF_REAL:{
+	case AFFINITY_REAL:{
 			sqlite3VdbeMemRealify(pMem);
 			break;
 		}
 	default:{
-			assert(aff == SQLITE_AFF_TEXT);
+			assert(aff == AFFINITY_TEXT);
 			assert(MEM_Str == (MEM_Blob >> 3));
 			pMem->flags |= (pMem->flags & MEM_Blob) >> 3;
-			sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT);
+			sqlite3ValueApplyAffinity(pMem, AFFINITY_TEXT);
 			assert(pMem->flags & MEM_Str || pMem->db->mallocFailed);
 			pMem->flags &=
 			    ~(MEM_Int | MEM_Real | MEM_Blob | MEM_Zero);
@@ -1300,8 +1300,8 @@ valueFromExpr(sqlite3 * db,	/* The database connection */
 			sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_DYNAMIC);
 		}
 		if ((op == TK_INTEGER || op == TK_FLOAT)
-		    && affinity == SQLITE_AFF_BLOB) {
-			sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC);
+		    && affinity == AFFINITY_BLOB) {
+			sqlite3ValueApplyAffinity(pVal, AFFINITY_NUMERIC);
 		} else {
 			sqlite3ValueApplyAffinity(pVal, affinity);
 		}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 9ab6295..f3a10c0 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 !pIdx->pTable->def->fields[j].is_nullable;
 	} else if (j == (-1)) {
 		return 1;
 	} else {
@@ -1111,7 +1113,7 @@ sqlite3IndexColumnAffinity(sqlite3 * db, Index * pIdx, int iCol)
 	assert(iCol >= 0 && iCol < (int)index_column_count(pIdx));
 	if (!pIdx->zColAff) {
 		if (sqlite3IndexAffinityStr(db, pIdx) == 0)
-			return SQLITE_AFF_BLOB;
+			return AFFINITY_BLOB;
 	}
 	return pIdx->zColAff[iCol];
 }
@@ -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)) {
+				    && pIndex->pTable->def->fields[iColumn].
+					is_nullable) {
 					isOrderDistinct = 0;
 				}
 
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 9d4055a..a3db23b 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -377,12 +377,12 @@ codeApplyAffinity(Parse * pParse, int base, int n, char *zAff)
 	/* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
 	 * and end of the affinity string.
 	 */
-	while (n > 0 && zAff[0] == SQLITE_AFF_BLOB) {
+	while (n > 0 && zAff[0] == AFFINITY_BLOB) {
 		n--;
 		base++;
 		zAff++;
 	}
-	while (n > 1 && zAff[n - 1] == SQLITE_AFF_BLOB) {
+	while (n > 1 && zAff[n - 1] == AFFINITY_BLOB) {
 		n--;
 	}
 
@@ -411,9 +411,9 @@ updateRangeAffinityStr(Expr * pRight,	/* RHS of comparison */
 	int i;
 	for (i = 0; i < n; i++) {
 		Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
-		if (sqlite3CompareAffinity(p, zAff[i]) == SQLITE_AFF_BLOB
+		if (sqlite3CompareAffinity(p, zAff[i]) == AFFINITY_BLOB
 		    || sqlite3ExprNeedsNoAffinityChange(p, zAff[i])) {
-			zAff[i] = SQLITE_AFF_BLOB;
+			zAff[i] = AFFINITY_BLOB;
 		}
 	}
 }
@@ -753,7 +753,7 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
 				 * affinity of the comparison has been applied to the value.
 				 */
 				if (zAff)
-					zAff[j] = SQLITE_AFF_BLOB;
+					zAff[j] = AFFINITY_BLOB;
 			}
 		} else if ((pTerm->eOperator & WO_ISNULL) == 0) {
 			Expr *pRight = pTerm->pExpr->pRight;
@@ -765,12 +765,12 @@ codeAllEqualityTerms(Parse * pParse,	/* Parsing context */
 			}
 			if (zAff) {
 				if (sqlite3CompareAffinity(pRight, zAff[j]) ==
-				    SQLITE_AFF_BLOB) {
-					zAff[j] = SQLITE_AFF_BLOB;
+				    AFFINITY_BLOB) {
+					zAff[j] = AFFINITY_BLOB;
 				}
 				if (sqlite3ExprNeedsNoAffinityChange
 				    (pRight, zAff[j])) {
-					zAff[j] = SQLITE_AFF_BLOB;
+					zAff[j] = AFFINITY_BLOB;
 				}
 			}
 		}
@@ -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,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 			 * FYI: entries in an index are ordered as follows:
 			 *      NULL, ... NULL, min_value, ...
 			 */
-			if ((j >= 0 && table_column_is_nullable(pIdx->pTable, j))
-			    || j == XN_EXPR) {
+			if ((j >= 0 &&
+				pIdx->pTable->def->fields[j].is_nullable) ||
+			    j == XN_EXPR) {
 				assert(pLoop->nSkip == 0);
 				bSeekPastNull = 1;
 				nExtraReg = 1;
@@ -1306,8 +1307,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 #endif
 			if (pRangeStart == 0) {
 				j = pIdx->aiColumn[nEq];
-				if ((j >= 0
-				     && table_column_is_nullable(pIdx->pTable, j)) || j == XN_EXPR) {
+				if ((j >= 0 &&
+				     pIdx->pTable->def->fields[j].is_nullable) ||
+				    j == XN_EXPR) {
 					bSeekPastNull = 1;
 				}
 			}
@@ -1386,8 +1388,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 == AFFINITY_INTEGER) {
 			/* 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 +1727,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..2636cd5 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -261,7 +261,7 @@ isLikeOrGlob(Parse * pParse,	/* Parsing and code generating context */
 #endif
 	pList = pExpr->x.pList;
 	pLeft = pList->a[1].pExpr;
-	if (pLeft->op != TK_COLUMN || sqlite3ExprAffinity(pLeft) != SQLITE_AFF_TEXT	/* Value might be numeric */
+	if (pLeft->op != TK_COLUMN || sqlite3ExprAffinity(pLeft) != AFFINITY_TEXT	/* Value might be numeric */
 	    ) {
 		/* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must
 		 * be the name of an indexed column with TEXT affinity.
@@ -276,7 +276,7 @@ isLikeOrGlob(Parse * pParse,	/* Parsing and code generating context */
 		Vdbe *pReprepare = pParse->pReprepare;
 		int iCol = pRight->iColumn;
 		pVal =
-		    sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
+		    sqlite3VdbeGetBoundValue(pReprepare, iCol, AFFINITY_BLOB);
 		if (pVal && sqlite3_value_type(pVal) == SQLITE_TEXT) {
 			z = (char *)sqlite3_value_text(pVal);
 		}
@@ -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] 20+ messages in thread

* [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
                   ` (2 preceding siblings ...)
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 3/4] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-15 17:03 ` Kirill Shcherbatov
  2018-05-16 17:59   ` [tarantool-patches] " Vladislav Shpilevoy
  2018-05-16 11:52 ` [tarantool-patches] Re: [PATCH v6 0/4] sql: moved " Vladislav Shpilevoy
  2018-05-23  5:19 ` Kirill Yukhin
  5 siblings, 1 reply; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-15 17:03 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

New server checks stored in space_opts. For ExprList transfering
to server data is packed in msgpack as array of maps:
[{"expr_str": "x < y", "name" : "FIRST"}, ..]
Introduced checks_array_decode aimed to unpack this
complex package.
Introduced sql_update_space_def_reference to update space
references as space_def pointer changes over the time,
i.e. resolved checks refer released memory.

Resolves #3272.
---
 src/box/alter.cc            | 11 +++++
 src/box/key_def.cc          |  2 +-
 src/box/opt_def.c           | 16 +++++++-
 src/box/opt_def.h           |  8 +++-
 src/box/space_def.c         | 97 ++++++++++++++++++++++++++++++++++++++++++++
 src/box/space_def.h         |  6 +++
 src/box/sql.c               | 83 ++++++++++++++++++++++++++++++++++++-
 src/box/sql.h               | 93 +++++++++++++++++++++++++++++++++++++++++-
 src/box/sql/build.c         | 99 ++++++++++++++++++++++++++-------------------
 src/box/sql/delete.c        |  6 +--
 src/box/sql/expr.c          | 53 ++++++++++--------------
 src/box/sql/fkey.c          | 15 +++----
 src/box/sql/insert.c        | 22 +++++-----
 src/box/sql/parse.y         | 86 +++++++++++++++++++--------------------
 src/box/sql/pragma.h        |  2 -
 src/box/sql/prepare.c       | 10 ++++-
 src/box/sql/resolve.c       | 39 ++++++------------
 src/box/sql/select.c        | 45 +++++++++++----------
 src/box/sql/sqliteInt.h     | 39 +++++-------------
 src/box/sql/tokenize.c      |  7 ++--
 src/box/sql/trigger.c       | 13 +++---
 src/box/sql/update.c        |  2 +-
 src/box/sql/wherecode.c     | 14 ++++---
 src/box/sql/whereexpr.c     |  7 ++--
 test/sql-tap/check.test.lua | 13 +++---
 25 files changed, 542 insertions(+), 246 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index c446b10..f1c2394 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -407,6 +407,7 @@ field_def_decode(struct field_def *field, const char **data,
 
 	if (field->default_value != NULL &&
 	    sql_expr_compile(sql_get(), field->default_value,
+			     strlen(field->default_value),
 			     &field->default_value_expr) != 0)
 		diag_raise();
 }
@@ -521,6 +522,16 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode,
 		space_def_new_xc(id, uid, exact_field_count, name, name_len,
 				 engine_name, engine_name_len, &opts, fields,
 				 field_count);
+	if (def->opts.checks != NULL) {
+		int rc =
+			sql_resolve_checks_space_def_reference(def->opts.checks,
+							       def);
+		if (rc != 0)
+			tnt_raise(ClientError, errcode,
+				  tt_cstr(name, name_len),
+				  diag_last_error(diag_get())->errmsg);
+	}
+
 	auto def_guard = make_scoped_guard([=] { space_def_delete(def); });
 	struct engine *engine = engine_find_xc(def->engine_name);
 	engine_check_space_def_xc(engine, def);
diff --git a/src/box/key_def.cc b/src/box/key_def.cc
index 98719c2..a5ce15d 100644
--- a/src/box/key_def.cc
+++ b/src/box/key_def.cc
@@ -58,7 +58,7 @@ part_type_by_name_wrapper(const char *str, uint32_t len)
 
 const struct opt_def part_def_reg[] = {
 	OPT_DEF_ENUM(PART_OPT_TYPE, field_type, struct key_part_def, type,
-		     part_type_by_name_wrapper),
+		     (void *)part_type_by_name_wrapper),
 	OPT_DEF(PART_OPT_FIELD, OPT_UINT32, struct key_part_def, fieldno),
 	OPT_DEF(PART_OPT_COLLATION, OPT_UINT32, struct key_part_def, coll_id),
 	OPT_DEF(PART_OPT_NULLABILITY, OPT_BOOL, struct key_part_def,
diff --git a/src/box/opt_def.c b/src/box/opt_def.c
index cd93c23..4987654 100644
--- a/src/box/opt_def.c
+++ b/src/box/opt_def.c
@@ -44,6 +44,7 @@ const char *opt_type_strs[] = {
 	/* [OPT_STR]	= */ "string",
 	/* [OPT_STRPTR] = */ "string",
 	/* [OPT_ENUM]   = */ "enum",
+	/* [OPT_ARRAY]  = */ "array",
 };
 
 static int
@@ -112,11 +113,12 @@ opt_set(void *opts, const struct opt_def *def, const char **val,
 		if (mp_typeof(**val) != MP_STR)
 			return -1;
 		str = mp_decode_str(val, &str_len);
-		if (def->to_enum == NULL) {
+		if (def->callback == NULL) {
 			ival = strnindex(def->enum_strs, str, str_len,
 					 def->enum_max);
 		} else {
-			ival = def->to_enum(str, str_len);
+			opt_def_to_enum_cb to_enum = def->callback;
+			ival = to_enum(str, str_len);
 		}
 		switch(def->enum_size) {
 		case sizeof(uint8_t):
@@ -135,6 +137,16 @@ opt_set(void *opts, const struct opt_def *def, const char **val,
 			unreachable();
 		};
 		break;
+	case OPT_ARRAY:
+		if (mp_typeof(**val) != MP_ARRAY)
+			return -1;
+		ival = mp_decode_array(val);
+		opt_def_action_cb array_parse_cb = def->callback;
+		assert(array_parse_cb);
+		*(const char **)opt = array_parse_cb(val, ival);
+		if (*(const char **)opt == NULL)
+			return -1;
+		break;
 	default:
 		unreachable();
 	}
diff --git a/src/box/opt_def.h b/src/box/opt_def.h
index 633832a..d5500d7 100644
--- a/src/box/opt_def.h
+++ b/src/box/opt_def.h
@@ -47,12 +47,14 @@ enum opt_type {
 	OPT_STR,	/* char[] */
 	OPT_STRPTR,	/* char*  */
 	OPT_ENUM,	/* enum */
+	OPT_ARRAY,	/* array */
 	opt_type_MAX,
 };
 
 extern const char *opt_type_strs[];
 
 typedef int64_t (*opt_def_to_enum_cb)(const char *str, uint32_t len);
+typedef void *(*opt_def_action_cb)(void *data, uint32_t k);
 
 struct opt_def {
 	const char *name;
@@ -65,7 +67,7 @@ struct opt_def {
 	const char **enum_strs;
 	uint32_t enum_max;
 	/** If not NULL, used to get a enum value by a string. */
-	opt_def_to_enum_cb to_enum;
+	void *callback;
 };
 
 #define OPT_DEF(key, type, opts, field) \
@@ -76,6 +78,10 @@ struct opt_def {
 	{ key, OPT_ENUM, offsetof(opts, field), sizeof(int), #enum_name, \
 	  sizeof(enum enum_name), enum_name##_strs, enum_name##_MAX, to_enum }
 
+#define OPT_DEF_ARRAY(key, opts, field, action) \
+	 { key, OPT_ARRAY, offsetof(opts, field), sizeof(((opts *)0)->field), \
+		NULL, 0, NULL, 0, action }
+
 #define OPT_END {NULL, opt_type_MAX, 0, 0, NULL, 0, NULL, 0, NULL}
 
 struct region;
diff --git a/src/box/space_def.c b/src/box/space_def.c
index 1fa3345..2dbcac5 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -29,21 +29,29 @@
  * SUCH DAMAGE.
  */
 
+#include "sqliteInt.h"
 #include "space_def.h"
 #include "diag.h"
 #include "error.h"
 #include "sql.h"
+#include "msgpuck.h"
+
+void *
+checks_array_decode(void *data, uint32_t array_items);
 
 const struct space_opts space_opts_default = {
 	/* .temporary = */ false,
 	/* .view = */ false,
 	/* .sql        = */ NULL,
+	/* .checks     = */ NULL,
 };
 
 const struct opt_def space_opts_reg[] = {
 	OPT_DEF("temporary", OPT_BOOL, struct space_opts, temporary),
 	OPT_DEF("view", OPT_BOOL, struct space_opts, is_view),
 	OPT_DEF("sql", OPT_STRPTR, struct space_opts, sql),
+	OPT_DEF_ARRAY("checks", struct space_opts, checks,
+		      (void *)checks_array_decode),
 	OPT_END,
 };
 
@@ -123,6 +131,18 @@ space_def_dup(const struct space_def *src)
 			}
 		}
 	}
+	if (src->opts.checks != NULL) {
+		ret->opts.checks =
+			sql_expr_list_dup(sql_get(), src->opts.checks, 0);
+		if (ret->opts.checks == NULL) {
+			diag_set(OutOfMemory, 0, "sql_expr_list_dup",
+				 "ret->opts.checks");
+			free(ret->opts.sql);
+			free(ret);
+			return NULL;
+		}
+		sql_update_checks_space_def_reference(ret->opts.checks, ret);
+	}
 	tuple_dictionary_ref(ret->dict);
 	return ret;
 }
@@ -209,6 +229,16 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
 			}
 		}
 	}
+	if (opts->checks != NULL) {
+		def->opts.checks = sql_expr_list_dup(sql_get(), opts->checks, 0);
+		if (def->opts.checks == NULL) {
+			diag_set(OutOfMemory, 0, "sql_expr_list_dup",
+				 "def->opts.pCheck");
+			free(def);
+			return NULL;
+		}
+		sql_update_checks_space_def_reference(def->opts.checks, def);
+	}
 	return def;
 }
 
@@ -233,3 +263,70 @@ space_def_delete(struct space_def *def)
 	TRASH(def);
 	free(def);
 }
+
+/**
+ * Decode checks from msgpack.
+ * @param data pointer to array of maps
+ *         e.g. [{"expr_str": "x < y", "name": "ONE"}, ..].
+ * @param array_items array items count.
+ * @retval not NULL Checks pointer on success.
+ * @retval NULL on error.
+ */
+void *
+checks_array_decode(void *data, uint32_t array_items)
+{
+	struct ExprList *pChecks = NULL;
+	const char **map = (const char **)data;
+	struct sqlite3 *db = sql_get();
+	for (unsigned i = 0; i < array_items; i++) {
+		pChecks = sql_expr_list_append(db, pChecks, NULL);
+		if (pChecks == NULL) {
+			diag_set(OutOfMemory, 0, "sql_expr_list_append",
+				 "pChecks");
+			goto error;
+		}
+		struct ExprList_item *pItem =
+			&pChecks->a[pChecks->nExpr - 1];
+		uint32_t map_size = mp_decode_map(map);
+		for (unsigned j = 0; j < map_size; j++) {
+			if (mp_typeof(**map) != MP_STR) {
+				diag_set(ClientError, ER_WRONG_INDEX_OPTIONS, 0,
+					 "key must be a string");
+				goto error;
+			}
+			uint32_t key_len;
+			const char *key = mp_decode_str(map, &key_len);
+			if (strncmp(key, "expr_str", key_len) == 0) {
+				uint32_t expr_str_len = 0;
+				const char *expr_str =
+					mp_decode_str(map, &expr_str_len);
+				struct Expr *check_expr = NULL;
+				if (sql_expr_compile(db, expr_str, expr_str_len,
+						     &check_expr) != 0)
+					goto error;
+				pItem->pExpr = check_expr;
+			} else if (strncmp(key, "name", key_len) == 0) {
+				uint32_t name_str_len = 0;
+				const char *name_str =
+					mp_decode_str(map, &name_str_len);
+				assert(pItem->zName == NULL);
+				pItem->zName = sqlite3DbStrNDup(db, name_str,
+								name_str_len);
+				if (pItem->zName == NULL) {
+					diag_set(OutOfMemory, 0,
+						 "checks_array_decode",
+						 "pItem->zName");
+					goto error;
+				}
+			} else {
+				diag_set(ClientError, ER_WRONG_INDEX_OPTIONS, 0,
+					 "pItem->zName");
+				goto error;
+			}
+		}
+	}
+	return pChecks;
+error:
+	sql_expr_list_free(db, pChecks);
+	return  NULL;
+}
diff --git a/src/box/space_def.h b/src/box/space_def.h
index 52447b6..0d2b9e6 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -33,6 +33,7 @@
 #include "trivia/util.h"
 #include "tuple_dictionary.h"
 #include "schema_def.h"
+#include "sql.h"
 #include <stdlib.h>
 #include <stdbool.h>
 
@@ -59,6 +60,10 @@ struct space_opts {
 	 * SQL statement that produced this space.
 	 */
 	char *sql;
+	/**
+	 * SQL Checks expressions list.
+	 */
+	struct ExprList *checks;
 };
 
 extern const struct space_opts space_opts_default;
@@ -81,6 +86,7 @@ static inline void
 space_opts_destroy(struct space_opts *opts)
 {
 	free(opts->sql);
+	sql_expr_list_free(sql_get(), opts->checks);
 	TRASH(opts);
 }
 
diff --git a/src/box/sql.c b/src/box/sql.c
index 357cbf9..bdb7e5b 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1510,13 +1510,45 @@ int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
 	bool is_view = false;
 	if (pTable != NULL)
 		is_view = pTable->def->opts.is_view;
-	p = enc->encode_map(base, is_view ? 2 : 1);
+	bool has_checks = (pTable != NULL && pTable->def->opts.checks != NULL &&
+			   pTable->def->opts.checks->nExpr > 0);
+	int checks_cnt = has_checks ? pTable->def->opts.checks->nExpr : 0;
+
+	int map_fields = 1;
+	map_fields += (is_view == true);
+	map_fields += (has_checks == true);
+	p = enc->encode_map(base, map_fields);
 	p = enc->encode_str(p, "sql", 3);
 	p = enc->encode_str(p, zSql, strlen(zSql));
 	if (is_view) {
 		p = enc->encode_str(p, "view", 4);
 		p = enc->encode_bool(p, true);
 	}
+
+	if (!has_checks || checks_cnt == 0)
+		return (int)(p - base);
+
+	struct ExprList_item *a = pTable->def->opts.checks->a;
+	p = enc->encode_str(p, "checks", 6);
+	p = enc->encode_array(p, checks_cnt);
+	for (int i = 0; i < checks_cnt; ++i) {
+		int items = 0;
+		items += (a[i].pExpr != NULL);
+		items += (a[i].zName != NULL);
+		p = enc->encode_map(p, items);
+		if (a[i].pExpr != NULL) {
+			Expr *pExpr = a[i].pExpr;
+			assert(pExpr->u.zToken != NULL);
+			p = enc->encode_str(p, "expr_str", 8);
+			p = enc->encode_str(p, pExpr->u.zToken,
+					    strlen(pExpr->u.zToken));
+		}
+		if (a[i].zName != NULL) {
+			p = enc->encode_str(p, "name", 10);
+			p = enc->encode_str(p, a[i].zName,
+					    strlen(a[i].zName));
+		}
+	}
 	return (int)(p - base);
 }
 
@@ -1739,6 +1771,52 @@ sql_ephemeral_table_new(Parse *parser, const char *name)
 	return table;
 }
 
+static int
+update_space_def_callback(Walker *pWalker, Expr *pExpr)
+{
+	if (pExpr->op == TK_COLUMN && ExprHasProperty(pExpr, EP_Resolved))
+		pExpr->space_def = (struct space_def *) pWalker->pParse;
+	return WRC_Continue;
+}
+
+void
+sql_update_checks_space_def_reference(ExprList *expr_list,
+				      struct space_def *def)
+{
+	assert(expr_list != NULL);
+	Walker w;
+	memset(&w, 0, sizeof(w));
+	w.xExprCallback = update_space_def_callback;
+	w.pParse = (void *)def;
+
+	for (int i = 0; i < expr_list->nExpr; i++)
+		sqlite3WalkExpr(&w, expr_list->a[i].pExpr);
+}
+
+int
+sql_resolve_checks_space_def_reference(ExprList *expr_list,
+				       struct space_def *def)
+{
+	Parse parser;
+	sql_parser_create(&parser);
+	parser.db = sql_get();
+	parser.parse_only = true;
+
+	Table dummy_table;
+	memset(&dummy_table, 0, sizeof(dummy_table));
+	dummy_table.def = def;
+
+	sql_resolve_self_reference(&parser, &dummy_table, NC_IsCheck, NULL,
+				   expr_list);
+	int rc = parser.rc;
+	if (rc != SQLITE_OK)
+		diag_set(IllegalParams, parser.zErrMsg);
+
+	sql_parser_destroy(&parser);
+
+	return rc == SQLITE_OK ? 0 : -1;
+}
+
 int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
@@ -1755,5 +1833,8 @@ sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 	}
 	pTable->def = new_def;
 	pTable->def->opts.temporary = false;
+	if (new_def->opts.checks != NULL)
+		sql_update_checks_space_def_reference(new_def->opts.checks,
+						      new_def);
 	return 0;
 }
diff --git a/src/box/sql.h b/src/box/sql.h
index 3c26492..6399c79 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -74,12 +74,14 @@ struct Table;
  * stuct Select and return it.
  * @param db SQL context handle.
  * @param expr Expression to parse.
+ * @param expr_len Expression to parse length (optional).
  * @param[out] result Result: AST structure.
  *
  * @retval Error code if any.
  */
 int
-sql_expr_compile(struct sqlite3 *db, const char *expr, struct Expr **result);
+sql_expr_compile(struct sqlite3 *db, const char *expr, int expr_len,
+	struct Expr **result);
 
 /**
  * Store duplicate of a parsed expression into @a parser.
@@ -175,6 +177,95 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
 int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *table);
 
+/**
+ * Duplicate Expr list.
+ * The flags parameter contains a combination of the EXPRDUP_XXX flags.
+ * If the EXPRDUP_REDUCE flag is set, then the structure returned is a
+ * truncated version of the usual Expr structure that will be stored as
+ * part of the in-memory representation of the database schema.
+ * @param db The database connection.
+ * @param p The ExprList to duplicate.
+ * @param flags EXPRDUP_XXX flags.
+ * @retval NULL on memory allocation error.
+ * @retval not NULL on success.
+ */
+struct ExprList *
+sql_expr_list_dup(struct sqlite3 * db, struct ExprList * p, int flags);
+
+/**
+ * Free AST pointed by expr list.
+ * @param db SQL handle.
+ * @param expr_list Root pointer of ExprList.
+ */
+void
+sql_expr_list_free(struct sqlite3 * db, struct ExprList *expr_list);
+
+/**
+ * Add a new element to the end of an expression list.  If pList is
+ * initially NULL, then create a new expression list.
+ *
+ * If a memory allocation error occurs, the entire list is freed and
+ * NULL is returned.  If non-NULL is returned, then it is guaranteed
+ * that the new entry was successfully appended.
+ * @param db SQL handle.
+ * @param expr_list List to which to append. Might be NULL.
+ * @param expr Expression to be appended. Might be NULL.
+ * @retval NULL on memory allocation error.
+ * @retval not NULL on success.
+ */
+struct ExprList *
+sql_expr_list_append(struct sqlite3 * db, struct ExprList *expr_list, struct Expr *expr);
+
+/**
+ *  Resolve names in expressions that can only reference a single table:
+ *  *   CHECK constraints
+ *  *   WHERE clauses on partial indices
+ *  The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
+ *  is set to -1 and the Expr.iColumn value is set to the column number.
+ *  Any errors cause an error message to be set in pParse.
+ * @param pParse Parsing context.
+ * @param pTab The table being referenced.
+ * @param type NC_IsCheck or NC_PartIdx or NC_IdxExpr.
+ * @param pExpr Expression to resolve.  May be NULL.
+ * @param pList Expression list to resolve.  May be NUL.
+ */
+void
+sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
+	struct Expr *expr, struct ExprList *expr_list);
+
+/**
+ * Resolve space_def references checks for expr_list.
+ * @param expr_list to modify.
+ * @param def to refer to.
+ */
+int
+sql_resolve_checks_space_def_reference(struct ExprList *expr_list,
+				       struct space_def *def);
+
+/**
+ * Update space_def references for checks expr_list.
+ * @param expr_list to modify.
+ * @param def to refer to.
+ */
+void
+sql_update_checks_space_def_reference(struct ExprList *expr_list,
+				      struct space_def *def);
+
+/**
+ * Initialize a new parser object.
+ * @param parser object to initialize.
+ */
+void
+sql_parser_create(struct Parse *parser);
+
+/**
+ * Release the parser object resources.
+ * @param parser object to release.
+ */
+void
+sql_parser_destroy(struct Parse *parser);
+
+
 #if defined(__cplusplus)
 } /* extern "C" { */
 #endif
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 718809d..60f1a50 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -218,7 +218,7 @@ freeIndex(sqlite3 * db, Index * p)
 	sqlite3DeleteIndexSamples(db, p);
 #endif
 	sql_expr_free(db, p->pPartIdxWhere, false);
-	sqlite3ExprListDelete(db, p->aColExpr);
+	sql_expr_list_free(db, p->aColExpr);
 	sqlite3DbFree(db, p->zColAff);
 	sqlite3_free(p->aiRowEst);
 	sqlite3DbFree(db, p);
@@ -373,7 +373,6 @@ deleteTable(sqlite3 * db, Table * pTable)
 	sqlite3DbFree(db, pTable->aCol);
 	sqlite3DbFree(db, pTable->zColAff);
 	sqlite3SelectDelete(db, pTable->pSelect);
-	sqlite3ExprListDelete(db, pTable->pCheck);
 	/* Do not delete pTable->def allocated not on region. */
 	assert(pTable->def != NULL);
 	if (!pTable->def->opts.temporary)
@@ -973,7 +972,7 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
 	}
 
  primary_key_exit:
-	sqlite3ExprListDelete(pParse->db, pList);
+	sql_expr_list_free(pParse->db, pList);
 	return;
 }
 
@@ -981,22 +980,25 @@ sqlite3AddPrimaryKey(Parse * pParse,	/* Parsing context */
  * Add a new CHECK constraint to the table currently under construction.
  */
 void
-sqlite3AddCheckConstraint(Parse * pParse,	/* Parsing context */
-			  Expr * pCheckExpr	/* The check expression */
-    )
+sqlite3AddCheckConstraint(Parse *parser, Expr *expr, ExprSpan *span)
 {
 #ifndef SQLITE_OMIT_CHECK
-	Table *pTab = pParse->pNewTable;
+	Table *pTab = parser->pNewTable;
 	if (pTab) {
-		pTab->pCheck =
-		    sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
-		if (pParse->constraintName.n) {
-			sqlite3ExprListSetName(pParse, pTab->pCheck,
-					       &pParse->constraintName, 1);
+		sqlite3 *db = parser->db;
+		expr->u.zToken =
+			sqlite3DbStrNDup(db, (char *)span->zStart,
+					 (int)(span->zEnd - span->zStart));
+		pTab->def->opts.checks =
+			sql_expr_list_append(parser->db, pTab->def->opts.checks,
+					     expr);
+		if (parser->constraintName.n) {
+			sqlite3ExprListSetName(parser, pTab->def->opts.checks,
+					       &parser->constraintName, 1);
 		}
 	} else
 #endif
-		sql_expr_free(pParse->db, pCheckExpr, false);
+		sql_expr_free(parser->db, expr, false);
 }
 
 /*
@@ -1088,6 +1090,22 @@ space_is_view(Table *table) {
 }
 
 /**
+ * Get checks list by space_id.
+ * @param space_id Space ID.
+ * @retval NULL on error.
+ * @param not NULL on success.
+ */
+struct ExprList *
+space_checks_expr_list(uint32_t space_id)
+{
+	struct space *space;
+	space = space_by_id(space_id);
+	assert(space != NULL);
+	assert(space->def != NULL);
+	return space->def->opts.checks;
+}
+
+/**
  * Create cursor which will be positioned to the space/index.
  * It makes space lookup and loads pointer to it into register,
  * which is passes to OP_OpenWrite as an argument.
@@ -1395,9 +1413,9 @@ convertToWithoutRowidTable(Parse * pParse, Table * pTab)
 		ExprList *pList;
 		Token ipkToken;
 		sqlite3TokenInit(&ipkToken, pTab->def->fields[pTab->iPKey].name);
-		pList = sqlite3ExprListAppend(pParse, 0,
-					      sqlite3ExprAlloc(db, TK_ID,
-							       &ipkToken, 0));
+		pList = sql_expr_list_append(pParse->db, 0,
+					     sqlite3ExprAlloc(db, TK_ID,
+							      &ipkToken, 0));
 		if (pList == 0)
 			return;
 		pList->a[0].sortOrder = pParse->iPkSortOrder;
@@ -1826,15 +1844,6 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 	if (sql_table_def_rebuild(db, p) != 0)
 		return;
 
-#ifndef SQLITE_OMIT_CHECK
-	/* Resolve names in all CHECK constraint expressions.
-	 */
-	if (p->pCheck) {
-		sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0,
-					    p->pCheck);
-	}
-#endif				/* !defined(SQLITE_OMIT_CHECK) */
-
 	/* Estimate the average row size for the table and for all implied indices */
 	estimateTableWidth(p);
 	for (pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {
@@ -1964,6 +1973,12 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
 		}
 #endif
 	}
+
+	/* We don't need Checks in SQL anymore. */
+	if (p->def->opts.checks != NULL) {
+		sql_expr_list_free(db, p->def->opts.checks);
+		p->def->opts.checks = NULL;
+	}
 }
 
 #ifndef SQLITE_OMIT_VIEW
@@ -2005,7 +2020,7 @@ sqlite3CreateView(Parse * pParse,	/* The parsing context */
 	 */
 	p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
 	p->def->opts.is_view = true;
-	p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
+	p->def->opts.checks = sql_expr_list_dup(db, pCNames, EXPRDUP_REDUCE);
 	if (db->mallocFailed)
 		goto create_view_fail;
 
@@ -2032,7 +2047,7 @@ sqlite3CreateView(Parse * pParse,	/* The parsing context */
 
  create_view_fail:
 	sqlite3SelectDelete(db, pSelect);
-	sqlite3ExprListDelete(db, pCNames);
+	sql_expr_list_free(db, pCNames);
 	return;
 }
 #endif				/* SQLITE_OMIT_VIEW */
@@ -2100,7 +2115,8 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 		db->lookaside.bDisable++;
 		pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
 		pParse->nTab = n;
-		if (pTable->pCheck) {
+		ExprList *checks = space_checks_expr_list(pTable->def->id);
+		if (checks != NULL) {
 			/* CREATE VIEW name(arglist) AS ...
 			 * The names of the columns in the table are taken from
 			 * arglist which is stored in pTable->pCheck.  The pCheck field
@@ -2108,8 +2124,7 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
 			 * a VIEW it holds the list of column names.
 			 */
 			struct space_def *old_def = pTable->def;
-			sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
-						   pTable);
+			sqlite3ColumnsFromExprList(pParse, checks, pTable);
 			if (sql_table_def_rebuild(db, pTable) != 0) {
 				nErr++;
 			} else {
@@ -2562,8 +2577,8 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
  fk_end:
 	sqlite3DbFree(db, pFKey);
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
-	sqlite3ExprListDelete(db, pFromCol);
-	sqlite3ExprListDelete(db, pToCol);
+	sql_expr_list_free(db, pFromCol);
+	sql_expr_list_free(db, pToCol);
 }
 
 /*
@@ -2971,7 +2986,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 		struct field_def *field =
 			&pTab->def->fields[pTab->def->field_count - 1];
 		sqlite3TokenInit(&prevCol, field->name);
-		pList = sqlite3ExprListAppend(pParse, 0,
+		pList = sql_expr_list_append(pParse->db, 0,
 					      sqlite3ExprAlloc(db, TK_ID,
 							       &prevCol, 0));
 		if (pList == 0)
@@ -3024,8 +3039,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	pIndex->nColumn = pList->nExpr;
 	/* Tarantool have access to each column by any index */
 	if (pPIWhere) {
-		sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere,
-					    0);
+		sql_resolve_self_reference(pParse, pTab, NC_PartIdx, pPIWhere,
+					   0);
 		pIndex->pPartIdxWhere = pPIWhere;
 		pPIWhere = 0;
 	}
@@ -3042,8 +3057,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	for (i = 0, pListItem = pList->a; i < pList->nExpr; i++, pListItem++) {
 		Expr *pCExpr;	/* The i-th index expression */
 		int requestedSortOrder;	/* ASC or DESC on the i-th expression */
-		sqlite3ResolveSelfReference(pParse, pTab, NC_IdxExpr,
-					    pListItem->pExpr, 0);
+		sql_resolve_self_reference(pParse, pTab, NC_IdxExpr,
+					   pListItem->pExpr, 0);
 		if (pParse->nErr)
 			goto exit_create_index;
 		pCExpr = sqlite3ExprSkipCollate(pListItem->pExpr);
@@ -3258,7 +3273,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
 	if (pIndex)
 		freeIndex(db, pIndex);
 	sql_expr_free(db, pPIWhere, false);
-	sqlite3ExprListDelete(db, pList);
+	sql_expr_list_free(db, pList);
 	sqlite3SrcListDelete(db, pTblName);
 	sqlite3DbFree(db, zName);
 }
@@ -3733,7 +3748,7 @@ sqlite3SrcListDelete(sqlite3 * db, SrcList * pList)
 		if (pItem->fg.isIndexedBy)
 			sqlite3DbFree(db, pItem->u1.zIndexedBy);
 		if (pItem->fg.isTabFunc)
-			sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
+			sql_expr_list_free(db, pItem->u1.pFuncArg);
 		sqlite3DeleteTable(db, pItem->pTab);
 		sqlite3SelectDelete(db, pItem->pSelect);
 		sql_expr_free(db, pItem->pOn, false);
@@ -3839,7 +3854,7 @@ sqlite3SrcListFuncArgs(Parse * pParse, SrcList * p, ExprList * pList)
 		pItem->u1.pFuncArg = pList;
 		pItem->fg.isTabFunc = 1;
 	} else {
-		sqlite3ExprListDelete(pParse->db, pList);
+		sql_expr_list_free(pParse->db, pList);
 	}
 }
 
@@ -4255,7 +4270,7 @@ sqlite3WithAdd(Parse * pParse,	/* Parsing context */
 	assert((pNew != 0 && zName != 0) || db->mallocFailed);
 
 	if (db->mallocFailed) {
-		sqlite3ExprListDelete(db, pArglist);
+		sql_expr_list_free(db, pArglist);
 		sqlite3SelectDelete(db, pQuery);
 		sqlite3DbFree(db, zName);
 		pNew = pWith;
@@ -4280,7 +4295,7 @@ sqlite3WithDelete(sqlite3 * db, With * pWith)
 		int i;
 		for (i = 0; i < pWith->nCte; i++) {
 			struct Cte *pCte = &pWith->a[i];
-			sqlite3ExprListDelete(db, pCte->pCols);
+			sql_expr_list_free(db, pCte->pCols);
 			sqlite3SelectDelete(db, pCte->pSelect);
 			sqlite3DbFree(db, pCte->zName);
 		}
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 5056005..476993d 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -182,7 +182,7 @@ sqlite3LimitWhere(Parse * pParse,	/* The parser context */
 	pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
 	if (pSelectRowid == 0)
 		goto limit_where_cleanup;
-	pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
+	pEList = sql_expr_list_append(pParse->db, 0, pSelectRowid);
 	if (pEList == 0)
 		goto limit_where_cleanup;
 
@@ -191,7 +191,7 @@ sqlite3LimitWhere(Parse * pParse,	/* The parser context */
 	 */
 	pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
 	if (pSelectSrc == 0) {
-		sqlite3ExprListDelete(pParse->db, pEList);
+		sql_expr_list_free(pParse->db, pEList);
 		goto limit_where_cleanup;
 	}
 
@@ -210,7 +210,7 @@ sqlite3LimitWhere(Parse * pParse,	/* The parser context */
 
  limit_where_cleanup:
 	sql_expr_free(pParse->db, pWhere);
-	sqlite3ExprListDelete(pParse->db, pOrderBy);
+	sql_expr_list_free(pParse->db, pOrderBy);
 	sql_expr_free(pParse->db, pLimit);
 	sql_expr_free(pParse->db, pOffset);
 	return 0;
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 9a8f045..9f874a1 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -1061,7 +1061,8 @@ sqlite3ExprFunction(Parse * pParse, ExprList * pList, Token * pToken)
 	assert(pToken);
 	pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1);
 	if (pNew == 0) {
-		sqlite3ExprListDelete(db, pList);	/* Avoid memory leak when malloc fails */
+		/* Avoid memory leak when malloc fails. */
+		sql_expr_list_free(db, pList);
 		return 0;
 	}
 	pNew->x.pList = pList;
@@ -1180,7 +1181,7 @@ sqlite3ExprDeleteNN(sqlite3 * db, Expr * p, bool extern_alloc)
 		if (ExprHasProperty(p, EP_xIsSelect)) {
 			sqlite3SelectDelete(db, p->x.pSelect);
 		} else {
-			sqlite3ExprListDelete(db, p->x.pList);
+			sql_expr_list_free(db, p->x.pList);
 		}
 	}
 	if (ExprHasProperty(p, EP_MemToken))
@@ -1368,8 +1369,8 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer)
 						     flags);
 			} else {
 				pNew->x.pList =
-				    sqlite3ExprListDup(db, p->x.pList,
-						       flags);
+					sql_expr_list_dup(db, p->x.pList,
+							  flags);
 			}
 		}
 
@@ -1426,7 +1427,7 @@ withDup(sqlite3 * db, With * p)
 				pRet->a[i].pSelect =
 				    sqlite3SelectDup(db, p->a[i].pSelect, 0);
 				pRet->a[i].pCols =
-				    sqlite3ExprListDup(db, p->a[i].pCols, 0);
+					sql_expr_list_dup(db, p->a[i].pCols, 0);
 				pRet->a[i].zName =
 				    sqlite3DbStrDup(db, p->a[i].zName);
 			}
@@ -1444,7 +1445,7 @@ withDup(sqlite3 * db, With * p)
  * be deleted (by being passed to their respective ...Delete() routines)
  * without effecting the originals.
  *
- * The expression list, ID, and source lists return by sqlite3ExprListDup(),
+ * The expression list, ID, and source lists return by sql_expr_list_dup(),
  * sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded
  * by subsequent calls to sqlite*ListAppend() routines.
  *
@@ -1463,7 +1464,7 @@ sqlite3ExprDup(sqlite3 * db, Expr * p, int flags)
 }
 
 ExprList *
-sqlite3ExprListDup(sqlite3 * db, ExprList * p, int flags)
+sql_expr_list_dup(sqlite3 *db, ExprList *p, int flags)
 {
 	ExprList *pNew;
 	struct ExprList_item *pItem, *pOldItem;
@@ -1556,8 +1557,8 @@ sqlite3SrcListDup(sqlite3 * db, SrcList * p, int flags)
 		pNewItem->pIBIndex = pOldItem->pIBIndex;
 		if (pNewItem->fg.isTabFunc) {
 			pNewItem->u1.pFuncArg =
-			    sqlite3ExprListDup(db, pOldItem->u1.pFuncArg,
-					       flags);
+				sql_expr_list_dup(db, pOldItem->u1.pFuncArg,
+						  flags);
 		}
 		pTab = pNewItem->pTab = pOldItem->pTab;
 		if (pTab) {
@@ -1612,12 +1613,12 @@ sqlite3SelectDup(sqlite3 * db, Select * p, int flags)
 	pNew = sqlite3DbMallocRawNN(db, sizeof(*p));
 	if (pNew == 0)
 		return 0;
-	pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags);
+	pNew->pEList = sql_expr_list_dup(db, p->pEList, flags);
 	pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
 	pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags);
-	pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags);
+	pNew->pGroupBy = sql_expr_list_dup(db, p->pGroupBy, flags);
 	pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
-	pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
+	pNew->pOrderBy = sql_expr_list_dup(db, p->pOrderBy, flags);
 	pNew->op = p->op;
 	pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags);
 	if (pPrior)
@@ -1636,21 +1637,9 @@ sqlite3SelectDup(sqlite3 * db, Select * p, int flags)
 	return pNew;
 }
 
-/*
- * Add a new element to the end of an expression list.  If pList is
- * initially NULL, then create a new expression list.
- *
- * If a memory allocation error occurs, the entire list is freed and
- * NULL is returned.  If non-NULL is returned, then it is guaranteed
- * that the new entry was successfully appended.
- */
 ExprList *
-sqlite3ExprListAppend(Parse * pParse,	/* Parsing context */
-		      ExprList * pList,	/* List to which to append. Might be NULL */
-		      Expr * pExpr	/* Expression to be appended. Might be NULL */
-    )
+sql_expr_list_append(sqlite3 * db, ExprList *pList, Expr *pExpr)
 {
-	sqlite3 *db = pParse->db;
 	assert(db != 0);
 	if (pList == 0) {
 		pList = sqlite3DbMallocRawNN(db, sizeof(ExprList));
@@ -1680,7 +1669,7 @@ sqlite3ExprListAppend(Parse * pParse,	/* Parsing context */
  no_mem:
 	/* Avoid leaking memory if malloc has failed. */
 	sql_expr_free(db, pExpr, false);
-	sqlite3ExprListDelete(db, pList);
+	sql_expr_list_free(db, pList);
 	return 0;
 }
 
@@ -1728,7 +1717,7 @@ sqlite3ExprListAppendVector(Parse * pParse,	/* Parsing context */
 
 	for (i = 0; i < pColumns->nId; i++) {
 		Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
-		pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
+		pList = sql_expr_list_append(pParse->db, pList, pSubExpr);
 		if (pList) {
 			assert(pList->nExpr == iFirst + i + 1);
 			pList->a[pList->nExpr - 1].zName = pColumns->a[i].zName;
@@ -1742,7 +1731,7 @@ sqlite3ExprListAppendVector(Parse * pParse,	/* Parsing context */
 			assert(pFirst->op == TK_SELECT_COLUMN);
 
 			/* Store the SELECT statement in pRight so it will be deleted when
-			 * sqlite3ExprListDelete() is called
+			 * sql_expr_list_free() is called
 			 */
 			pFirst->pRight = pExpr;
 			pExpr = 0;
@@ -1870,10 +1859,10 @@ exprListDeleteNN(sqlite3 * db, ExprList * pList)
 }
 
 void
-sqlite3ExprListDelete(sqlite3 * db, ExprList * pList)
+sql_expr_list_free(sqlite3 *db, ExprList *expr_list)
 {
-	if (pList)
-		exprListDeleteNN(db, pList);
+	if (expr_list)
+		exprListDeleteNN(db, expr_list);
 }
 
 /*
@@ -4440,7 +4429,7 @@ sqlite3ExprCodeAtInit(Parse * pParse,	/* Parsing context */
 	assert(ConstFactorOk(pParse));
 	p = pParse->pConstExpr;
 	pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
-	p = sqlite3ExprListAppend(pParse, p, pExpr);
+	p = sql_expr_list_append(pParse->db, p, pExpr);
 	if (p) {
 		struct ExprList_item *pItem = &p->a[p->nExpr - 1];
 		pItem->u.iConstExprReg = regDest;
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index c7b1cda..0a28943 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -737,7 +737,7 @@ fkTriggerDelete(sqlite3 * dbMem, Trigger * p)
 	if (p) {
 		TriggerStep *pStep = p->step_list;
 		sql_expr_free(dbMem, pStep->pWhere, false);
-		sqlite3ExprListDelete(dbMem, pStep->pExprList);
+		sql_expr_list_free(dbMem, pStep->pExprList);
 		sqlite3SelectDelete(dbMem, pStep->pSelect);
 		sql_expr_free(dbMem, p->pWhen, false);
 		sqlite3DbFree(dbMem, p);
@@ -1367,7 +1367,8 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 					    sqlite3ExprAlloc(db, TK_NULL, 0, 0);
 				}
 				pList =
-				    sqlite3ExprListAppend(pParse, pList, pNew);
+					sql_expr_list_append(pParse->db, pList,
+							     pNew);
 				sqlite3ExprListSetName(pParse, pList, &tFromCol,
 						       0);
 			}
@@ -1390,9 +1391,9 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 				pRaise->affinity = ON_CONFLICT_ACTION_ABORT;
 			}
 			pSelect = sqlite3SelectNew(pParse,
-						   sqlite3ExprListAppend(pParse,
-									 0,
-									 pRaise),
+						   sql_expr_list_append(pParse->db,
+									0,
+									pRaise),
 						   sqlite3SrcListAppend(db, 0,
 									&tFrom),
 						   pWhere, 0, 0, 0, 0, 0, 0);
@@ -1415,7 +1416,7 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 			pStep->pWhere =
 			    sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
 			pStep->pExprList =
-			    sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
+				sql_expr_list_dup(db, pList, EXPRDUP_REDUCE);
 			pStep->pSelect =
 			    sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
 			if (pWhen) {
@@ -1430,7 +1431,7 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 
 		sql_expr_free(db, pWhere, false);
 		sql_expr_free(db, pWhen, false);
-		sqlite3ExprListDelete(db, pList);
+		sql_expr_list_free(db, pList);
 		sqlite3SelectDelete(db, pSelect);
 		if (db->mallocFailed == 1) {
 			fkTriggerDelete(db, pTrigger);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 1a883e2..32ad806 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -922,7 +922,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 
  insert_cleanup:
 	sqlite3SrcListDelete(db, pTabList);
-	sqlite3ExprListDelete(db, pList);
+	sql_expr_list_free(db, pList);
 	sqlite3SelectDelete(db, pSelect);
 	sqlite3IdListDelete(db, pColumn);
 	sqlite3DbFree(db, aRegIdx);
@@ -1183,10 +1183,10 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 
 	/* Test all CHECK constraints
 	 */
-#ifndef SQLITE_OMIT_CHECK
-	if (pTab->pCheck && (user_session->sql_flags &
+	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(pTab->tnum);
+	ExprList *pCheck = space_checks_expr_list(space_id);
+	if (pCheck && (user_session->sql_flags &
 			     SQLITE_IgnoreChecks) == 0) {
-		ExprList *pCheck = pTab->pCheck;
 		pParse->ckBase = regNewData + 1;
 		onError =
 		    overrideError != ON_CONFLICT_ACTION_DEFAULT ? overrideError
@@ -1217,7 +1217,6 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			sqlite3VdbeResolveLabel(v, allOk);
 		}
 	}
-#endif				/* !defined(SQLITE_OMIT_CHECK) */
 
 	/* Test all UNIQUE constraints by creating entries for each UNIQUE
 	 * index and making sure that duplicate entries do not already exist.
@@ -1853,12 +1852,15 @@ xferOptimization(Parse * pParse,	/* Parser context */
 			return 0;	/* pDestIdx has no corresponding index in pSrc */
 		}
 	}
-#ifndef SQLITE_OMIT_CHECK
-	if (pDest->pCheck
-	    && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck, -1)) {
-		return 0;	/* Tables have different CHECK constraints.  Ticket #2252 */
+	ExprList *pCheck_src = space_checks_expr_list(
+		SQLITE_PAGENO_TO_SPACEID(pSrc->tnum));
+	ExprList *pCheck_dest = space_checks_expr_list(
+		SQLITE_PAGENO_TO_SPACEID(pDest->tnum));
+	if (pCheck_dest
+	    && sqlite3ExprListCompare(pCheck_src, pCheck_dest, -1)) {
+		/* Tables have different CHECK constraints.  Ticket #2252 */
+		return 0;
 	}
-#endif
 #ifndef SQLITE_OMIT_FOREIGN_KEY
 	/* Disallow the transfer optimization if the destination table constains
 	 * any foreign key constraints.  This is more restrictive than necessary.
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index b078e20..b7f2f45 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -286,7 +286,7 @@ ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
                                  {sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
 ccons ::= UNIQUE onconf(R).      {sqlite3CreateIndex(pParse,0,0,0,R,0,0,0,0,
                                    SQLITE_IDXTYPE_UNIQUE);}
-ccons ::= CHECK LP expr(X) RP.   {sqlite3AddCheckConstraint(pParse,X.pExpr);}
+ccons ::= CHECK LP expr(X) RP.   {sqlite3AddCheckConstraint(pParse,X.pExpr,&X);}
 ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R).
                                  {sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
 ccons ::= defer_subclause(D).    {sqlite3DeferForeignKey(pParse,D);}
@@ -337,7 +337,7 @@ tcons ::= UNIQUE LP sortlist(X) RP onconf(R).
                                  {sqlite3CreateIndex(pParse,0,0,X,R,0,0,0,0,
                                        SQLITE_IDXTYPE_UNIQUE);}
 tcons ::= CHECK LP expr(E) RP onconf.
-                                 {sqlite3AddCheckConstraint(pParse,E.pExpr);}
+                                 {sqlite3AddCheckConstraint(pParse,E.pExpr,&E);}
 tcons ::= FOREIGN KEY LP eidlist(FA) RP
           REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). {
     sqlite3CreateForeignKey(pParse, FA, &T, TA, R);
@@ -529,25 +529,25 @@ distinct(A) ::= .           {A = 0;}
 // opcode of TK_ASTERISK.
 //
 %type selcollist {ExprList*}
-%destructor selcollist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor selcollist {sql_expr_list_free(pParse->db, $$);}
 %type sclp {ExprList*}
-%destructor sclp {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor sclp {sql_expr_list_free(pParse->db, $$);}
 sclp(A) ::= selcollist(A) COMMA.
 sclp(A) ::= .                                {A = 0;}
 selcollist(A) ::= sclp(A) expr(X) as(Y).     {
-   A = sqlite3ExprListAppend(pParse, A, X.pExpr);
+   A = sql_expr_list_append(pParse->db, A, X.pExpr);
    if( Y.n>0 ) sqlite3ExprListSetName(pParse, A, &Y, 1);
    sqlite3ExprListSetSpan(pParse,A,&X);
 }
 selcollist(A) ::= sclp(A) STAR. {
   Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
-  A = sqlite3ExprListAppend(pParse, A, p);
+  A = sql_expr_list_append(pParse->db, A, p);
 }
 selcollist(A) ::= sclp(A) nm(X) DOT STAR. {
   Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
   Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
   Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
-  A = sqlite3ExprListAppend(pParse,A, pDot);
+  A = sql_expr_list_append(pParse->db,A, pDot);
 }
 
 // An option "AS <id>" phrase that can follow one of the expressions that
@@ -662,23 +662,23 @@ using_opt(U) ::= .                        {U = 0;}
 
 
 %type orderby_opt {ExprList*}
-%destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor orderby_opt {sql_expr_list_free(pParse->db, $$);}
 
 // the sortlist non-terminal stores a list of expression where each
 // expression is optionally followed by ASC or DESC to indicate the
 // sort order.
 //
 %type sortlist {ExprList*}
-%destructor sortlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor sortlist {sql_expr_list_free(pParse->db, $$);}
 
 orderby_opt(A) ::= .                          {A = 0;}
 orderby_opt(A) ::= ORDER BY sortlist(X).      {A = X;}
 sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z). {
-  A = sqlite3ExprListAppend(pParse,A,Y.pExpr);
+  A = sql_expr_list_append(pParse->db,A,Y.pExpr);
   sqlite3ExprListSetSortOrder(A,Z);
 }
 sortlist(A) ::= expr(Y) sortorder(Z). {
-  A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/
+  A = sql_expr_list_append(pParse->db,0,Y.pExpr); /*A-overwrites-Y*/
   sqlite3ExprListSetSortOrder(A,Z);
 }
 
@@ -689,7 +689,7 @@ sortorder(A) ::= DESC.          {A = SQLITE_SO_DESC;}
 sortorder(A) ::= .              {A = SQLITE_SO_UNDEFINED;}
 
 %type groupby_opt {ExprList*}
-%destructor groupby_opt {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor groupby_opt {sql_expr_list_free(pParse->db, $$);}
 groupby_opt(A) ::= .                      {A = 0;}
 groupby_opt(A) ::= GROUP BY nexprlist(X). {A = X;}
 
@@ -778,17 +778,17 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
 %endif
 
 %type setlist {ExprList*}
-%destructor setlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor setlist {sql_expr_list_free(pParse->db, $$);}
 
 setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). {
-  A = sqlite3ExprListAppend(pParse, A, Y.pExpr);
+  A = sql_expr_list_append(pParse->db, A, Y.pExpr);
   sqlite3ExprListSetName(pParse, A, &X, 1);
 }
 setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). {
   A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr);
 }
 setlist(A) ::= nm(X) EQ expr(Y). {
-  A = sqlite3ExprListAppend(pParse, 0, Y.pExpr);
+  A = sql_expr_list_append(pParse->db, 0, Y.pExpr);
   sqlite3ExprListSetName(pParse, A, &X, 1);
 }
 setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
@@ -970,13 +970,13 @@ term(A) ::= CTIME_KW(OP). {
 }
 
 expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
-  ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
+  ExprList *pList = sql_expr_list_append(pParse->db, X, Y.pExpr);
   A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
   if( A.pExpr ){
     A.pExpr->x.pList = pList;
     spanSet(&A, &L, &R);
   }else{
-    sqlite3ExprListDelete(pParse->db, pList);
+    sql_expr_list_free(pParse->db, pList);
   }
 }
 
@@ -999,8 +999,8 @@ expr(A) ::= expr(A) likeop(OP) expr(Y).  [LIKE_KW]  {
   ExprList *pList;
   int bNot = OP.n & 0x80000000;
   OP.n &= 0x7fffffff;
-  pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
-  pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
+  pList = sql_expr_list_append(pParse->db,0, Y.pExpr);
+  pList = sql_expr_list_append(pParse->db,pList, A.pExpr);
   A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
   exprNot(pParse, bNot, &A);
   A.zEnd = Y.zEnd;
@@ -1010,9 +1010,9 @@ expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E).  [LIKE_KW]  {
   ExprList *pList;
   int bNot = OP.n & 0x80000000;
   OP.n &= 0x7fffffff;
-  pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
-  pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
-  pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
+  pList = sql_expr_list_append(pParse->db,0, Y.pExpr);
+  pList = sql_expr_list_append(pParse->db,pList, A.pExpr);
+  pList = sql_expr_list_append(pParse->db,pList, E.pExpr);
   A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
   exprNot(pParse, bNot, &A);
   A.zEnd = E.zEnd;
@@ -1095,14 +1095,14 @@ expr(A) ::= PLUS(B) expr(X). [BITNOT]
 between_op(A) ::= BETWEEN.     {A = 0;}
 between_op(A) ::= NOT BETWEEN. {A = 1;}
 expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
-  ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr);
-  pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr);
+  ExprList *pList = sql_expr_list_append(pParse->db,0, X.pExpr);
+  pList = sql_expr_list_append(pParse->db,pList, Y.pExpr);
   A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0);
   if( A.pExpr ){
     A.pExpr->x.pList = pList;
   }else{
-    sqlite3ExprListDelete(pParse->db, pList);
-  } 
+    sql_expr_list_free(pParse->db, pList);
+  }
   exprNot(pParse, N, &A);
   A.zEnd = Y.zEnd;
 }
@@ -1140,7 +1140,7 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] {
     */
     Expr *pRHS = Y->a[0].pExpr;
     Y->a[0].pExpr = 0;
-    sqlite3ExprListDelete(pParse->db, Y);
+    sql_expr_list_free(pParse->db, Y);
     /* pRHS cannot be NULL because a malloc error would have been detected
     ** before now and control would have never reached this point */
     if( ALWAYS(pRHS) ){
@@ -1154,7 +1154,7 @@ expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] {
       A.pExpr->x.pList = Y;
       sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
     }else{
-      sqlite3ExprListDelete(pParse->db, Y);
+      sql_expr_list_free(pParse->db, Y);
     }
     exprNot(pParse, N, &A);
   }
@@ -1192,22 +1192,22 @@ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
   spanSet(&A,&C,&E);  /*A-overwrites-C*/
   A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0);
   if( A.pExpr ){
-    A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
+    A.pExpr->x.pList = Z ? sql_expr_list_append(pParse->db,Y,Z) : Y;
     sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
   }else{
-    sqlite3ExprListDelete(pParse->db, Y);
+    sql_expr_list_free(pParse->db, Y);
     sql_expr_free(pParse->db, Z, false);
   }
 }
 %type case_exprlist {ExprList*}
-%destructor case_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor case_exprlist {sql_expr_list_free(pParse->db, $$);}
 case_exprlist(A) ::= case_exprlist(A) WHEN expr(Y) THEN expr(Z). {
-  A = sqlite3ExprListAppend(pParse,A, Y.pExpr);
-  A = sqlite3ExprListAppend(pParse,A, Z.pExpr);
+  A = sql_expr_list_append(pParse->db,A, Y.pExpr);
+  A = sql_expr_list_append(pParse->db,A, Z.pExpr);
 }
 case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). {
-  A = sqlite3ExprListAppend(pParse,0, Y.pExpr);
-  A = sqlite3ExprListAppend(pParse,A, Z.pExpr);
+  A = sql_expr_list_append(pParse->db,0, Y.pExpr);
+  A = sql_expr_list_append(pParse->db,A, Z.pExpr);
 }
 %type case_else {Expr*}
 %destructor case_else {sql_expr_free(pParse->db, $$, false);}
@@ -1219,21 +1219,21 @@ case_operand(A) ::= expr(X).            {A = X.pExpr; /*A-overwrites-X*/}
 case_operand(A) ::= .                   {A = 0;} 
 
 %type exprlist {ExprList*}
-%destructor exprlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor exprlist {sql_expr_list_free(pParse->db, $$);}
 %type nexprlist {ExprList*}
-%destructor nexprlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor nexprlist {sql_expr_list_free(pParse->db, $$);}
 
 exprlist(A) ::= nexprlist(A).
 exprlist(A) ::= .                            {A = 0;}
 nexprlist(A) ::= nexprlist(A) COMMA expr(Y).
-    {A = sqlite3ExprListAppend(pParse,A,Y.pExpr);}
+    {A = sql_expr_list_append(pParse->db,A,Y.pExpr);}
 nexprlist(A) ::= expr(Y).
-    {A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/}
+    {A = sql_expr_list_append(pParse->db,0,Y.pExpr); /*A-overwrites-Y*/}
 
 /* A paren_exprlist is an optional expression list contained inside
 ** of parenthesis */
 %type paren_exprlist {ExprList*}
-%destructor paren_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor paren_exprlist {sql_expr_list_free(pParse->db, $$);}
 paren_exprlist(A) ::= .   {A = 0;}
 paren_exprlist(A) ::= LP exprlist(X) RP.  {A = X;}
 
@@ -1261,9 +1261,9 @@ uniqueflag(A) ::= .        {A = ON_CONFLICT_ACTION_NONE;}
 // used for the arguments to an index.  That is just an historical accident.
 //
 %type eidlist {ExprList*}
-%destructor eidlist {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor eidlist {sql_expr_list_free(pParse->db, $$);}
 %type eidlist_opt {ExprList*}
-%destructor eidlist_opt {sqlite3ExprListDelete(pParse->db, $$);}
+%destructor eidlist_opt {sql_expr_list_free(pParse->db, $$);}
 
 %include {
   /* Add a single new term to an ExprList that is used to store a
@@ -1278,7 +1278,7 @@ uniqueflag(A) ::= .        {A = ON_CONFLICT_ACTION_NONE;}
     int hasCollate,
     int sortOrder
   ){
-    ExprList *p = sqlite3ExprListAppend(pParse, pPrior, 0);
+    ExprList *p = sql_expr_list_append(pParse->db, pPrior, 0);
     if( (hasCollate || sortOrder!=SQLITE_SO_UNDEFINED)
         && pParse->db->init.busy==0
     ){
diff --git a/src/box/sql/pragma.h b/src/box/sql/pragma.h
index eb7c93b..f966018 100644
--- a/src/box/sql/pragma.h
+++ b/src/box/sql/pragma.h
@@ -166,14 +166,12 @@ static const PragmaName aPragmaName[] = {
 	 /* iArg:      */ SQLITE_FullColNames},
 #endif
 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
-#if !defined(SQLITE_OMIT_CHECK)
 	{ /* zName:     */ "ignore_check_constraints",
 	 /* ePragTyp:  */ PragTyp_FLAG,
 	 /* ePragFlg:  */ PragFlg_Result0 | PragFlg_NoColumns1,
 	 /* ColNames:  */ 0, 0,
 	 /* iArg:      */ SQLITE_IgnoreChecks},
 #endif
-#endif
 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 	{ /* zName:     */ "index_info",
 	 /* ePragTyp:  */ PragTyp_INDEX_INFO,
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index f949709..5c7e0c8 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -437,12 +437,20 @@ sqlite3_prepare_v2(sqlite3 * db,	/* Database handle. */
 }
 
 void
+sql_parser_create(struct Parse *parser)
+{
+	memset(parser, 0, sizeof(struct Parse));
+	struct region *region = &fiber()->gc;
+	parser->region_initial_size = region_used(region);
+}
+
+void
 sql_parser_destroy(Parse *parser)
 {
 	assert(parser != NULL);
 	sqlite3 *db = parser->db;
 	sqlite3DbFree(db, parser->aLabel);
-	sqlite3ExprListDelete(db, parser->pConstExpr);
+	sql_expr_list_free(db, parser->pConstExpr);
 	if (db != NULL) {
 		assert(db->lookaside.bDisable >=
 		       parser->disableLookaside);
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index bf729b7..3c2cd63 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -530,10 +530,8 @@ notValid(Parse * pParse,	/* Leave error message here */
 		const char *zIn = "partial index WHERE clauses";
 		if (pNC->ncFlags & NC_IdxExpr)
 			zIn = "index expressions";
-#ifndef SQLITE_OMIT_CHECK
 		else if (pNC->ncFlags & NC_IsCheck)
 			zIn = "CHECK constraints";
-#endif
 		sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
 	}
 }
@@ -1578,40 +1576,27 @@ sqlite3ResolveSelectNames(Parse * pParse,	/* The parser context */
 	sqlite3WalkSelect(&w, p);
 }
 
-/*
- * Resolve names in expressions that can only reference a single table:
- *
- *    *   CHECK constraints
- *    *   WHERE clauses on partial indices
- *
- * The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
- * is set to -1 and the Expr.iColumn value is set to the column number.
- *
- * Any errors cause an error message to be set in pParse.
- */
 void
-sqlite3ResolveSelfReference(Parse * pParse,	/* Parsing context */
-			    Table * pTab,	/* The table being referenced */
-			    int type,	/* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
-			    Expr * pExpr,	/* Expression to resolve.  May be NULL. */
-			    ExprList * pList	/* Expression list to resolve.  May be NUL. */
-    )
+sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
+			   struct Expr *expr, struct ExprList *expr_list)
 {
-	SrcList sSrc;		/* Fake SrcList for pParse->pNewTable */
-	NameContext sNC;	/* Name context for pParse->pNewTable */
+	/* Fake SrcList for pParse->pNewTable */
+	SrcList sSrc;
+	/* Name context for pParse->pNewTable */
+	NameContext sNC;
 
 	assert(type == NC_IsCheck || type == NC_PartIdx || type == NC_IdxExpr);
 	memset(&sNC, 0, sizeof(sNC));
 	memset(&sSrc, 0, sizeof(sSrc));
 	sSrc.nSrc = 1;
-	sSrc.a[0].zName = pTab->def->name;
-	sSrc.a[0].pTab = pTab;
+	sSrc.a[0].zName = table->def->name;
+	sSrc.a[0].pTab = table;
 	sSrc.a[0].iCursor = -1;
-	sNC.pParse = pParse;
+	sNC.pParse = parser;
 	sNC.pSrcList = &sSrc;
 	sNC.ncFlags = type;
-	if (sqlite3ResolveExprNames(&sNC, pExpr))
+	if (sqlite3ResolveExprNames(&sNC, expr))
 		return;
-	if (pList)
-		sqlite3ResolveExprListNames(&sNC, pList);
+	if (expr_list)
+		sqlite3ResolveExprListNames(&sNC, expr_list);
 }
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 48aaffc..cadebf7 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -92,12 +92,12 @@ clearSelect(sqlite3 * db, Select * p, int bFree)
 {
 	while (p) {
 		Select *pPrior = p->pPrior;
-		sqlite3ExprListDelete(db, p->pEList);
+		sql_expr_list_free(db, p->pEList);
 		sqlite3SrcListDelete(db, p->pSrc);
 		sql_expr_free(db, p->pWhere, false);
-		sqlite3ExprListDelete(db, p->pGroupBy);
+		sql_expr_list_free(db, p->pGroupBy);
 		sql_expr_free(db, p->pHaving, false);
-		sqlite3ExprListDelete(db, p->pOrderBy);
+		sql_expr_list_free(db, p->pOrderBy);
 		sql_expr_free(db, p->pLimit, false);
 		sql_expr_free(db, p->pOffset, false);
 		if (p->pWith)
@@ -148,8 +148,8 @@ sqlite3SelectNew(Parse * pParse,	/* Parsing context */
 	}
 	if (pEList == 0) {
 		pEList =
-		    sqlite3ExprListAppend(pParse, 0,
-					  sqlite3Expr(db, TK_ASTERISK, 0));
+			sql_expr_list_append(pParse->db, 0,
+					     sqlite3Expr(db, TK_ASTERISK, 0));
 	}
 	struct session MAYBE_UNUSED *user_session;
 	user_session = current_session();
@@ -2372,7 +2372,7 @@ generateWithRecursiveQuery(Parse * pParse,	/* Parsing context */
 	sqlite3VdbeResolveLabel(v, addrBreak);
 
  end_of_recursive_query:
-	sqlite3ExprListDelete(pParse->db, p->pOrderBy);
+	sql_expr_list_free(pParse->db, p->pOrderBy);
 	p->pOrderBy = pOrderBy;
 	p->pLimit = pLimit;
 	p->pOffset = pOffset;
@@ -2667,7 +2667,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				/* Query flattening in sqlite3Select() might refill p->pOrderBy.
 				 * Be sure to delete p->pOrderBy, therefore, to avoid a memory leak.
 				 */
-				sqlite3ExprListDelete(db, p->pOrderBy);
+				sql_expr_list_free(db, p->pOrderBy);
 				pDelete = p->pPrior;
 				p->pPrior = pPrior;
 				p->pOrderBy = 0;
@@ -3216,8 +3216,8 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 				pNew->flags |= EP_IntValue;
 				pNew->u.iValue = i;
 				pOrderBy =
-				    sqlite3ExprListAppend(pParse, pOrderBy,
-							  pNew);
+					sql_expr_list_append(pParse->db, pOrderBy,
+							     pNew);
 				if (pOrderBy)
 					pOrderBy->a[nOrderBy++].u.x.
 					    iOrderByCol = (u16) i;
@@ -3249,7 +3249,7 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 	/* Reattach the ORDER BY clause to the query.
 	 */
 	p->pOrderBy = pOrderBy;
-	pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0);
+	pPrior->pOrderBy = sql_expr_list_dup(pParse->db, pOrderBy, 0);
 
 	/* Allocate a range of temporary registers and the KeyInfo needed
 	 * for the logic that removes duplicate result rows when the
@@ -4106,7 +4106,7 @@ flattenSubquery(Parse * pParse,		/* Parsing context */
 							  pParent->pHaving);
 			assert(pParent->pGroupBy == 0);
 			pParent->pGroupBy =
-			    sqlite3ExprListDup(db, pSub->pGroupBy, 0);
+				sql_expr_list_dup(db, pSub->pGroupBy, 0);
 		} else {
 			pParent->pWhere =
 			    sqlite3ExprAnd(db, pWhere, pParent->pWhere);
@@ -4396,7 +4396,8 @@ convertCompoundSelectToSubquery(Walker * pWalker, Select * p)
 	*pNew = *p;
 	p->pSrc = pNewSrc;
 	p->pEList =
-	    sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0));
+		sql_expr_list_append(pParse->db, 0,
+				     sqlite3Expr(db, TK_ASTERISK, 0));
 	p->op = TK_SELECT;
 	p->pWhere = 0;
 	pNew->pGroupBy = 0;
@@ -4837,8 +4838,8 @@ selectExpander(Walker * pWalker, Select * p)
 				/* This particular expression does not need to be expanded.
 				 */
 				pNew =
-				    sqlite3ExprListAppend(pParse, pNew,
-							  a[k].pExpr);
+					sql_expr_list_append(pParse->db, pNew,
+							     a[k].pExpr);
 				if (pNew) {
 					pNew->a[pNew->nExpr - 1].zName =
 					    a[k].zName;
@@ -4942,7 +4943,8 @@ selectExpander(Walker * pWalker, Select * p)
 						} else {
 							pExpr = pRight;
 						}
-						pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
+						pNew = sql_expr_list_append(
+							pParse->db, pNew, pExpr);
 						sqlite3TokenInit(&sColname, zColname);
 						sqlite3ExprListSetName(pParse,
 								       pNew,
@@ -4984,7 +4986,7 @@ selectExpander(Walker * pWalker, Select * p)
 				}
 			}
 		}
-		sqlite3ExprListDelete(db, pEList);
+		sql_expr_list_free(db, pEList);
 		p->pEList = pNew;
 	}
 #if SQLITE_MAX_COLUMN
@@ -5385,7 +5387,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		/* If ORDER BY makes no difference in the output then neither does
 		 * DISTINCT so it can be removed too.
 		 */
-		sqlite3ExprListDelete(db, p->pOrderBy);
+		sql_expr_list_free(db, p->pOrderBy);
 		p->pOrderBy = 0;
 		p->selFlags &= ~SF_Distinct;
 	}
@@ -5632,7 +5634,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 	if ((p->selFlags & (SF_Distinct | SF_Aggregate)) == SF_Distinct
 	    && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1) == 0) {
 		p->selFlags &= ~SF_Distinct;
-		pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
+		pGroupBy = p->pGroupBy = sql_expr_list_dup(db, pEList, 0);
 		/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
 		 * the sDistinct.isTnct is still set.  Hence, isTnct represents the
 		 * original setting of the SF_Distinct flag, not the current setting
@@ -6222,7 +6224,8 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 
 				if (flag) {
 					pMinMax =
-					    sqlite3ExprListDup(db, pMinMax, 0);
+						sql_expr_list_dup(db, pMinMax,
+								  0);
 					pDel = pMinMax;
 					assert(db->mallocFailed
 					       || pMinMax != 0);
@@ -6244,7 +6247,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 				    sqlite3WhereBegin(pParse, pTabList, pWhere,
 						      pMinMax, 0, flag, 0);
 				if (pWInfo == 0) {
-					sqlite3ExprListDelete(db, pDel);
+					sql_expr_list_free(db, pDel);
 					goto select_end;
 				}
 				updateAccumulator(pParse, &sAggInfo);
@@ -6267,7 +6270,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 					   SQLITE_JUMPIFNULL);
 			selectInnerLoop(pParse, p, p->pEList, -1, 0, 0, pDest,
 					addrEnd, addrEnd);
-			sqlite3ExprListDelete(db, pDel);
+			sql_expr_list_free(db, pDel);
 		}
 		sqlite3VdbeResolveLabel(v, addrEnd);
 
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 75699ae..44897af 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1911,7 +1911,6 @@ struct Table {
 	Select *pSelect;	/* NULL for tables.  Points to definition if a view. */
 	FKey *pFKey;		/* Linked list of all foreign keys in this table */
 	char *zColAff;		/* String defining the affinity of each column */
-	ExprList *pCheck;	/* All CHECK constraints */
 	/*   ... also used as column name list in a VIEW */
 	Hash idxHash;		/* All (named) indices indexed by name */
 	int tnum;		/* Root BTree page for this table */
@@ -2894,10 +2893,8 @@ struct Parse {
 	Token constraintName;	/* Name of the constraint currently being parsed */
 	int regRoot;		/* Register holding root page number for new objects */
 	int nMaxArg;		/* Max args passed to user function by sub-program */
-#ifdef SELECTTRACE_ENABLED
 	int nSelect;		/* Number of SELECT statements seen */
 	int nSelectIndent;	/* How far to indent SELECTTRACE() output */
-#endif
 	Parse *pToplevel;	/* Parse structure for main program (or NULL) */
 	Table *pTriggerTab;	/* Table triggers are being coded for */
 	u32 nQueryLoop;		/* Est number of iterations of a query (10*log2(N)) */
@@ -3472,12 +3469,10 @@ void sqlite3PExprAddSelect(Parse *, Expr *, Select *);
 Expr *sqlite3ExprAnd(sqlite3 *, Expr *, Expr *);
 Expr *sqlite3ExprFunction(Parse *, ExprList *, Token *);
 void sqlite3ExprAssignVarNumber(Parse *, Expr *, u32);
-ExprList *sqlite3ExprListAppend(Parse *, ExprList *, Expr *);
 ExprList *sqlite3ExprListAppendVector(Parse *, ExprList *, IdList *, Expr *);
 void sqlite3ExprListSetSortOrder(ExprList *, int);
 void sqlite3ExprListSetName(Parse *, ExprList *, Token *, int);
 void sqlite3ExprListSetSpan(Parse *, ExprList *, ExprSpan *);
-void sqlite3ExprListDelete(sqlite3 *, ExprList *);
 u32 sqlite3ExprListFlags(const ExprList *);
 int sqlite3Init(sqlite3 *);
 int sqlite3InitCallback(void *, int, char **, char **);
@@ -3493,10 +3488,18 @@ void sqlite3StartTable(Parse *, Token *, int);
 void sqlite3AddColumn(Parse *, Token *, Token *);
 void sqlite3AddNotNull(Parse *, int);
 void sqlite3AddPrimaryKey(Parse *, ExprList *, int, int, int);
-void sqlite3AddCheckConstraint(Parse *, Expr *);
 void sqlite3AddDefaultValue(Parse *, ExprSpan *);
 void sqlite3AddCollateType(Parse *, Token *);
 
+
+/**
+ * Add a new CHECK constraint to the table currently under construction.
+ * @param parser Parsing context.
+ * @param expr The check expression.
+ * @param span The check expression string.
+ */
+void sqlite3AddCheckConstraint(Parse *parser, Expr *expr, ExprSpan *span);
+
 const char *
 column_collation_name(Table *, uint32_t);
 const char *
@@ -3507,6 +3510,8 @@ struct coll *
 sql_default_coll();
 bool
 space_is_view(Table *);
+struct ExprList *
+space_checks_expr_list(uint32_t space_id);
 
 void sqlite3EndTable(Parse *, Token *, Token *, Select *);
 int
@@ -3673,7 +3678,6 @@ void sqlite3MayAbort(Parse *);
 void sqlite3HaltConstraint(Parse *, int, int, char *, i8, u8);
 void sqlite3UniqueConstraint(Parse *, int, Index *);
 Expr *sqlite3ExprDup(sqlite3 *, Expr *, int);
-ExprList *sqlite3ExprListDup(sqlite3 *, ExprList *, int);
 SrcList *sqlite3SrcListDup(sqlite3 *, SrcList *, int);
 IdList *sqlite3IdListDup(sqlite3 *, IdList *);
 Select *sqlite3SelectDup(sqlite3 *, Select *, int);
@@ -3866,7 +3870,6 @@ int sqlite3MatchSpanName(const char *, const char *, const char *);
 int sqlite3ResolveExprNames(NameContext *, Expr *);
 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 *, struct space_def *, int, int);
 void sqlite3AlterFinishAddColumn(Parse *, Token *);
@@ -4117,24 +4120,4 @@ extern int sqlite3InitDatabase(sqlite3 * db);
 enum on_conflict_action
 table_column_nullable_action(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_destroy(struct Parse *parser);
-
 #endif				/* SQLITEINT_H */
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 279c3af..afd202a 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -649,16 +649,17 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 }
 
 int
-sql_expr_compile(sqlite3 *db, const char *expr, struct Expr **result)
+sql_expr_compile(sqlite3 *db, const char *expr, int expr_len,
+		 struct Expr **result)
 {
 	const char *outer = "SELECT ";
-	int len = strlen(outer) + strlen(expr);
+	int len = strlen(outer) + expr_len;
 	char *stmt = (char *) region_alloc(&fiber()->gc, len + 1);
 	if (stmt == NULL) {
 		diag_set(OutOfMemory, len + 1, "region_alloc", "stmt");
 		return -1;
 	}
-	sprintf(stmt, "%s%s", outer, expr);
+	sprintf(stmt, "%s%.*s", outer, expr_len, expr);
 
 	struct Parse parser;
 	sql_parser_create(&parser);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 0845d22..e81f8c7 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -53,7 +53,7 @@ sqlite3DeleteTriggerStep(sqlite3 * db, TriggerStep * pTriggerStep)
 		pTriggerStep = pTriggerStep->pNext;
 
 		sql_expr_free(db, pTmp->pWhere, false);
-		sqlite3ExprListDelete(db, pTmp->pExprList);
+		sql_expr_list_free(db, pTmp->pExprList);
 		sqlite3SelectDelete(db, pTmp->pSelect);
 		sqlite3IdListDelete(db, pTmp->pIdList);
 
@@ -438,12 +438,12 @@ sqlite3TriggerUpdateStep(sqlite3 * db,	/* The database connection */
 	pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName);
 	if (pTriggerStep) {
 		pTriggerStep->pExprList =
-		    sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
+			sql_expr_list_dup(db, pEList, EXPRDUP_REDUCE);
 		pTriggerStep->pWhere =
 		    sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
 		pTriggerStep->orconf = orconf;
 	}
-	sqlite3ExprListDelete(db, pEList);
+	sql_expr_list_free(db, pEList);
 	sql_expr_free(db, pWhere, false);
 	return pTriggerStep;
 }
@@ -724,9 +724,10 @@ codeTriggerProgram(Parse * pParse,	/* The parser context */
 		case TK_UPDATE:{
 				sqlite3Update(pParse,
 					      targetSrcList(pParse, pStep),
-					      sqlite3ExprListDup(db,
-								 pStep->
-								 pExprList, 0),
+					      sql_expr_list_dup(db,
+								pStep->
+									pExprList,
+								0),
 					      sqlite3ExprDup(db, pStep->pWhere,
 							     0),
 					      pParse->eOrconf);
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 635b2d6..121db6e 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -681,7 +681,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
  update_cleanup:
 	sqlite3DbFree(db, aXRef);	/* Also frees aRegIdx[] and aToOpen[] */
 	sqlite3SrcListDelete(db, pTabList);
-	sqlite3ExprListDelete(db, pChanges);
+	sql_expr_list_free(db, pChanges);
 	sql_expr_free(db, pWhere, false);
 	return;
 }
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index a3db23b..7b55f3e 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -510,11 +510,13 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
 							   pExpr, 0);
 
 					pRhs =
-					    sqlite3ExprListAppend(pParse, pRhs,
-								  pNewRhs);
+						sql_expr_list_append(pParse->db,
+								     pRhs,
+								     pNewRhs);
 					pLhs =
-					    sqlite3ExprListAppend(pParse, pLhs,
-								  pNewLhs);
+						sql_expr_list_append(pParse->db,
+								     pLhs,
+								     pNewLhs);
 				}
 			}
 			if (!db->mallocFailed) {
@@ -561,8 +563,8 @@ codeEqualityTerm(Parse * pParse,	/* The parsing context */
 				pLeft->x.pList = pOrigLhs;
 				pX->pLeft = pLeft;
 			}
-			sqlite3ExprListDelete(pParse->db, pLhs);
-			sqlite3ExprListDelete(pParse->db, pRhs);
+			sql_expr_list_free(pParse->db, pLhs);
+			sql_expr_list_free(pParse->db, pRhs);
 		}
 
 		if (eType == IN_INDEX_INDEX_DESC) {
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 2636cd5..9fc62d8 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -782,8 +782,9 @@ exprAnalyzeOrTerm(SrcList * pSrc,	/* the FROM clause */
 				    sqlite3ExprDup(db, pOrTerm->pExpr->pRight,
 						   0);
 				pList =
-				    sqlite3ExprListAppend(pWInfo->pParse, pList,
-							  pDup);
+					sql_expr_list_append(pWInfo->pParse->db,
+							     pList,
+							     pDup);
 				pLeft = pOrTerm->pExpr->pLeft;
 			}
 			assert(pLeft != 0);
@@ -803,7 +804,7 @@ exprAnalyzeOrTerm(SrcList * pSrc,	/* the FROM clause */
 				pTerm = &pWC->a[idxTerm];
 				markTermAsChild(pWC, idxNew, idxTerm);
 			} else {
-				sqlite3ExprListDelete(db, pList);
+				sql_expr_list_free(db, pList);
 			}
 			pTerm->eOperator = WO_NOOP;	/* case 1 trumps case 3 */
 		}
diff --git a/test/sql-tap/check.test.lua b/test/sql-tap/check.test.lua
index 9e21c55..3443879 100755
--- a/test/sql-tap/check.test.lua
+++ b/test/sql-tap/check.test.lua
@@ -345,7 +345,8 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.1>
-        1, "subqueries prohibited in CHECK constraints"
+        1, "Failed to create space 'T3': subqueries prohibited in CHECK "..
+            "constraints"
         -- </check-3.1>
     })
 
@@ -370,7 +371,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.3>
-        1, "no such column: Q"
+        1, "Failed to create space 'T3': no such column: Q"
         -- </check-3.3>
     })
 
@@ -394,7 +395,7 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-3.5>
-        1, "no such column: T2.X"
+        1, "Failed to create space 'T3': no such column: T2.X"
         -- </check-3.5>
     })
 
@@ -555,7 +556,8 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.1>
-        1, "parameters prohibited in CHECK constraints"
+        1, "Failed to create space 'T5': parameters prohibited in CHECK "..
+            "constraints"
         -- </check-5.1>
     })
 
@@ -567,7 +569,8 @@ test:do_catchsql_test(
         );
     ]], {
         -- <check-5.2>
-        1, "parameters prohibited in CHECK constraints"
+        1, "Failed to create space 'T5': parameters prohibited in CHECK "..
+            "constraints"
         -- </check-5.2>
     })
 
-- 
2.7.4

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

* [tarantool-patches] Re: [PATCH v6 0/4] sql: moved Checks to server
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
                   ` (3 preceding siblings ...)
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server Kirill Shcherbatov
@ 2018-05-16 11:52 ` Vladislav Shpilevoy
  2018-05-16 13:13   ` Kirill Shcherbatov
  2018-05-23  5:19 ` Kirill Yukhin
  5 siblings, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-16 11:52 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. Thanks for the patchset! Please, describe what it does, why and how.

On 15/05/2018 20:03, Kirill Shcherbatov wrote:
> Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-checks
> Issue: https://github.com/tarantool/tarantool/issues/3272
> 
> Kirill Shcherbatov (4):
>    sql: fix code style in sqlite3Pragma
>    sql: remove SQL fields from Table and Column
>    sql: space_def* instead of Table* in Expr
>    sql: remove Checks to server
> 
>   src/box/alter.cc            |  11 +
>   src/box/field_def.c         |   1 +
>   src/box/field_def.h         |  20 ++
>   src/box/key_def.cc          |   2 +-
>   src/box/opt_def.c           |  16 +-
>   src/box/opt_def.h           |   8 +-
>   src/box/space_def.c         | 135 +++++++++--
>   src/box/space_def.h         |  24 ++
>   src/box/sql.c               | 225 +++++++++++++++---
>   src/box/sql.h               | 125 +++++++++-
>   src/box/sql/alter.c         |  59 +++--
>   src/box/sql/analyze.c       |  18 +-
>   src/box/sql/build.c         | 553 +++++++++++++++++++++++---------------------
>   src/box/sql/delete.c        |  31 +--
>   src/box/sql/expr.c          | 215 ++++++++---------
>   src/box/sql/fkey.c          |  71 +++---
>   src/box/sql/hash.c          |  10 +-
>   src/box/sql/hash.h          |   2 +-
>   src/box/sql/insert.c        | 138 ++++++-----
>   src/box/sql/parse.y         |  86 +++----
>   src/box/sql/pragma.c        |  96 ++++----
>   src/box/sql/pragma.h        |   2 -
>   src/box/sql/prepare.c       |  52 +++--
>   src/box/sql/resolve.c       |  71 +++---
>   src/box/sql/select.c        | 245 ++++++++++++--------
>   src/box/sql/sqliteInt.h     |  81 ++-----
>   src/box/sql/tokenize.c      |  12 +-
>   src/box/sql/treeview.c      |   2 +-
>   src/box/sql/trigger.c       |  20 +-
>   src/box/sql/update.c        |  60 ++---
>   src/box/sql/util.c          |   9 -
>   src/box/sql/vdbe.c          |  28 +--
>   src/box/sql/vdbeaux.c       |  34 ---
>   src/box/sql/vdbemem.c       |  18 +-
>   src/box/sql/where.c         |  27 ++-
>   src/box/sql/wherecode.c     |  53 +++--
>   src/box/sql/whereexpr.c     |  19 +-
>   test/sql-tap/check.test.lua |  13 +-
>   38 files changed, 1540 insertions(+), 1052 deletions(-)
> 

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

* [tarantool-patches] Re: [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 3/4] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
@ 2018-05-16 12:33   ` Vladislav Shpilevoy
  2018-05-16 13:10     ` Kirill Shcherbatov
       [not found]   ` <26E4269B-2BCB-42C3-8216-D51E290E4723@corp.mail.ru>
  1 sibling, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-16 12:33 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. See below 3 minor comments.

On 15/05/2018 20:03, 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           |  20 +++---
>   src/box/sql/alter.c     |   2 +-
>   src/box/sql/analyze.c   |   2 +-
>   src/box/sql/build.c     | 100 +++++++++++-------------------
>   src/box/sql/delete.c    |   4 +-
>   src/box/sql/expr.c      | 158 ++++++++++++++++++++++++++----------------------
>   src/box/sql/fkey.c      |  18 +++---
>   src/box/sql/insert.c    |  47 ++++++++------
>   src/box/sql/pragma.c    |  10 +--
>   src/box/sql/resolve.c   |  12 ++--
>   src/box/sql/select.c    |  26 +++++---
>   src/box/sql/sqliteInt.h |  44 +++-----------
>   src/box/sql/update.c    |  35 +++++------
>   src/box/sql/vdbe.c      |  26 ++++----
>   src/box/sql/vdbeaux.c   |  37 ------------
>   src/box/sql/vdbemem.c   |  18 +++---
>   src/box/sql/where.c     |  15 +++--
>   src/box/sql/wherecode.c |  35 ++++++-----
>   src/box/sql/whereexpr.c |   6 +-
>   21 files changed, 293 insertions(+), 337 deletions(-)
> 
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 119940c..9a8f045 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -3519,45 +3526,48
> -/*
> +/**
>    * 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.

1. Lets move this comment to the header.

>    */
>   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.

2. Same.

>    */
>   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 +3582,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 +3591,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.

3. Same.

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

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

> 1. Lets move this comment to the heade> 2. Same.
> 3. Same.

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 9a8f045..544fe3d 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3531,14 +3531,6 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse, /* The parsing context */
}
}

-/**
- * 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, struct space_def *space_def,
int iTabCur, int iCol, int regOut)
@@ -3549,22 +3541,6 @@ sqlite3ExprCodeGetColumnOfTable(Vdbe *v, struct space_def *space_def,
}
}

-/**
- * 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().
- * @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, struct space_def *space_def,
int iColumn, int iTable, int iReg, u8 p5)
@@ -3591,16 +3567,6 @@ sqlite3ExprCodeGetColumn(Parse *pParse, struct space_def *space_def,
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, struct space_def * space_def,
int iColumn, int iTable, int iReg)
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 75699ae..04f5197 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3585,9 +3585,47 @@ 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);
+
+/**
+ * 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().
+ * @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 *, struct space_def *, int, int, int, u8);
+
+/**
+ * Generate code that will extract the iColumn-th column from
+ * table pTab and store the column value in a register, copy the
+ * result.
+ * @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 *, struct space_def *, int, int, int);
+
+/**
+ * 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 *, struct space_def *, int, int, int);
+
void sqlite3ExprCodeMove(Parse *, int, int, int);
void sqlite3ExprCacheStore(Parse *, int, int, int);
void sqlite3ExprCachePush(Parse *);

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

* [tarantool-patches] Re: [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
  2018-05-16 13:10     ` Kirill Shcherbatov
@ 2018-05-16 13:11       ` Vladislav Shpilevoy
  0 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-16 13:11 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches, Nikita Pettik

Thanks! Now the first 3 patches are OK to me. Nikita, please, take a look.

On 16/05/2018 16:10, Kirill Shcherbatov wrote:
>> 1. Lets move this comment to the heade> 2. Same.
>> 3. Same.
> 
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 9a8f045..544fe3d 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -3531,14 +3531,6 @@ sqlite3ExprCodeLoadIndexColumn(Parse * pParse, /* The parsing context */
> }
> }
> 
> -/**
> - * 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, struct space_def *space_def,
> int iTabCur, int iCol, int regOut)
> @@ -3549,22 +3541,6 @@ sqlite3ExprCodeGetColumnOfTable(Vdbe *v, struct space_def *space_def,
> }
> }
> 
> -/**
> - * 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().
> - * @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, struct space_def *space_def,
> int iColumn, int iTable, int iReg, u8 p5)
> @@ -3591,16 +3567,6 @@ sqlite3ExprCodeGetColumn(Parse *pParse, struct space_def *space_def,
> 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, struct space_def * space_def,
> int iColumn, int iTable, int iReg)
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 75699ae..04f5197 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -3585,9 +3585,47 @@ 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);
> +
> +/**
> + * 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().
> + * @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 *, struct space_def *, int, int, int, u8);
> +
> +/**
> + * Generate code that will extract the iColumn-th column from
> + * table pTab and store the column value in a register, copy the
> + * result.
> + * @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 *, struct space_def *, int, int, int);
> +
> +/**
> + * 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 *, struct space_def *, int, int, int);
> +
> void sqlite3ExprCodeMove(Parse *, int, int, int);
> void sqlite3ExprCacheStore(Parse *, int, int, int);
> void sqlite3ExprCachePush(Parse *);
> 

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

* [tarantool-patches] Re: [PATCH v6 0/4] sql: moved Checks to server
  2018-05-16 11:52 ` [tarantool-patches] Re: [PATCH v6 0/4] sql: moved " Vladislav Shpilevoy
@ 2018-05-16 13:13   ` Kirill Shcherbatov
  0 siblings, 0 replies; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-16 13:13 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Vladislav Shpilevoy

> Hello. Thanks for the patchset! Please, describe what it does, why and how.

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 made able to move SQL Checks in Server code.
For checks ExprList transfering to server data is packed in msgpack as array of maps:
[{"expr_str": "x < y", "name" : "FIRST"}, ..]
Introduced checks_array_decode aimed to unpack this complex package.
Introduced sql_update_space_def_reference to update space references as space_def pointer 
changes over the  i.e. resolved checks refer released memory.

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

* [tarantool-patches] Re: [PATCH v6 4/4] sql: remove Checks to server
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server Kirill Shcherbatov
@ 2018-05-16 17:59   ` Vladislav Shpilevoy
  0 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-16 17:59 UTC (permalink / raw)
  To: tarantool-patches, Kirill Shcherbatov

Hello. Thanks for the patch! See my 33 comments below.

On 15/05/2018 20:03, Kirill Shcherbatov wrote:
> New server checks stored in space_opts. For ExprList transfering
> to server data is packed in msgpack as array of maps:
> [{"expr_str": "x < y", "name" : "FIRST"}, ..]
> Introduced checks_array_decode aimed to unpack this
> complex package.
> Introduced sql_update_space_def_reference to update space
> references as space_def pointer changes over the time,
> i.e. resolved checks refer released memory.
> 
> Resolves #3272.
> ---
>   src/box/alter.cc            | 11 +++++
>   src/box/key_def.cc          |  2 +-
>   src/box/opt_def.c           | 16 +++++++-
>   src/box/opt_def.h           |  8 +++-
>   src/box/space_def.c         | 97 ++++++++++++++++++++++++++++++++++++++++++++
>   src/box/space_def.h         |  6 +++
>   src/box/sql.c               | 83 ++++++++++++++++++++++++++++++++++++-
>   src/box/sql.h               | 93 +++++++++++++++++++++++++++++++++++++++++-
>   src/box/sql/build.c         | 99 ++++++++++++++++++++++++++-------------------
>   src/box/sql/delete.c        |  6 +--
>   src/box/sql/expr.c          | 53 ++++++++++--------------
>   src/box/sql/fkey.c          | 15 +++----
>   src/box/sql/insert.c        | 22 +++++-----
>   src/box/sql/parse.y         | 86 +++++++++++++++++++--------------------
>   src/box/sql/pragma.h        |  2 -
>   src/box/sql/prepare.c       | 10 ++++-
>   src/box/sql/resolve.c       | 39 ++++++------------
>   src/box/sql/select.c        | 45 +++++++++++----------
>   src/box/sql/sqliteInt.h     | 39 +++++-------------
>   src/box/sql/tokenize.c      |  7 ++--
>   src/box/sql/trigger.c       | 13 +++---
>   src/box/sql/update.c        |  2 +-
>   src/box/sql/wherecode.c     | 14 ++++---
>   src/box/sql/whereexpr.c     |  7 ++--
>   test/sql-tap/check.test.lua | 13 +++---
>   25 files changed, 542 insertions(+), 246 deletions(-)
> 
> diff --git a/src/box/alter.cc b/src/box/alter.cc
> index c446b10..f1c2394 100644
> --- a/src/box/alter.cc
> +++ b/src/box/alter.cc
> @@ -521,6 +522,16 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode,
>   		space_def_new_xc(id, uid, exact_field_count, name, name_len,
>   				 engine_name, engine_name_len, &opts, fields,
>   				 field_count);
> +	if (def->opts.checks != NULL) {
> +		int rc =
> +			sql_resolve_checks_space_def_reference(def->opts.checks,
> +							       def);
> +		if (rc != 0)
> +			tnt_raise(ClientError, errcode,
> +				  tt_cstr(name, name_len),
> +				  diag_last_error(diag_get())->errmsg);
> +	}
> +

1. Apply this please:

diff --git a/src/box/alter.cc b/src/box/alter.cc
index f1c2394ac..3c6be0d45 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -522,14 +522,11 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode,
                 space_def_new_xc(id, uid, exact_field_count, name, name_len,
                                  engine_name, engine_name_len, &opts, fields,
                                  field_count);
-       if (def->opts.checks != NULL) {
-               int rc =
-                       sql_resolve_checks_space_def_reference(def->opts.checks,
-                                                              def);
-               if (rc != 0)
-                       tnt_raise(ClientError, errcode,
-                                 tt_cstr(name, name_len),
-                                 diag_last_error(diag_get())->errmsg);
+       if (def->opts.checks != NULL &&
+           sql_resolve_checks_space_def_reference(def->opts.checks,
+                                                  def) != 0) {
+               tnt_raise(ClientError, errcode, def->name,
+                         box_error_message(box_error_last()));
         }

> diff --git a/src/box/opt_def.h b/src/box/opt_def.h
> index 633832a..d5500d7 100644
> --- a/src/box/opt_def.h
> +++ b/src/box/opt_def.h
> @@ -47,12 +47,14 @@ enum opt_type {
>   	OPT_STR,	/* char[] */
>   	OPT_STRPTR,	/* char*  */
>   	OPT_ENUM,	/* enum */
> +	OPT_ARRAY,	/* array */


2. Lets extract this OPT_DEF_ARRAY things into a separate commit.

>   	opt_type_MAX,
>   };
>   
>   extern const char *opt_type_strs[];
>   
>   typedef int64_t (*opt_def_to_enum_cb)(const char *str, uint32_t len);
> +typedef void *(*opt_def_action_cb)(void *data, uint32_t k);
3. As I can see, opt_def_action_cb takes MessagePack, so 'void *data' must be
'const char **data', and k is length. So rename it to 'len' please as it is
done for enum.

4. I understand what happens here, but it looks too complicated. What to do, when
an array member is not like ArrayType *member, but ArrayType member[fixed_count]?
This will not work.
> *(const char **)opt = array_parse_cb(val, ival);
> if (*(const char **)opt == NULL)
> 	return -1;

Lets better pass the original 'opts' as the first argument of the collback, and
make the former return int - 0 on success and -1 on error. Callback must care itself
about how to store the array.

>   
>   struct opt_def {
>   	const char *name;
> @@ -65,7 +67,7 @@ struct opt_def {
>   	const char **enum_strs;
>   	uint32_t enum_max;
>   	/** If not NULL, used to get a enum value by a string. */
> -	opt_def_to_enum_cb to_enum;
> +	void *callback;

5. Lets create a second typedef with array callback signature, and put here
both enum and array callbacks in union. This allow you to do not cast
void * to function pointer.
>   struct region;
> diff --git a/src/box/space_def.c b/src/box/space_def.c
> index 1fa3345..2dbcac5 100644
> --- a/src/box/space_def.c
> +++ b/src/box/space_def.c
> @@ -29,21 +29,29 @@
>    * SUCH DAMAGE.
>    */
>   
> +#include "sqliteInt.h"

6. Please, remove dependency on sqliteInt.h. All that you need must
be in sql.h.

>   #include "space_def.h"
>   #include "diag.h"
>   #include "error.h"
>   #include "sql.h"
> +#include "msgpuck.h"

7. Why? I removed this and nothing is changed.

> @@ -123,6 +131,18 @@ space_def_dup(const struct space_def *src)
>   			}
>   		}
>   	}
> +	if (src->opts.checks != NULL) {
> +		ret->opts.checks =
> +			sql_expr_list_dup(sql_get(), src->opts.checks, 0);
> +		if (ret->opts.checks == NULL) {
> +			diag_set(OutOfMemory, 0, "sql_expr_list_dup",
> +				 "ret->opts.checks");
> +			free(ret->opts.sql);
> +			free(ret);

8. You did not destroy default_value expressions built above.

> +			return NULL;
> +		}
> +		sql_update_checks_space_def_reference(ret->opts.checks, ret);

9. Lets all CHECK methods name sql_checks_<method_name>.
Here it will be sql_checks_update_space_def_reference.
> @@ -209,6 +229,16 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
>   			}
>   		}
>   	}
> +	if (opts->checks != NULL) {
> +		def->opts.checks = sql_expr_list_dup(sql_get(), opts->checks, 0);
> +		if (def->opts.checks == NULL) {
> +			diag_set(OutOfMemory, 0, "sql_expr_list_dup",
> +				 "def->opts.pCheck");
> +			free(def);

10. Same as 8.

> +			return NULL;
> +		}
> +		sql_update_checks_space_def_reference(def->opts.checks, def);
> +	}
>   	return def;
>   }
>   
> @@ -233,3 +263,70 @@ space_def_delete(struct space_def *def)
>   	TRASH(def);
>   	free(def);
>   }
> +
> +/**
> + * Decode checks from msgpack.
> + * @param data pointer to array of maps
> + *         e.g. [{"expr_str": "x < y", "name": "ONE"}, ..].
> + * @param array_items array items count.
> + * @retval not NULL Checks pointer on success.
> + * @retval NULL on error.
> + */
> +void *
> +checks_array_decode(void *data, uint32_t array_items)

11. I saw that you moved this out of space_def.c, but why?
It is space_opts decoder, and must be in space_def.c file, it is not?

> +{
> +	struct ExprList *pChecks = NULL;
> +	const char **map = (const char **)data;
> +	struct sqlite3 *db = sql_get();
> +	for (unsigned i = 0; i < array_items; i++) {
> +		pChecks = sql_expr_list_append(db, pChecks, NULL);
> +		if (pChecks == NULL) {
> +			diag_set(OutOfMemory, 0, "sql_expr_list_append",
> +				 "pChecks");
> +			goto error;

12. Here you go to error to free pChecks, but it is NULL already.

> +		}
> +		struct ExprList_item *pItem =
> +			&pChecks->a[pChecks->nExpr - 1];
> +		uint32_t map_size = mp_decode_map(map);
> +		for (unsigned j = 0; j < map_size; j++) {
> +			if (mp_typeof(**map) != MP_STR) {
> +				diag_set(ClientError, ER_WRONG_INDEX_OPTIONS, 0,
> +					 "key must be a string");
> +				goto error;
> +			}
> +			uint32_t key_len;
> +			const char *key = mp_decode_str(map, &key_len);
> +			if (strncmp(key, "expr_str", key_len) == 0) {
> +				uint32_t expr_str_len = 0;
> +				const char *expr_str =
> +					mp_decode_str(map, &expr_str_len);
> +				struct Expr *check_expr = NULL;
> +				if (sql_expr_compile(db, expr_str, expr_str_len,
> +						     &check_expr) != 0)
> +					goto error;
> +				pItem->pExpr = check_expr;
> +			} else if (strncmp(key, "name", key_len) == 0) {
> +				uint32_t name_str_len = 0;
> +				const char *name_str =
> +					mp_decode_str(map, &name_str_len);
> +				assert(pItem->zName == NULL);
> +				pItem->zName = sqlite3DbStrNDup(db, name_str,
> +								name_str_len);

13. Lets do not use DB allocations for memory, that is stored in the core.

> diff --git a/src/box/space_def.h b/src/box/space_def.h
> index 52447b6..0d2b9e6 100644
> --- a/src/box/space_def.h
> +++ b/src/box/space_def.h
> @@ -33,6 +33,7 @@
>   #include "trivia/util.h"
>   #include "tuple_dictionary.h"
>   #include "schema_def.h"
> +#include "sql.h"

14. Do not include sql.h. You can predeclare struct ExprList, and move
space_opts_destroy into space_def.c.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index 357cbf9..bdb7e5b 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1510,13 +1510,45 @@ int tarantoolSqlite3MakeTableOpts(Table *pTable, const char *zSql, void *buf)
>   	bool is_view = false;
>   	if (pTable != NULL)
>   		is_view = pTable->def->opts.is_view;
> -	p = enc->encode_map(base, is_view ? 2 : 1);
> +	bool has_checks = (pTable != NULL && pTable->def->opts.checks != NULL &&
> +			   pTable->def->opts.checks->nExpr > 0);

15. How is it possible, that checks != NULL, but checks.nExpr == 0?

> +	int checks_cnt = has_checks ? pTable->def->opts.checks->nExpr : 0;
> +
> +	int map_fields = 1;
> +	map_fields += (is_view == true);

16. Why do you need () ?

> +	map_fields += (has_checks == true);
> +	p = enc->encode_map(base, map_fields);
>   	p = enc->encode_str(p, "sql", 3);
>   	p = enc->encode_str(p, zSql, strlen(zSql));
>   	if (is_view) {
>   		p = enc->encode_str(p, "view", 4);
>   		p = enc->encode_bool(p, true);
>   	}
> +
> +	if (!has_checks || checks_cnt == 0)

17. Why is not has_checks the same as checks_cnt > 0 ?

> +		return (int)(p - base);
> +
> +	struct ExprList_item *a = pTable->def->opts.checks->a;
> +	p = enc->encode_str(p, "checks", 6);
> +	p = enc->encode_array(p, checks_cnt);
> +	for (int i = 0; i < checks_cnt; ++i) {
> +		int items = 0;
> +		items += (a[i].pExpr != NULL);
> +		items += (a[i].zName != NULL);

18. Same as 16.

> +		p = enc->encode_map(p, items);
> +		if (a[i].pExpr != NULL) {

19. How is it possible, that pExpr == NULL in a check?
> @@ -1739,6 +1771,52 @
>   int
>   sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
>   {
> @@ -1755,5 +1833,8 @@ sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
>   	}
>   	pTable->def = new_def;
>   	pTable->def->opts.temporary = false;
> +	if (new_def->opts.checks != NULL)
> +		sql_update_checks_space_def_reference(new_def->opts.checks,
> +						      new_def);

20. You already do this in space_def_new.
> diff --git a/src/box/sql.h b/src/box/sql.h
> index 3c26492..6399c79 100644
> --- a/src/box/sql.h
> +++ b/src/box/sql.h
> @@ -74,12 +74,14 @@ struct Table;
>    * stuct Select and return it.
>    * @param db SQL context handle.
>    * @param expr Expression to parse.
> + * @param expr_len Expression to parse length (optional).

21. Optional? Looks like not.

>    * @param[out] result Result: AST structure.
>    *
>    * @retval Error code if any.
>    */
>   int
> -sql_expr_compile(struct sqlite3 *db, const char *expr, struct Expr **result);
> +sql_expr_compile(struct sqlite3 *db, const char *expr, int expr_len,
> +	struct Expr **result);
>   
> @@ -175,6 +177,95 @@ sql_ephemeral_space_def_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Add a new element to the end of an expression list.  If pList is

22. What is pList?

> + * initially NULL, then create a new expression list.
> + *
> + * If a memory allocation error occurs, the entire list is freed and
> + * NULL is returned.  If non-NULL is returned, then it is guaranteed
> + * that the new entry was successfully appended.
> + * @param db SQL handle.
> + * @param expr_list List to which to append. Might be NULL.
> + * @param expr Expression to be appended. Might be NULL.
> + * @retval NULL on memory allocation error.
> + * @retval not NULL on success.
> + */
> +struct ExprList *
> +sql_expr_list_append(struct sqlite3 * db, struct ExprList *expr_list, struct Expr *expr);
> +
> +/**
> + *  Resolve names in expressions that can only reference a single table:

23. Too many white spaces after *. And below too.

> + *  *   CHECK constraints
> + *  *   WHERE clauses on partial indices
> + *  The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
> + *  is set to -1 and the Expr.iColumn value is set to the column number.
> + *  Any errors cause an error message to be set in pParse.
> + * @param pParse Parsing context.
> + * @param pTab The table being referenced.
> + * @param type NC_IsCheck or NC_PartIdx or NC_IdxExpr.
> + * @param pExpr Expression to resolve.  May be NULL.
> + * @param pList Expression list to resolve.  May be NUL.

24. Names are out of date.

> + */
> +void
> +sql_resolve_self_reference(struct Parse *parser, struct Table *table, int type,
> +	struct Expr *expr, struct ExprList *expr_list);
> +
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 718809d..60f1a50 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -1964,6 +1973,12 @@ sqlite3EndTable(Parse * pParse,	/* Parse context */
>   		}
>   #endif
>   	}
> +
> +	/* We don't need Checks in SQL anymore. */
> +	if (p->def->opts.checks != NULL) {
> +		sql_expr_list_free(db, p->def->opts.checks);
> +		p->def->opts.checks = NULL;

25. space_opts_destroy must do it, when Table object is destroyed.
> @@ -2100,7 +2115,8 @@ sqlite3ViewGetColumnNames(Parse * pParse, Table * pTable)
>   		db->lookaside.bDisable++;
>   		pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
>   		pParse->nTab = n;
> -		if (pTable->pCheck) {
> +		ExprList *checks = space_checks_expr_list(pTable->def->id);

26. Why you can not get checks from def->opts.checks?
> @@ -2562,8 +2577,8 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
>    fk_end:
>   	sqlite3DbFree(db, pFKey);
>   #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
> -	sqlite3ExprListDelete(db, pFromCol);
> -	sqlite3ExprListDelete(db, pToCol);
> +	sql_expr_list_free(db, pFromCol);
> +	sql_expr_list_free(db, pToCol);

27. The previous name was correct - delete, not free. Our naming convention is
new/delete for allocating and freeing objects, and create/destroy for initializing
and cleaning an object.
> @@ -2971,7 +2986,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   		struct field_def *field =
>   			&pTab->def->fields[pTab->def->field_count - 1];
>   		sqlite3TokenInit(&prevCol, field->name);
> -		pList = sqlite3ExprListAppend(pParse, 0,
> +		pList = sql_expr_list_append(pParse->db, 0,
>   					      sqlite3ExprAlloc(db, TK_ID,

28. Now the indentation is wrong.
> @@ -3024,8 +3039,8 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   	pIndex->nColumn = pList->nExpr;
>   	/* Tarantool have access to each column by any index */
>   	if (pPIWhere) {
> -		sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere,
> -					    0);
> +		sql_resolve_self_reference(pParse, pTab, NC_PartIdx, pPIWhere,
> +					   0);

29. NULL, not 0. Last argument is a pointer. In other places too, please.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 1a883e2..32ad806 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -922,7 +922,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
>   
>    insert_cleanup:
>   	sqlite3SrcListDelete(db, pTabList);
> -	sqlite3ExprListDelete(db, pList);
> +	sql_expr_list_free(db, pList);
>   	sqlite3SelectDelete(db, pSelect);
>   	sqlite3IdListDelete(db, pColumn);
>   	sqlite3DbFree(db, aRegIdx);
> @@ -1183,10 +1183,10 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>   
>   	/* Test all CHECK constraints
>   	 */
> -#ifndef SQLITE_OMIT_CHECK
> -	if (pTab->pCheck && (user_session->sql_flags &
> +	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(pTab->tnum);
> +	ExprList *pCheck = space_checks_expr_list(space_id);

30. Why you can not get checks from pTab->def->opts.check?
> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index b078e20..b7f2f45 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -286,7 +286,7 @@ ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
>                                    {sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
>   ccons ::= UNIQUE onconf(R).      {sqlite3CreateIndex(pParse,0,0,0,R,0,0,0,0,
>                                      SQLITE_IDXTYPE_UNIQUE);}
> -ccons ::= CHECK LP expr(X) RP.   {sqlite3AddCheckConstraint(pParse,X.pExpr);}
> +ccons ::= CHECK LP expr(X) RP.   {sqlite3AddCheckConstraint(pParse,X.pExpr,&X);}

31. I see, that in all sqlite3AddCheckConstraint usage places you pass ExprSpan and
its pExpr field separately. Why? You can pass ExprSpan and get its pExpr field
inside sqlite3AddCheckConstraint.
> diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
> index bf729b7..3c2cd63 100644
> --- a/src/box/sql/resolve.c
> +++ b/src/box/sql/resolve.c
> @@ -530,10 +530,8 @@ notValid(Parse * pParse,	/* Leave error message here */
>   		const char *zIn = "partial index WHERE clauses";
>   		if (pNC->ncFlags & NC_IdxExpr)
>   			zIn = "index expressions";
> -#ifndef SQLITE_OMIT_CHECK

32. I see, that you try to delete SQLITE_OMIT_CHECK. It still exists in build.c.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 75699ae..44897af 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1911,7 +1911,6 @@ struct Table {
>   	Select *pSelect;	/* NULL for tables.  Points to definition if a view. */
>   	FKey *pFKey;		/* Linked list of all foreign keys in this table */
>   	char *zColAff;		/* String defining the affinity of each column */
> -	ExprList *pCheck;	/* All CHECK constraints */
>   	/*   ... also used as column name list in a VIEW */
>   	Hash idxHash;		/* All (named) indices indexed by name */
>   	int tnum;		/* Root BTree page for this table */
> @@ -2894,10 +2893,8 @@ struct Parse {
>   	Token constraintName;	/* Name of the constraint currently being parsed */
>   	int regRoot;		/* Register holding root page number for new objects */
>   	int nMaxArg;		/* Max args passed to user function by sub-program */
> -#ifdef SELECTTRACE_ENABLED

33. Can you extract this and sql_parser_create() moving into a little separate
commit?

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

* [tarantool-patches] Re: [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 2/4] sql: remove SQL fields from Table and Column Kirill Shcherbatov
@ 2018-05-17 17:25   ` n.pettik
  2018-05-18 15:35     ` Kirill Shcherbatov
  0 siblings, 1 reply; 20+ messages in thread
From: n.pettik @ 2018-05-17 17:25 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov, Vladislav Shpilevoy

Disclaimer:
Most of comments don’t seem to be vital.
You may skip them, if you disagree with them.
=======================================================================

> 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.

Where did you introduce this flag? AFAIR It has already been introduced
in space opts several patches ago.

> 4. Introduced sql_table_def_rebuild intended for collect
> fragmented with sql_field_retrieve space_def into memory
> located in one allocation.

Duplicate: in fact, it is fifth point.

> +static inline bool
> +nullable_action_is_nullable(enum on_conflict_action nullable_action)

Sounds confusing to me. Mb better call it just ‘action_is_nullable’?
It is just a suggestion.

> @@ -116,10 +104,10 @@ 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;

You don’t need carrying this line: it fits into one.

> +			if (e != NULL) {
> 				char *expr_pos_old = expr_pos;
> 				e = sql_expr_dup(sql_get(), e, 0, &expr_pos);
> 				assert(e != NULL);
> @@ -201,10 +189,10 @@ 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;

Either this.

> @@ -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);

And this one.

> +
> +		struct field_def *field = &def->fields[i];
> +		const char *zToken = field->default_value;

Lest use Tarantool naming conventions, i.e. don’t use Hungarian notation.

> 		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_is_nullable(
> +				       def->fields[i].nullable_action));

Here smth wrong with indentations (and in other places where you use this assertion).

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

In tarantoolSqlite3MakeTableFormat() you declare such var as const.
Lets use everywhere const, or don’t use it at all.

> +
> +struct space_def *
> +sql_ephemeral_space_def_new(Parse *parser, const char *name)

Use ‘struct’ prefix for Parse struct: you did it in declaration, tho. (*)

Please, provide explanation, why you call it ‘ephemeral’.
I mean, this function is used both for real tables and those,
which, for instance, represent result set of selects.
Furthermore, there is some mess between sql_table_new and
sql_ephemeral_table_new(). I strongly recommend you to write
comments in code concerning these points.

> +{
> +	struct space_def *def = NULL;

Why do you need to declare it beforehand?
You can write this way:
struct space_def *def = (struct space_def *)region_alloc(region, size);

> +	struct region *region = &fiber()->gc;
> +	size_t name_len = name != NULL ? strlen(name) : 0;
> +	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),
> +			"region_alloc", "sql_ephemeral_space_def_new");
> +		parser->rc = SQL_TARANTOOL_ERROR;
> +		parser->nErr++;
> +		return NULL;

You can avoid passing parse context as an argument,
and check return value. Wouldn’t it be better?

> +	}
> +
> +	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)

The same as (*).

> diff --git a/src/box/sql.h b/src/box/sql.h
> index db92d80..3c26492 100644
> --- a/src/box/sql.h
> +++ b/src/box/sql.h
> @@ -65,6 +65,7 @@ sql_get();
> struct Expr;
> struct Parse;
> struct Select;
> +struct Table;
> 
> /**
>  * Perform parsing of provided expression. This is done by
> @@ -143,6 +144,37 @@ sql_expr_dup(struct sqlite3 *db, struct Expr *p, int flags, char **buffer);
> void
> sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
> 
> +/**
> + * Create and initialize a new ephemeral SQL Table object.
> + * @param parser SQL Parser object.
> + * @param name Table to create name.

Did you mean ’Name of table to be created’?

> + * @retval NULL on memory allocation error, Parser state changed.
> + * @retval not NULL on success.
> + */

I may already mention, but don’t write comments
'just for comments’. I think it is a good place to explain,
for example, difference between ephemeral (btw, I don’t like this name)
table and ‘ordinary’ one.

> +struct Table *
> +sql_ephemeral_table_new(struct Parse *parser, const char *name);
> +
> +/**
> + * Create and initialize a new ephemeral space_def object.
> + * @param parser SQL Parser object.
> + * @param name Table to create name.

The same.

> @@ -388,17 +370,14 @@ deleteTable(sqlite3 * db, Table * pTable)
> 	/* Delete the Table structure itself.
> 	 */
> 	sqlite3HashClear(&pTable->idxHash);
> -	sqlite3DeleteColumnNames(db, pTable);
> -	sqlite3DbFree(db, pTable->zName);
> +	sqlite3DbFree(db, pTable->aCol);
> 	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)

You would better describe somewhere that you are using
this field as a sign of def allocated on region.
It can be misleading since it is used for ephemeral spaces
which def if allocated on regular malloc.

> @@ -675,42 +646,50 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
> 	sqlite3 *db = pParse->db;
> 	if ((p = pParse->pNewTable) == 0)
> 		return;
> +	assert(p->def->opts.temporary);

The same is here: add comment explaining this assert.

> @@ -1937,7 +1920,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));

Too many same assertions in one function. Do you really need them all?

> 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 */

Don’t put comment right to code: move it up.

> 
> diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
> index 2ab8751..f949709 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;
> -	}
> -}

I would mention in commit message about the fact that
now almost within parsing context is allocated on region,
and at the end of parsing region is truncated. Or, at least,
in comments to sql_parser_destroy()/sql_parser_create()

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

Redundant empty line.

> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 5a50413..e08d709 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++) {

You can move definition of i inside ‘for' cycle:
for (uint32_t i = 0; ...)

> @@ -1819,13 +1818,31 @@ sqlite3ColumnsFromExprList(Parse * pParse,	/* Parsing context */
> 		testcase(aCol == 0);
> 	} else {
> 		nCol = 0;
> -		aCol = 0;
> +		aCol = NULL;
> 	}
> 	assert(nCol == (i16) nCol);
> -	*pnCol = nCol;
> -	*paCol = aCol;
> 
> -	for (i = 0, pCol = aCol; i < nCol && !db->mallocFailed; i++, pCol++) {
> +	/*
> +	 * This should be a table without resolved columns.
> +	 * sqlite3ViewGetColumnNames could use it to resolve
> +	 * names for existing table.
> +	 */
> +	assert(pTable->def->fields == NULL);
> +	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. */

Math? I can’t understand this comment. Rephrase it pls.

> +	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; i++, pCol++) {
> 		/* Get an appropriate name for the column
> 		 */
> 		p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
> @@ -1845,7 +1862,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 +1891,34 @@ 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);

Why do you declare it as uin32_t and do cast?
region_alloc and memcpy take it as size_t arg, btw.

> @@ -4683,18 +4711,29 @@ selectExpander(Walker * pWalker, Select * p)
> 			assert(pFrom->pTab == 0);
> 			if (sqlite3WalkSelect(pWalker, pSel))
> 				return WRC_Abort;
> +			/*
> +			 * Will be overwritten with pointer as
> +			 * unique identifier.
> +			 */
> +			const char *name = "sqlite_sq_DEADBEAFDEADBEAF”;

Lest don’t use sqlite prefixes. Just call it ‘unused’.

> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index c77aa9b..279c3af 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;
> +

Redundant empty line.

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

* [tarantool-patches] Re: [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
       [not found]   ` <26E4269B-2BCB-42C3-8216-D51E290E4723@corp.mail.ru>
@ 2018-05-18 15:26     ` Kirill Shcherbatov
  2018-05-18 17:04       ` n.pettik
  2018-05-21 12:48       ` [tarantool-patches] " Nikita Pettik
  0 siblings, 2 replies; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-18 15:26 UTC (permalink / raw)
  To: n.pettik, tarantool-patches; +Cc: Vladislav Shpilevoy

> Probably, you have missed some word (‘use’, ‘replace’ or whatever)
> 
> Sorry, but I can’t parse this commit message. Lets dive a little bit into details
> and explain what this patch *really* does. Such as:
> “Move affinity filed to struct field in order to bla-bla”.
> 
> The rest is almost okay, a few minor remarks.
```
    sql: space_def* instead of Table* in Expr
    
    Changed Table pointer to space_def in Expr structure.
    This makes able to resolve self reference in DDL before
    the table object is created.
    
    Part of #3272.
```
> I would better use enum affinity_type than char.
@@ -1468,7 +1468,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
-                       char affinity = def->fields[i].affinity;
+                       enum affinity_type affinity = def->fields[i].affinity;

@@ -1594,7 +1594,7 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
-                       char affinity = def->fields[col].affinity;
+                       enum affinity_type affinity = def->fields[col].affinity;

@@ -1801,8 +1801,10 @@ xferOptimization(Parse * pParse, /* Parser context */
-               char pdest_affinity = pDest->def->fields[i].affinity;
-               char psrc_affinity = pSrc->def->fields[i].affinity;
+               enum affinity_type pdest_affinity =
+                       pDest->def->fields[i].affinity;
+               enum affinity_type psrc_affinity =
+                       pSrc->def->fields[i].affinity;


> If tab1 is what?

-               /* The tab2 must be NOT NULL if tab1 is */

Invalid with introducing is_nullable.

> Wait, why did you delete assert which had been introduced in the previous patch?
Because I've changed func. prototype.

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

* [tarantool-patches] Re: [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-17 17:25   ` [tarantool-patches] " n.pettik
@ 2018-05-18 15:35     ` Kirill Shcherbatov
  2018-05-18 17:24       ` n.pettik
  0 siblings, 1 reply; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-18 15:35 UTC (permalink / raw)
  To: tarantool-patches, n.pettik; +Cc: v.shpilevoy

> Where did you introduce this flag? AFAIR It has already been introduced
> in space opts several patches ago.
> Duplicate: in fact, it is fifth point.
Updated commit message.

> Sounds confusing to me. Mb better call it just ‘action_is_nullable’?
> It is just a suggestion.

+++ b/src/box/field_def.h
@@ -131,7 +131,7 @@ struct field_def {
 };
 
 static inline bool
-nullable_action_is_nullable(enum on_conflict_action nullable_action)
+action_is_nullable(enum on_conflict_action nullable_action)

etc.

> You don’t need carrying this line: it fits into one.
> Either this.
@@ -105,8 +105,7 @@ space_def_dup(const struct space_def *src)
-                       struct Expr *e =
-                               src->fields[i].default_value_expr;
+                       struct Expr *e = src->fields[i].default_value_expr;

@@ -190,8 +189,7 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
-                       struct Expr *e =
-                               fields[i].default_value_expr;
+                       struct Expr *e = fields[i].default_value_expr;

> And this one.
@@ -1448,8 +1448,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 
        for (i = 0; i < n; i++) {
                const char *t;
-               struct coll *coll =
-                       coll_by_id(pTable->def->fields[i].coll_id);
+               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;
>
> Lest use Tarantool naming conventions, i.e. don’t use Hungarian notation.
@@ -1448,15 +1448,14 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
 
        for (i = 0; i < n; i++) {
                const char *t;
-               struct coll *coll =
-                       coll_by_id(pTable->def->fields[i].coll_id);
+               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;
+               const char *default_str = field->default_value;
                int base_len = 4;
                if (coll != NULL)
                        base_len += 1;
-               if (zToken != NULL)
+               if (default_str != NULL)
                        base_len += 1;
                p = enc->encode_map(p, base_len);
                p = enc->encode_str(p, "name", 4);
@@ -1487,9 +1486,9 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
                        p = enc->encode_str(p, "collation", strlen("collation"));
                        p = enc->encode_uint(p, coll->id);
                }
-               if (zToken != NULL) {
+               if (default_str != NULL) {
                        p = enc->encode_str(p, "default", strlen("default"));
-                       p = enc->encode_str(p, zToken, strlen(zToken));
+                       p = enc->encode_str(p, default_str, strlen(default_str));
                }
        }
        return (int)(p - base);

> Here smth wrong with indentations (and in other places where you use this assertion).
@@ -1463,8 +1463,7 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
                assert(def->fields[i].is_nullable ==
-                              action_is_nullable(
-                                      def->fields[i].nullable_action));
+                      action_is_nullable(def->fields[i].nullable_action));
 
@@ -1558,8 +1557,7 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
                assert(def->fields[col].is_nullable ==
-                              action_is_nullable(
-                                      def->fields[col].nullable_action));
+                      action_is_nullable(def->fields[col].nullable_action));

@@ -200,9 +200,8 @@ sqlite3AlterFinishAddColumn(Parse * pParse, Token * pColDef)
        assert(pNew->def->fields[pNew->def->field_count - 1].is_nullable ==
-                      action_is_nullable(
-                              pNew->def->fields[pNew->def->field_count -
-                                                1].nullable_action));
+              action_is_nullable(pNew->def->fields[
+               pNew->def->field_count - 1].nullable_action));

>
>> +
>> +struct space_def *
>> +sql_ephemeral_space_def_new(Parse *parser, const char *name)
>
> Use ‘struct’ prefix for Parse struct: you did it in declaration, tho. (*)
@@ -1696,7 +1696,7 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
 struct space_def *
-sql_ephemeral_space_def_new(Parse *parser, const char *name)
+sql_ephemeral_space_def_new(struct Parse *parser, const char *name)

@@ -1720,7 +1720,7 @@ sql_ephemeral_space_def_new(Parse *parser, const char *name)
-Table *
-sql_ephemeral_table_new(Parse *parser, const char *name)
+struct Table *
+sql_ephemeral_table_new(struct Parse *parser, const char *name)

>
> Please, provide explanation, why you call it ‘ephemeral’.
> I mean, this function is used both for real tables and those,
> which, for instance, represent result set of selects.
> Furthermore, there is some mess between sql_table_new and
> sql_ephemeral_table_new(). I strongly recommend you to write
> comments in code concerning these points.

@@ -146,6 +146,12 @@ sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
 
 /**
  * Create and initialize a new ephemeral SQL Table object.
+ * All memory allocation operations except Table object itself
+ * are performed in the region.
+ *
+ * The 'ephemeral' means that this memory is temporal,
+ * so the table should be rebuild with sql_table_def_rebuild for further use.
+ *
  * @param parser SQL Parser object.
  * @param name Table to create name.
  * @retval NULL on memory allocation error, Parser state changed.
@@ -156,6 +162,11 @@ sql_ephemeral_table_new(struct Parse *parser, const char *name);
 
 /**
  * Create and initialize a new ephemeral space_def object.
+ * All memory allocation operations are performed on the region.
+ *
+ * The 'ephemeral' means that this memory is temporal,
+ * so the table should be rebuild with sql_table_def_rebuild for further use.
+ *
  * @param parser SQL Parser object.
  * @param name Table to create name.
  * @retval NULL on memory allocation error, Parser state changed.

> Why do you need to declare it beforehand?
> You can write this way:
> struct space_def *def = (struct space_def *)region_alloc(region, size);
@@ -1698,12 +1698,11 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
struct space_def *
sql_ephemeral_space_def_new(struct Parse *parser, const char *name)
{
- struct space_def *def = NULL;
struct region *region = &fiber()->gc;
size_t name_len = name != NULL ? strlen(name) : 0;
uint32_t dummy;
size_t size = space_def_sizeof(name_len, NULL, 0, &dummy, &dummy, &dummy);
- def = (struct space_def *)region_alloc(region, size);
+ struct space_def *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");

> The same as (*).@@ -1740,12 +1739,12 @@ int
 sql_table_def_rebuild(struct sqlite3 *db, struct Table *pTable)
 {
        struct space_def *old_def = pTable->def;
-       struct space_def *new_def = NULL;
-       new_def = space_def_new(old_def->id, old_def->uid,
-                               old_def->field_count, old_def->name,
-                               strlen(old_def->name), old_def->engine_name,
-                               strlen(old_def->engine_name), &old_def->opts,
-                               old_def->fields, old_def->field_count);
+       struct space_def *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);
        
>
> Did you mean ’Name of table to be created’?
- * @param name Table to create name.
+ * @param name Name of table to be created.

> I may already mention, but don’t write comments
> 'just for comments’. I think it is a good place to explain,
> for example, difference between ephemeral (btw, I don’t like this name)
> table and ‘ordinary’ one.
Accounted above.

>> +	if (!pTable->def->opts.temporary)
>
> You would better describe somewhere that you are using
> this field as a sign of def allocated on region.
> It can be misleading since it is used for ephemeral spaces
> which def if allocated on regular malloc.
+++ b/src/box/space_def.h
@@ -47,6 +47,8 @@ struct space_opts {
         * - it is empty at server start
         * - changes are not written to WAL
         * - changes are not part of a snapshot
+        * - in SQL: space_def memory is allocated on region and
+        *   does not require manual release.
         */

@@ -166,6 +167,7 @@ sql_ephemeral_table_new(struct Parse *parser, const char *name);
  *
  * The 'ephemeral' means that this memory is temporal,
  * so the table should be rebuild with sql_table_def_rebuild for further use.
+ * (opts.temporary flag is set 'true' to indicate this property)

> The same is here: add comment explaining this assert.
@@ -646,7 +646,6 @@ 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 ((int)p->def->field_count + 1 > db->aLimit[SQLITE_LIMIT_COLUMN]) {
                sqlite3ErrorMsg(pParse, "too many columns on %s",
@@ -654,6 +653,12 @@ sqlite3AddColumn(Parse * pParse, Token * pName, Token * pType)
                return;
        }
 #endif
+       /*
+        * As sql_field_retrieve will allocate memory on region
+        * ensure that p->def is also temporal and would be rebuilded or
+        * dropped.
+        */
+       assert(p->def->opts.temporary);
        if (sql_field_retrieve(pParse, p,
                               (uint32_t) p->def->field_count) == NULL)
                return;

>
>> @@ -1937,7 +1920,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));
>
> Too many same assertions in one function. Do you really need them all?
@@ -1895,7 +1895,6 @@ sqlite3EndTable(Parse * pParse,   /* Parse context */
                if (pSelect) {
                        zStmt = createTableStmt(db, p);
                } else {
-                       assert(p->def->opts.is_view == (p->pSelect != NULL));
                       
@@ -1956,7 +1955,6 @@ sqlite3EndTable(Parse * pParse,   /* Parse context */
 #ifndef SQLITE_OMIT_ALTERTABLE
-               assert(p->def->opts.is_view == (p->pSelect != NULL));

>
>> 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 */
>
> Don’t put comment right to code: move it up.
@@ -2238,7 +2238,8 @@ isCandidateForInOpt(Expr * pX)
        assert(pTab->def->opts.is_view == (pTab->pSelect != NULL));
-       assert(!pTab->def->opts.is_view);       /* FROM clause is not a view */
+       /* FROM clause is not a view */
+       assert(!pTab->def->opts.is_view);

> I would mention in commit message about the fact that
> now almost within parsing context is allocated on region,
> and at the end of parsing region is truncated. Or, at least,
> in comments to sql_parser_destroy()/sql_parser_create()
@@ -4157,6 +4157,8 @@ table_column_nullable_action(struct Table *tab, uint32_t column);
 
 /**
  * Initialize a new parser object.
+ * A number of service allocations are performed on the region, which is also
+ * cleared in the destroy function.
  * @param parser object to initialize.

>> +	/* NULL nullable_action should math is_nullable flag. */
> Math? I can’t understand this comment. Rephrase it pls.
-       /* NULL nullable_action should math is_nullable flag. */
+       /*
+        * Set is_nullable flag for fields with default NULL nullable_action to
+        * be consistent.
+        */

> Why do you declare it as uin32_t and do cast?
> region_alloc and memcpy take it as size_t arg, btw.
-               uint32_t name_len = (uint32_t)strlen(zName);
+               size_t name_len = strlen(zName);

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

* [tarantool-patches] Re: [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
  2018-05-18 15:26     ` Kirill Shcherbatov
@ 2018-05-18 17:04       ` n.pettik
  2018-05-21 12:48       ` [tarantool-patches] " Nikita Pettik
  1 sibling, 0 replies; 20+ messages in thread
From: n.pettik @ 2018-05-18 17:04 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov


>> Probably, you have missed some word (‘use’, ‘replace’ or whatever)
>> 
>> Sorry, but I can’t parse this commit message. Lets dive a little bit into details
>> and explain what this patch *really* does. Such as:
>> “Move affinity filed to struct field in order to bla-bla”.
>> 
>> The rest is almost okay, a few minor remarks.
> ```
>    sql: space_def* instead of Table* in Expr

This message still lacks a verb. Such as:
sql: use space_def* instead Table* in Expr
OR
sql: replace Table* with space_def* in Expr

> 
>    Changed Table pointer to space_def in Expr structure.
>    This makes able to resolve self reference in DDL before

I guess, you lost ’t’ or smth else..

>> Wait, why did you delete assert which had been introduced in the previous patch?
> Because I've changed func. prototype.

Well, I mean anyway these patches come together, so it makes no sense
to fix code (especially asserts) introduced in n-th commit by code in (n+1)-th -
it is just redundant diff (in most cases).

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

* [tarantool-patches] Re: [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-18 15:35     ` Kirill Shcherbatov
@ 2018-05-18 17:24       ` n.pettik
  2018-05-18 19:45         ` Kirill Shcherbatov
  0 siblings, 1 reply; 20+ messages in thread
From: n.pettik @ 2018-05-18 17:24 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov


> @@ -146,6 +146,12 @@ sql_expr_free(struct sqlite3 *db, struct Expr *expr, bool extern_alloc);
> 
> /**
>  * Create and initialize a new ephemeral SQL Table object.
> + * All memory allocation operations except Table object itself
> + * are performed in the region.
> + *
> + * The 'ephemeral' means that this memory is temporal,
> + * so the table should be rebuild with sql_table_def_rebuild for further use.

Fit comments in 66 chars. Moreover, I see that you accidentally included
changes concerning this comment to the next patch.
Lets return it back to this one.

> + *
>  * @param parser SQL Parser object.
>  * @param name Table to create name.
>  * @retval NULL on memory allocation error, Parser state changed.
> @@ -156,6 +162,11 @@ sql_ephemeral_table_new(struct Parse *parser, const char *name);
> 
> /**
>  * Create and initialize a new ephemeral space_def object.
> + * All memory allocation operations are performed on the region.
> + *
> + * The 'ephemeral' means that this memory is temporal,
> + * so the table should be rebuild with sql_table_def_rebuild for further use.

Lets don’t repeat the same comment twice. You may refer to it, if you want.

>> I would mention in commit message about the fact that
>> now almost within parsing context is allocated on region,
>> and at the end of parsing region is truncated. Or, at least,
>> in comments to sql_parser_destroy()/sql_parser_create()
> @@ -4157,6 +4157,8 @@ table_column_nullable_action(struct Table *tab, uint32_t column);
> 
> /**
>  * Initialize a new parser object.
> + * A number of service allocations are performed on the region, which is also

Fit comment in 66 chars.

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

* [tarantool-patches] Re: [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-18 17:24       ` n.pettik
@ 2018-05-18 19:45         ` Kirill Shcherbatov
  2018-05-18 20:13           ` n.pettik
  0 siblings, 1 reply; 20+ messages in thread
From: Kirill Shcherbatov @ 2018-05-18 19:45 UTC (permalink / raw)
  To: tarantool-patches, n.pettik; +Cc: v.shpilevoy

> Fit comments in 66 chars. Moreover, I see that you accidentally included
> changes concerning this comment to the next patch.
> Lets return it back to this one.
> 
> Lets don’t repeat the same comment twice. You may refer to it, if you want.
> 
> Fit comment in 66 chars.

Ok, I've fixed everything.

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

* [tarantool-patches] Re: [PATCH v6 2/4] sql: remove SQL fields from Table and Column
  2018-05-18 19:45         ` Kirill Shcherbatov
@ 2018-05-18 20:13           ` n.pettik
  0 siblings, 0 replies; 20+ messages in thread
From: n.pettik @ 2018-05-18 20:13 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov


> Ok, I've fixed everything.

If so, I am OK with this patch.

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

* [tarantool-patches] Re: [tarantool-patches] Re: [PATCH v6 3/4] sql: space_def* instead of Table* in Expr
  2018-05-18 15:26     ` Kirill Shcherbatov
  2018-05-18 17:04       ` n.pettik
@ 2018-05-21 12:48       ` Nikita Pettik
  1 sibling, 0 replies; 20+ messages in thread
From: Nikita Pettik @ 2018-05-21 12:48 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Yukhin, Kirill Shcherbatov

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


Hello. I took a look again at your branch. Now this patch seems to be OK.

-- 
Nikita Pettik

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

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

* [tarantool-patches] Re: [PATCH v6 0/4] sql: moved Checks to server
  2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
                   ` (4 preceding siblings ...)
  2018-05-16 11:52 ` [tarantool-patches] Re: [PATCH v6 0/4] sql: moved " Vladislav Shpilevoy
@ 2018-05-23  5:19 ` Kirill Yukhin
  5 siblings, 0 replies; 20+ messages in thread
From: Kirill Yukhin @ 2018-05-23  5:19 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

Hi,
On 15 мая 20:03, Kirill Shcherbatov wrote:
> Branch: http://github.com/tarantool/tarantool/tree/gh-3272-no-sql-checks
> Issue: https://github.com/tarantool/tarantool/issues/3272
> 
> Kirill Shcherbatov (4):
>   sql: fix code style in sqlite3Pragma
>   sql: remove SQL fields from Table and Column
>   sql: space_def* instead of Table* in Expr
>   sql: remove Checks to server
I've checked in the patch set into 2.0

--
Regards, Kirill Yukhin

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

end of thread, other threads:[~2018-05-23  5:19 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved Checks to server Kirill Shcherbatov
2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 1/4] sql: fix code style in sqlite3Pragma Kirill Shcherbatov
2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 2/4] sql: remove SQL fields from Table and Column Kirill Shcherbatov
2018-05-17 17:25   ` [tarantool-patches] " n.pettik
2018-05-18 15:35     ` Kirill Shcherbatov
2018-05-18 17:24       ` n.pettik
2018-05-18 19:45         ` Kirill Shcherbatov
2018-05-18 20:13           ` n.pettik
2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 3/4] sql: space_def* instead of Table* in Expr Kirill Shcherbatov
2018-05-16 12:33   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-16 13:10     ` Kirill Shcherbatov
2018-05-16 13:11       ` Vladislav Shpilevoy
     [not found]   ` <26E4269B-2BCB-42C3-8216-D51E290E4723@corp.mail.ru>
2018-05-18 15:26     ` Kirill Shcherbatov
2018-05-18 17:04       ` n.pettik
2018-05-21 12:48       ` [tarantool-patches] " Nikita Pettik
2018-05-15 17:03 ` [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server Kirill Shcherbatov
2018-05-16 17:59   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-16 11:52 ` [tarantool-patches] Re: [PATCH v6 0/4] sql: moved " Vladislav Shpilevoy
2018-05-16 13:13   ` Kirill Shcherbatov
2018-05-23  5:19 ` Kirill Yukhin

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