[tarantool-patches] Re: [PATCH v2 9/9] sql: run check constraint tests on space alter

Kirill Shcherbatov kshcherbatov at tarantool.org
Tue Mar 26 13:59:29 MSK 2019


Introduced reusable pre-compiled VDBE programs for ck constraints
and space trigger to fire checks on insert and update operations.

Closes #3691

@TarantoolBot document
Title: check constraint for LUA space

Now it is possible to create check constraints for LUA spaces:
insert the tuple {<CONSTRAINT NAME>, <DST SPACE ID>, <EXPRESSION STRING>} in
the _ck_constraint space to create new check constraint.

Example:
s = box.schema.create_space('test')
s:create_index('pk')
format = {{'X', 'unsigned'}, {'Y', 'unsigned'}}
s:format(format)
box.space._ck_constraint:insert({'physics', s.id, 'X<Y'})
box.space.test:insert({6, 5})
- error: 'Check constraint failed: physics'
---
 src/box/alter.cc         |  21 +++++-
 src/box/ck_constraint.c  | 142 ++++++++++++++++++++++++++++++++++++++-
 src/box/ck_constraint.h  |  24 +++++--
 src/box/sql.c            |   1 +
 src/box/sql/insert.c     |  94 ++++++--------------------
 src/box/sql/sqlInt.h     |  25 +++++++
 src/box/sql/vdbeapi.c    |   8 ---
 test/sql/checks.result   |  20 ++++++
 test/sql/checks.test.lua |   5 ++
 9 files changed, 248 insertions(+), 92 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 9aa5e3653..b6d1c8537 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -1379,6 +1379,11 @@ BuildCkConstraints::alter(struct alter_space *alter)
 {
 	rlist_swap(&alter->new_space->ck_constraint, &ck_constraint);
 	rlist_swap(&ck_constraint, &alter->old_space->ck_constraint);
+	struct ck_constraint *ck;
+	rlist_foreach_entry(ck, &ck_constraint, link)
+		trigger_clear(&ck->trigger);
+	rlist_foreach_entry(ck, &alter->new_space->ck_constraint, link)
+		trigger_add(&alter->new_space->before_replace, &ck->trigger);
 }
 
 void
@@ -1386,6 +1391,11 @@ BuildCkConstraints::rollback(struct alter_space *alter)
 {
 	rlist_swap(&alter->old_space->ck_constraint, &ck_constraint);
 	rlist_swap(&ck_constraint, &alter->new_space->ck_constraint);
+	struct ck_constraint *ck;
+	rlist_foreach_entry(ck, &ck_constraint, link)
+		trigger_clear(&ck->trigger);
+	rlist_foreach_entry(ck, &alter->old_space->ck_constraint, link)
+		trigger_add(&alter->old_space->before_replace, &ck->trigger);
 }
 
 BuildCkConstraints::~BuildCkConstraints()
@@ -4166,10 +4176,12 @@ on_replace_ck_constraint_rollback(struct trigger *trigger, void *event)
 			return;
 		assert(space != NULL);
 		rlist_add_entry(&space->ck_constraint, ck_constraint, link);
+		trigger_add(&space->before_replace, &ck_constraint->trigger);
 	}  else if (stmt->new_tuple != NULL && stmt->old_tuple == NULL) {
 		/* Rollback INSERT check constraint. */
 		assert(space != NULL);
 		rlist_del_entry(ck_constraint, link);
+		trigger_clear(&ck_constraint->trigger);
 		ck_constraint_delete(ck_constraint);
 	} else {
 		/* Rollback REPLACE check constraint. */
@@ -4180,7 +4192,9 @@ on_replace_ck_constraint_rollback(struct trigger *trigger, void *event)
 						    strlen(space_name));
 		assert(new_ck_constraint != NULL);
 		rlist_del_entry(new_ck_constraint, link);
+		trigger_clear(&new_ck_constraint->trigger);
 		rlist_add_entry(&space->ck_constraint, ck_constraint, link);
+		trigger_add(&space->before_replace, &ck_constraint->trigger);
 		ck_constraint_delete(new_ck_constraint);
 	}
 }
@@ -4232,9 +4246,13 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 		struct ck_constraint *old_ck_constraint =
 			space_ck_constraint_by_name(space, space_name,
 						    strlen(space_name));
-		if (old_ck_constraint != NULL)
+		if (old_ck_constraint != NULL) {
 			rlist_del_entry(old_ck_constraint, link);
+			trigger_clear(&old_ck_constraint->trigger);
+		}
 		rlist_add_entry(&space->ck_constraint, new_ck_constraint, link);
+		trigger_add(&space->before_replace,
+			    &new_ck_constraint->trigger);
 		on_commit->data = old_ck_constraint;
 		on_rollback->data = old_tuple == NULL ? new_ck_constraint :
 							old_ck_constraint;
@@ -4250,6 +4268,7 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 			space_ck_constraint_by_name(space, name, name_len);
 		assert(old_ck_constraint != NULL);
 		rlist_del_entry(old_ck_constraint, link);
+		trigger_clear(&old_ck_constraint->trigger);
 		on_commit->data = old_ck_constraint;
 		on_rollback->data = old_ck_constraint;
 	}
diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index 110098efc..c2e8547f1 100644
--- a/src/box/ck_constraint.c
+++ b/src/box/ck_constraint.c
@@ -29,11 +29,16 @@
  * SUCH DAMAGE.
  */
 #include <assert.h>
+#include "bind.h"
 #include "ck_constraint.h"
 #include "errcode.h"
+#include "session.h"
+#include "schema.h"
 #include "small/rlist.h"
+#include "tuple.h"
 #include "sql.h"
 #include "sql/sqlInt.h"
+#include "sql/vdbeInt.h"
 
 /**
  * Resolve space_def references for check constraint via AST
@@ -84,6 +89,130 @@ ck_constraint_def_create(struct ck_constraint_def *ck_constraint_def,
 	rlist_create(&ck_constraint_def->link);
 }
 
+/**
+ * Compile constraint check subroutine.
+ * @param ck_constraint Check constraint to compile.
+ * @param expr Check constraint expression AST is built for
+ *             ck_constraint->def.
+ * @param space_def The space definition of the space this check
+ *                  constraint is constructed for.
+ * @retval not NULL sql_stmt program pointer on success.
+ * @retval NULL otherwise.
+ */
+int
+ck_constraint_test_compile(struct ck_constraint *ck_constraint,
+			   struct Expr *expr, const struct space_def *space_def)
+{
+	int rc = -1;
+	assert(ck_constraint->space_id == space_def->id);
+	struct Parse parser;
+	sql_parser_create(&parser, sql_get());
+	struct Vdbe *v = sqlGetVdbe(&parser);
+	if (v == NULL) {
+		diag_set(OutOfMemory, sizeof(struct Vdbe),
+			 "sqlGetVdbe", "vdbe");
+		goto end;
+	}
+
+	/* Compile VDBE with default sql parameters. */
+	struct session *user_session = current_session();
+	uint32_t sql_flags = user_session->sql_flags;
+	user_session->sql_flags = default_flags;
+
+	/*
+	 * Generate a prologue code to bind variable new_tuple_var
+	 * to new_tuple_reg.
+	 */
+	uint32_t field_count = space_def->field_count;
+	int new_tuple_reg = sqlGetTempRange(&parser, field_count);
+	struct Expr bind = {.op = TK_VARIABLE, .u.zToken = "?"};
+	ck_constraint->new_tuple_var = parser.nVar + 1;
+	for (uint32_t i = 0; i < field_count; i++) {
+		sqlExprAssignVarNumber(&parser, &bind, 1);
+		sqlExprCodeTarget(&parser, &bind, new_tuple_reg + i);
+	}
+	vdbe_emit_ck_constraint(&parser, expr, ck_constraint->def->name,
+				new_tuple_reg);
+	sql_finish_coding(&parser);
+	if (parser.is_aborted) {
+		diag_set(ClientError, ER_CREATE_CK_CONSTRAINT,
+			 ck_constraint->def->name,
+			 "can not compile expression");
+		goto end;
+	}
+	sql_parser_destroy(&parser);
+
+	/* Restore original sql flags for user_session.  */
+	user_session->sql_flags = sql_flags;
+	ck_constraint->stmt = (struct sql_stmt *)v;
+	rc = 0;
+end:
+	return rc;
+}
+
+/**
+ * Perform ck constraint checks with new tuple data new_tuple_raw
+ * before insert or replace in space space_def.
+ * @param ck_constraint Check constraint to test.
+ * @param space_def The space definition of the space this check
+ *                  constraint is constructed for.
+ * @param new_tuple_raw The tuple to be inserted in space.
+ * @retval 0 if check constraint test is passed, -1 otherwise.
+ */
+static int
+ck_constraint_test(struct ck_constraint *ck_constraint,
+		   struct space_def *space_def, const char *new_tuple_raw)
+{
+	assert(new_tuple_raw != NULL);
+	/*
+	 * Prepare parameters for checks->stmt execution:
+	 * Unpacked new tuple fields mapped to Vdbe memory from
+	 * variables from range:
+	 * [new_tuple_var,new_tuple_var+field_count]
+	 */
+	mp_decode_array(&new_tuple_raw);
+	/* Reset VDBE to make new bindings. */
+	sql_reset(ck_constraint->stmt);
+	for (uint32_t i = 0; i < space_def->field_count; i++) {
+		struct sql_bind bind;
+		if (sql_bind_decode(&bind, ck_constraint->new_tuple_var + i,
+				    &new_tuple_raw) != 0)
+			return -1;
+		if (sql_bind_column(ck_constraint->stmt, &bind,
+				    ck_constraint->new_tuple_var + i) != 0)
+			return -1;
+	}
+	/* Checks VDBE can't expire, reset expired flag & Burn. */
+	struct Vdbe *v = (struct Vdbe *)ck_constraint->stmt;
+	v->expired = 0;
+	int rc;
+	while ((rc = sql_step(ck_constraint->stmt)) == SQL_ROW) {}
+	if (v->rc != SQL_DONE && v->rc != SQL_TARANTOOL_ERROR)
+		diag_set(ClientError, ER_SQL, v->zErrMsg);
+	return rc == SQL_DONE ? 0 : -1;
+}
+
+/**
+ * Trigger routine executing ck constraint check on space
+ * insert and replace.
+ */
+static void
+ck_constraint_space_trigger(struct trigger *trigger, void *event)
+{
+	struct ck_constraint *ck_constraint =
+		(struct ck_constraint *)trigger->data;
+	struct space *space = space_by_id(ck_constraint->space_id);
+	assert(space != NULL);
+	struct txn *txn = (struct txn *) event;
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	struct tuple *new_tuple = stmt->new_tuple;
+	if (stmt == NULL || new_tuple == NULL)
+		return;
+	if (ck_constraint_test(ck_constraint, space->def,
+			       tuple_data(new_tuple)) != 0)
+		diag_raise();
+}
+
 struct ck_constraint *
 ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
 		  struct space_def *space_def)
@@ -110,6 +239,8 @@ ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
 	ck_constraint_def_create(ck_constraint->def, ck_constraint_def->name,
 				 ck_constraint_name_len,
 				 ck_constraint_def->expr_str, expr_str_len);
+	trigger_create(&ck_constraint->trigger, ck_constraint_space_trigger,
+		       ck_constraint, NULL);
 	struct Expr *expr =
 		sql_expr_compile(sql_get(), ck_constraint_def->expr_str,
 				 expr_str_len);
@@ -120,18 +251,23 @@ ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
 			 box_error_message(box_error_last()));
 		goto error;
 	}
-	ck_constraint->expr = expr;
+	if (ck_constraint_test_compile(ck_constraint, expr, space_def) != 0)
+		goto error;
 
+end:
+	sql_expr_delete(sql_get(), expr, false);
 	return ck_constraint;
 error:
 	ck_constraint_delete(ck_constraint);
-	return NULL;
+	ck_constraint = NULL;
+	goto end;
 }
 
 void
 ck_constraint_delete(struct ck_constraint *ck_constraint)
 {
-	sql_expr_delete(sql_get(), ck_constraint->expr, false);
+	assert(rlist_empty(&ck_constraint->trigger.link));
+	sql_finalize(ck_constraint->stmt);
 	TRASH(ck_constraint);
 	free(ck_constraint);
 }
diff --git a/src/box/ck_constraint.h b/src/box/ck_constraint.h
index 02aa525ce..77b1a9bed 100644
--- a/src/box/ck_constraint.h
+++ b/src/box/ck_constraint.h
@@ -32,6 +32,7 @@
  */
 
 #include <stdint.h>
+#include "trigger.h"
 #include "small/rlist.h"
 
 #if defined(__cplusplus)
@@ -40,6 +41,7 @@ extern "C" {
 
 struct space;
 struct space_def;
+struct sql_stmt;
 struct Expr;
 
 /**
@@ -72,17 +74,29 @@ struct ck_constraint {
 	 */
 	struct ck_constraint_def *def;
 	/**
-	 * The check constraint expression AST is built for
-	 * ck_constraint::def::expr_str with sql_expr_compile
-	 * and resolved with sqlResolveExprNames for
-	 * space with space[ck_constraint::space_id] definition.
+	 * Precompiled reusable VDBE program for proceeding ck
+	 * constraint checks and setting bad exitcode and error
+	 * message when ck condition unsatisfied.
+	 * Program rely on new_tuple_var parameter to be binded
+	 * in the VDBE memory before run.
 	 */
-	struct Expr *expr;
+	struct sql_stmt *stmt;
 	/**
 	 * The id of the space this check constraint is
 	 * built for.
 	 */
 	uint32_t space_id;
+	/**
+	 * The first ck_constraint::stmt VDBE variable of the
+	 * range space[ck_constraint::space_id]->def->field_count
+	 * representing a new tuple to be inserted.
+	 */
+	int new_tuple_var;
+	/**
+	 * Trigger object executing check constraint on space
+	 * insert and replace.
+	 */
+	struct trigger trigger;
 	/**
 	 * Organize check constraint structs into linked list
 	 * with space::ck_constraint.
diff --git a/src/box/sql.c b/src/box/sql.c
index fc469126e..912b24bf0 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 #include <assert.h>
+#include "bind.h"
 #include "field_def.h"
 #include "cfg.h"
 #include "sql.h"
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 2fe74a027..3caad3c24 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -798,51 +798,26 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	sqlDbFree(db, aRegIdx);
 }
 
-/*
- * Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
- */
-#define CKCNSTRNT_COLUMN   0x01	/* CHECK constraint uses a changing column */
-
-/* This is the Walker callback from checkConstraintUnchanged().  Set
- * bit 0x01 of pWalker->eCode if
- * pWalker->eCode to 0 if this expression node references any of the
- * columns that are being modifed by an UPDATE statement.
- */
-static int
-checkConstraintExprNode(Walker * pWalker, Expr * pExpr)
-{
-	if (pExpr->op == TK_COLUMN) {
-		assert(pExpr->iColumn >= 0 || pExpr->iColumn == -1);
-		if (pExpr->iColumn >= 0) {
-			if (pWalker->u.aiCol[pExpr->iColumn] >= 0) {
-				pWalker->eCode |= CKCNSTRNT_COLUMN;
-			}
-		}
-	}
-	return WRC_Continue;
-}
-
-/*
- * pExpr is a CHECK constraint on a row that is being UPDATE-ed.  The
- * only columns that are modified by the UPDATE are those for which
- * aiChng[i]>=0.
- *
- * Return true if CHECK constraint pExpr does not use any of the
- * changing columns.  In other words, return true if this CHECK constraint
- * can be skipped when validating the new row in the UPDATE statement.
- */
-static int
-checkConstraintUnchanged(Expr * pExpr, int *aiChng)
+void
+vdbe_emit_ck_constraint(struct Parse *parser, struct Expr *expr,
+			const char *name, int new_tuple_reg)
 {
-	Walker w;
-	memset(&w, 0, sizeof(w));
-	w.eCode = 0;
-	w.xExprCallback = checkConstraintExprNode;
-	w.u.aiCol = aiChng;
-	sqlWalkExpr(&w, pExpr);
-	testcase(w.eCode == 0);
-	testcase(w.eCode == CKCNSTRNT_COLUMN);
-	return !w.eCode;
+	parser->ckBase = new_tuple_reg;
+	struct Vdbe *v = sqlGetVdbe(parser);
+	const char *ck_constraint_name = sqlDbStrDup(parser->db, name);
+	VdbeNoopComment((v, "BEGIN: ck constraint %s test", name));
+	/* Skip check when it is turned off. */
+	int all_is_ok = sqlVdbeMakeLabel(v);
+	sqlExprIfTrue(parser, expr, all_is_ok, SQL_JUMPIFNULL);
+	sqlMayAbort(parser);
+	const char *fmt = tnt_errcode_desc(ER_CK_CONSTRAINT_FAILED);
+	const char *error_msg = tt_sprintf(fmt, ck_constraint_name);
+	sqlVdbeAddOp4(v, OP_Halt, SQL_TARANTOOL_ERROR,
+		      ON_CONFLICT_ACTION_ABORT, 0,
+		      sqlDbStrDup(parser->db, error_msg), P4_DYNAMIC);
+	sqlVdbeChangeP5(v, ER_CK_CONSTRAINT_FAILED);
+	VdbeNoopComment((v, "END: ck constraint %s test", name));
+	sqlVdbeResolveLabel(v, all_is_ok);
 }
 
 void
@@ -912,37 +887,6 @@ vdbe_emit_constraint_checks(struct Parse *parse_context, struct space *space,
 			unreachable();
 		}
 	}
-	/*
-	 * For CHECK constraint and for INSERT/UPDATE conflict
-	 * action DEFAULT and ABORT in fact has the same meaning.
-	 */
-	if (on_conflict == ON_CONFLICT_ACTION_DEFAULT)
-		on_conflict = ON_CONFLICT_ACTION_ABORT;
-	/* Test all CHECK constraints. */
-	enum on_conflict_action on_conflict_check = on_conflict;
-	if (on_conflict == ON_CONFLICT_ACTION_REPLACE)
-		on_conflict_check = ON_CONFLICT_ACTION_ABORT;
-	if (!rlist_empty(&space->ck_constraint))
-		parse_context->ckBase = new_tuple_reg;
-	struct ck_constraint *ck_constraint;
-	rlist_foreach_entry(ck_constraint, &space->ck_constraint, link) {
-		struct Expr *expr = ck_constraint->expr;
-		if (is_update && checkConstraintUnchanged(expr, upd_cols) != 0)
-			continue;
-		int all_ok = sqlVdbeMakeLabel(v);
-		sqlExprIfTrue(parse_context, expr, all_ok, SQL_JUMPIFNULL);
-		if (on_conflict == ON_CONFLICT_ACTION_IGNORE) {
-			sqlVdbeGoto(v, ignore_label);
-			sqlVdbeResolveLabel(v, all_ok);
-		} else {
-			char *name = ck_constraint->def->name;
-			sqlHaltConstraint(parse_context,
-					      SQL_CONSTRAINT_CHECK,
-					      on_conflict_check, name,
-					      P4_TRANSIENT, P5_ConstraintCheck);
-		}
-		sqlVdbeResolveLabel(v, all_ok);
-	}
 	sql_emit_table_types(v, space->def, new_tuple_reg);
 	/*
 	 * Other actions except for REPLACE and UPDATE OR IGNORE
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 9fef9ea2e..2a79d2b59 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -592,6 +592,17 @@ sql_column_value(sql_stmt *,
 int
 sql_finalize(sql_stmt * pStmt);
 
+/*
+ * Terminate the current execution of an SQL statement and reset
+ * it back to its starting state so that it can be reused.
+ *
+ * @param stmt VDBE program, may be NULL.
+ * @retval SQL_OK On success.
+ * @retval sql_ret_code Error code on error.
+ */
+int
+sql_reset(struct sql_stmt *stmt);
+
 int
 sql_exec(sql *,	/* An open database */
 	     const char *sql,	/* SQL to be evaluated */
@@ -3827,6 +3838,20 @@ vdbe_emit_constraint_checks(struct Parse *parse_context,
 			    enum on_conflict_action on_conflict,
 			    int ignore_label, int *upd_cols);
 
+/**
+ * Gnerate code to make check constraints tests on tuple insertion
+ * on INSERT, REPLACE or UPDATE operations.
+ * @param parser Current parsing context.
+ * @param expr Check constraint AST.
+ * @param name Check constraint name to raise error.
+ * @param new_tuple_reg The first ck_constraint::stmt VDBE
+ *                      register of the range
+ *                      space_def::field_count representing a
+ *                      new tuple to be inserted.
+ */
+void
+vdbe_emit_ck_constraint(struct Parse *parser, struct Expr *expr,
+			const char *name, int new_tuple_reg);
 /**
  * This routine generates code to finish the INSERT or UPDATE
  * operation that was started by a prior call to
diff --git a/src/box/sql/vdbeapi.c b/src/box/sql/vdbeapi.c
index be5c9dff9..ebedd3c42 100644
--- a/src/box/sql/vdbeapi.c
+++ b/src/box/sql/vdbeapi.c
@@ -132,14 +132,6 @@ sql_finalize(sql_stmt * pStmt)
 	return rc;
 }
 
-/*
- * Terminate the current execution of an SQL statement and reset it
- * back to its starting state so that it can be reused. A success code from
- * the prior execution is returned.
- *
- * This routine sets the error code and string returned by
- * sql_errcode(), sql_errmsg() and sql_errmsg16().
- */
 int
 sql_reset(sql_stmt * pStmt)
 {
diff --git a/test/sql/checks.result b/test/sql/checks.result
index aeacd09cb..2d46a2edc 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -71,6 +71,10 @@ box.sql.execute("INSERT INTO \"test\" VALUES(5);")
 ---
 - error: 'Check constraint failed: CK_CONSTRAINT_01'
 ...
+box.space.test:insert({5})
+---
+- error: 'Check constraint failed: CK_CONSTRAINT_01'
+...
 box.space._ck_constraint:replace({'CK_CONSTRAINT_01', 513, 'X<=5'})
 ---
 - ['CK_CONSTRAINT_01', 513, 'X<=5']
@@ -82,6 +86,10 @@ box.sql.execute("INSERT INTO \"test\" VALUES(6);")
 ---
 - error: 'Check constraint failed: CK_CONSTRAINT_01'
 ...
+box.space.test:insert({6})
+---
+- error: 'Check constraint failed: CK_CONSTRAINT_01'
+...
 -- Can't drop table with check constraints.
 box.space.test:delete({5})
 ---
@@ -113,13 +121,25 @@ box.sql.execute("INSERT INTO t1 VALUES (7, 1, 1)")
 ---
 - error: 'Check constraint failed: ONE'
 ...
+box.space.T1:insert({7, 1, 1})
+---
+- error: 'Check constraint failed: ONE'
+...
 box.sql.execute("INSERT INTO t1 VALUES (2, 1, 1)")
 ---
 - error: 'Check constraint failed: TWO'
 ...
+box.space.T1:insert({2, 1, 1})
+---
+- error: 'Check constraint failed: TWO'
+...
 box.sql.execute("INSERT INTO t1 VALUES (2, 4, 1)")
 ---
 ...
+box.space.T1:update({1}, {{'+', 1, 5}})
+---
+- error: 'Check constraint failed: ONE'
+...
 box.sql.execute("DROP TABLE t1")
 ---
 ...
diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
index 9a7e5faf4..0e6c990c9 100644
--- a/test/sql/checks.test.lua
+++ b/test/sql/checks.test.lua
@@ -27,9 +27,11 @@ box.space._ck_constraint:insert({'CK_CONSTRAINT_01', 513, 'X<5'})
 box.space._ck_constraint:count({})
 
 box.sql.execute("INSERT INTO \"test\" VALUES(5);")
+box.space.test:insert({5})
 box.space._ck_constraint:replace({'CK_CONSTRAINT_01', 513, 'X<=5'})
 box.sql.execute("INSERT INTO \"test\" VALUES(5);")
 box.sql.execute("INSERT INTO \"test\" VALUES(6);")
+box.space.test:insert({6})
 -- Can't drop table with check constraints.
 box.space.test:delete({5})
 box.space.test.index.pk:drop()
@@ -41,8 +43,11 @@ box.space.test:drop()
 box.sql.execute("CREATE TABLE t1(x INTEGER CONSTRAINT ONE CHECK( x<5 ), y REAL CONSTRAINT TWO CHECK( y>x ), z INTEGER PRIMARY KEY);")
 box.space._ck_constraint:count()
 box.sql.execute("INSERT INTO t1 VALUES (7, 1, 1)")
+box.space.T1:insert({7, 1, 1})
 box.sql.execute("INSERT INTO t1 VALUES (2, 1, 1)")
+box.space.T1:insert({2, 1, 1})
 box.sql.execute("INSERT INTO t1 VALUES (2, 4, 1)")
+box.space.T1:update({1}, {{'+', 1, 5}})
 box.sql.execute("DROP TABLE t1")
 
 --
-- 
2.21.0





More information about the Tarantool-patches mailing list