Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org
Cc: v.shpilevoy@tarantool.org,
	Kirill Shcherbatov <kshcherbatov@tarantool.org>
Subject: [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server
Date: Tue, 15 May 2018 20:03:21 +0300	[thread overview]
Message-ID: <4898952651385c13e1d4458e7020a23353bd4446.1526403792.git.kshcherbatov@tarantool.org> (raw)
In-Reply-To: <cover.1526403792.git.kshcherbatov@tarantool.org>
In-Reply-To: <cover.1526403792.git.kshcherbatov@tarantool.org>

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

  parent reply	other threads:[~2018-05-15 17:03 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-15 17:03 [tarantool-patches] [PATCH v6 0/4] sql: moved " 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 ` Kirill Shcherbatov [this message]
2018-05-16 17:59   ` [tarantool-patches] Re: [PATCH v6 4/4] sql: remove Checks to server 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

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=4898952651385c13e1d4458e7020a23353bd4446.1526403792.git.kshcherbatov@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [tarantool-patches] [PATCH v6 4/4] sql: remove Checks to server' \
    /path/to/YOUR_REPLY

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

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

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