[Tarantool-patches] [tarantool-patches] Re: [PATCH v4 1/4] box: add an ability to disable CK constraints

Kirill Shcherbatov kshcherbatov at tarantool.org
Tue Oct 15 14:13:54 MSK 2019


> -> when processed data is verified and constraints validation
> is not required. For instance, during casual recovery process there's
> no need to provide any checks since data is assumed to be consistent.
Applied.

> Also explain why we have to store this option.
> To be honest, I doubt that 'is_enable' option should be persisted..
> At least, we can assume that it is always 'on' by default, so that
> save user from having to specify this option each time.

Verbally discussed. To implement a behavior that Oracle users are familiar with.
It was a Kostya's point of view and it is already implemented.

> Extra diff.
Fixed.

=====================================================

Now it is possible to disable and enable ck constraints in LUA.
This option is persistent. All ck constraints are constructed
in enabled state when Tarantool is configured. This ability
may be usefulwhen processed data is verified and constraints
validation is not required. For instance, during casual recovery
process there's no need to provide any checks since data is
assumed to be consistent.

Part of #4244
---
 src/box/alter.cc             |  31 ++++++--
 src/box/bootstrap.snap       | Bin 5934 -> 5944 bytes
 src/box/ck_constraint.c      |   6 +-
 src/box/ck_constraint.h      |   8 +-
 src/box/lua/schema.lua       |  16 +++-
 src/box/lua/space.cc         |   3 +
 src/box/lua/upgrade.lua      |  13 ++++
 src/box/schema_def.h         |   1 +
 src/box/sql/build.c          |  11 +--
 test/box-py/bootstrap.result |   3 +-
 test/box/access_misc.result  | 137 ++++++++++++++++++-----------------
 test/sql/checks.result       | 108 ++++++++++++++++++++++-----
 test/sql/checks.test.lua     |  57 ++++++++++-----
 test/sql/errinj.result       |  12 +--
 test/sql/errinj.test.lua     |  12 +--
 15 files changed, 285 insertions(+), 133 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index bb3686d6e..f8f1802d0 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -4595,9 +4595,12 @@ ck_constraint_def_new_from_tuple(struct tuple *tuple)
 	const char *expr_str =
 		tuple_field_str_xc(tuple, BOX_CK_CONSTRAINT_FIELD_CODE,
 				   &expr_str_len);
+	bool is_enabled =
+		tuple_field_count(tuple) <= BOX_CK_CONSTRAINT_FIELD_IS_ENABLED ||
+		tuple_field_bool_xc(tuple, BOX_CK_CONSTRAINT_FIELD_IS_ENABLED);
 	struct ck_constraint_def *ck_def =
 		ck_constraint_def_new(name, name_len, expr_str, expr_str_len,
-				      space_id, language);
+				      space_id, language, is_enabled);
 	if (ck_def == NULL)
 		diag_raise();
 	return ck_def;
@@ -4697,6 +4700,26 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 		auto ck_def_guard = make_scoped_guard([=] {
 			ck_constraint_def_delete(ck_def);
 		});
+		/*
+		 * A corner case: enabling/disabling an existent
+		 * ck constraint doesn't require the object
+		 * rebuilding.
+		 */
+		const char *name = ck_def->name;
+		struct ck_constraint *old_ck_constraint =
+			space_ck_constraint_by_name(space, name, strlen(name));
+		if (old_ck_constraint != NULL) {
+			struct ck_constraint_def *old_def =
+						old_ck_constraint->def;
+			assert(old_def->space_id == ck_def->space_id);
+			assert(strcmp(old_def->name, ck_def->name) == 0);
+			if (old_def->language == ck_def->language &&
+			    strcmp(old_def->expr_str, ck_def->expr_str) == 0) {
+				old_def->is_enabled = ck_def->is_enabled;
+				trigger_run_xc(&on_alter_space, space);
+				return;
+			}
+		}
 		/*
 		 * FIXME: Ck constraint creation on non-empty
 		 * space is not implemented yet.
@@ -4704,8 +4727,7 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 		struct index *pk = space_index(space, 0);
 		if (pk != NULL && index_size(pk) > 0) {
 			tnt_raise(ClientError, ER_CREATE_CK_CONSTRAINT,
-				  ck_def->name,
-				  "referencing space must be empty");
+				  name, "referencing space must be empty");
 		}
 		struct ck_constraint *new_ck_constraint =
 			ck_constraint_new(ck_def, space->def);
@@ -4715,9 +4737,6 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 		auto ck_guard = make_scoped_guard([=] {
 			ck_constraint_delete(new_ck_constraint);
 		});
-		const char *name = new_ck_constraint->def->name;
-		struct ck_constraint *old_ck_constraint =
-			space_ck_constraint_by_name(space, name, strlen(name));
 		if (old_ck_constraint != NULL)
 			rlist_del_entry(old_ck_constraint, link);
 		if (space_add_ck_constraint(space, new_ck_constraint) != 0)
diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 4c9aea7f50f8ac86be32ca9f126fea9a3d2d182f..59f6cc16bb48841fa99b4e13590b8e0677433b35 100644
GIT binary patch
delta 4861

diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index 1cde27022..d21717f94 100644
--- a/src/box/ck_constraint.c
+++ b/src/box/ck_constraint.c
@@ -44,7 +44,7 @@ const char *ck_constraint_language_strs[] = {"SQL"};
 struct ck_constraint_def *
 ck_constraint_def_new(const char *name, uint32_t name_len, const char *expr_str,
 		      uint32_t expr_str_len, uint32_t space_id,
-		      enum ck_constraint_language language)
+		      enum ck_constraint_language language, bool is_enabled)
 {
 	uint32_t expr_str_offset;
 	uint32_t ck_def_sz = ck_constraint_def_sizeof(name_len, expr_str_len,
@@ -55,6 +55,7 @@ ck_constraint_def_new(const char *name, uint32_t name_len, const char *expr_str,
 		diag_set(OutOfMemory, ck_def_sz, "malloc", "ck_def");
 		return NULL;
 	}
+	ck_def->is_enabled = is_enabled;
 	ck_def->expr_str = (char *)ck_def + expr_str_offset;
 	ck_def->language = language;
 	ck_def->space_id = space_id;
@@ -201,7 +202,8 @@ ck_constraint_on_replace_trigger(struct trigger *trigger, void *event)
 
 	struct ck_constraint *ck_constraint;
 	rlist_foreach_entry(ck_constraint, &space->ck_constraint, link) {
-		if (ck_constraint_program_run(ck_constraint, field_ref) != 0)
+		if (ck_constraint->def->is_enabled &&
+		    ck_constraint_program_run(ck_constraint, field_ref) != 0)
 			diag_raise();
 	}
 }
diff --git a/src/box/ck_constraint.h b/src/box/ck_constraint.h
index f26f77a38..6761318d6 100644
--- a/src/box/ck_constraint.h
+++ b/src/box/ck_constraint.h
@@ -71,6 +71,11 @@ struct ck_constraint_def {
 	 * defined for.
 	 */
 	uint32_t space_id;
+	/**
+	 * Per constraint option regulating its execution: it is
+	 * disabled (set to false) contraint won't be fired.
+	 */
+	bool is_enabled;
 	/** The language of ck constraint. */
 	enum ck_constraint_language language;
 	/**
@@ -140,6 +145,7 @@ ck_constraint_def_sizeof(uint32_t name_len, uint32_t expr_str_len,
  * @param expr_str_len The length of the @a expr string.
  * @param space_id The identifier of the target space.
  * @param language The language of the @a expr string.
+ * @param is_enabled Whether this ck constraint should be fired.
  * @retval not NULL Check constraint definition object pointer
  *                  on success.
  * @retval NULL Otherwise. The diag message is set.
@@ -147,7 +153,7 @@ ck_constraint_def_sizeof(uint32_t name_len, uint32_t expr_str_len,
 struct ck_constraint_def *
 ck_constraint_def_new(const char *name, uint32_t name_len, const char *expr,
 		      uint32_t expr_str_len, uint32_t space_id,
-		      enum ck_constraint_language language);
+		      enum ck_constraint_language language, bool is_enabled);
 
 /**
  * Destroy check constraint definition memory, release acquired
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 98067f795..e898c3aa6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1706,7 +1706,7 @@ space_mt.create_check_constraint = function(space, name, code)
         box.error(box.error.PROC_LUA,
                   "Usage: space:create_constraint(name, code)")
     end
-    box.space._ck_constraint:insert({space.id, name, false, 'SQL', code})
+    box.space._ck_constraint:insert({space.id, name, false, 'SQL', code, true})
     return space.ck_constraint[name]
 end
 space_mt.pairs = function(space, key, opts)
@@ -1759,6 +1759,20 @@ ck_constraint_mt.drop = function(ck_constraint)
     check_ck_constraint_arg(ck_constraint, 'drop')
     box.space._ck_constraint:delete({ck_constraint.space_id, ck_constraint.name})
 end
+ck_constraint_mt.enable = function(ck_constraint, yesno)
+    check_ck_constraint_arg(ck_constraint, 'enable')
+    local s = builtin.space_by_id(ck_constraint.space_id)
+    if s == nil then
+        box.error(box.error.NO_SUCH_SPACE, tostring(ck_constraint.space_id))
+    end
+    local t = box.space._ck_constraint:get({ck_constraint.space_id,
+                                            ck_constraint.name})
+    if t == nil then
+        box.error(box.error.NO_SUCH_CONSTRAINT, tostring(ck_constraint.name))
+    end
+    box.space._ck_constraint:update({ck_constraint.space_id, ck_constraint.name},
+                                    {{'=', 6, yesno}})
+end
 
 box.schema.index_mt = base_index_mt
 box.schema.memtx_index_mt = memtx_index_mt
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index d0a7e7815..8b84e1fb2 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -205,6 +205,9 @@ lbox_push_ck_constraint(struct lua_State *L, struct space *space, int i)
 		lua_pushstring(L, ck_constraint->def->expr_str);
 		lua_setfield(L, -2, "expr");
 
+		lua_pushboolean(L, ck_constraint->def->is_enabled);
+		lua_setfield(L, -2, "is_enabled");
+
 		lua_setfield(L, -2, ck_constraint->def->name);
 	}
 	lua_pop(L, 1);
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 2abd75dff..e71b7fb41 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -927,6 +927,19 @@ local function upgrade_to_2_3_0()
                                         datetime, datetime})
         _priv:replace{ADMIN, PUBLIC, 'function', t.id, box.priv.X}
     end
+
+    log.info("Extend _ck_constraint space format with is_enabled field")
+    local _ck_constraint = box.space._ck_constraint
+    for _, tuple in _ck_constraint:pairs() do
+        _ck_constraint:update({tuple[1], tuple[2]}, {{'=', 6, true}})
+    end
+    local format = {{name='space_id', type='unsigned'},
+                    {name='name', type='string'},
+                    {name='is_deferred', type='boolean'},
+                    {name='language', type='str'},
+                    {name='code', type='str'},
+                    {name='is_enabled', type='boolean'}}
+    _ck_constraint:format(format)
 end
 
 --------------------------------------------------------------------------------
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
index 85f652d52..ba870ff42 100644
--- a/src/box/schema_def.h
+++ b/src/box/schema_def.h
@@ -267,6 +267,7 @@ enum {
 	BOX_CK_CONSTRAINT_FIELD_DEFERRED = 2,
 	BOX_CK_CONSTRAINT_FIELD_LANGUAGE = 3,
 	BOX_CK_CONSTRAINT_FIELD_CODE = 4,
+	BOX_CK_CONSTRAINT_FIELD_IS_ENABLED = 5,
 };
 
 /** _func_index fields. */
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 233f56734..51cd7ce63 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -982,7 +982,7 @@ vdbe_emit_ck_constraint_create(struct Parse *parser,
 	 * Occupy registers for 5 fields: each member in
 	 * _ck_constraint space plus one for final msgpack tuple.
 	 */
-	int ck_constraint_reg = sqlGetTempRange(parser, 6);
+	int ck_constraint_reg = sqlGetTempRange(parser, 7);
 	sqlVdbeAddOp2(v, OP_SCopy, reg_space_id, ck_constraint_reg);
 	sqlVdbeAddOp4(v, OP_String8, 0, ck_constraint_reg + 1, 0,
 		      sqlDbStrDup(db, ck_def->name), P4_DYNAMIC);
@@ -991,8 +991,9 @@ vdbe_emit_ck_constraint_create(struct Parse *parser,
 		      ck_constraint_language_strs[ck_def->language], P4_STATIC);
 	sqlVdbeAddOp4(v, OP_String8, 0, ck_constraint_reg + 4, 0,
 		      sqlDbStrDup(db, ck_def->expr_str), P4_DYNAMIC);
-	sqlVdbeAddOp3(v, OP_MakeRecord, ck_constraint_reg, 5,
-		      ck_constraint_reg + 5);
+	sqlVdbeAddOp2(v, OP_Bool, true, ck_constraint_reg + 5);
+	sqlVdbeAddOp3(v, OP_MakeRecord, ck_constraint_reg, 6,
+		      ck_constraint_reg + 6);
 	const char *error_msg =
 		tt_sprintf(tnt_errcode_desc(ER_CONSTRAINT_EXISTS),
 					    ck_def->name);
@@ -1002,9 +1003,9 @@ vdbe_emit_ck_constraint_create(struct Parse *parser,
 					      false, OP_NoConflict) != 0)
 		return;
 	sqlVdbeAddOp2(v, OP_SInsert, BOX_CK_CONSTRAINT_ID,
-		      ck_constraint_reg + 5);
+		      ck_constraint_reg + 6);
 	VdbeComment((v, "Create CK constraint %s", ck_def->name));
-	sqlReleaseTempRange(parser, ck_constraint_reg, 6);
+	sqlReleaseTempRange(parser, ck_constraint_reg, 7);
 }
 
 /**
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index a59979e62..123aa2feb 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -92,7 +92,8 @@ box.space._space:select{}
       {'name': 'child_cols', 'type': 'array'}, {'name': 'parent_cols', 'type': 'array'}]]
   - [364, 1, '_ck_constraint', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
       {'name': 'name', 'type': 'string'}, {'name': 'is_deferred', 'type': 'boolean'},
-      {'name': 'language', 'type': 'str'}, {'name': 'code', 'type': 'str'}]]
+      {'name': 'language', 'type': 'str'}, {'name': 'code', 'type': 'str'}, {'name': 'is_enabled',
+        'type': 'boolean'}]]
   - [372, 1, '_func_index', 'memtx', 0, {}, [{'name': 'space_id', 'type': 'unsigned'},
       {'name': 'index_id', 'type': 'unsigned'}, {'name': 'func_id', 'type': 'unsigned'}]]
 ...
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index a1b6435bc..27eb47aa0 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result

diff --git a/test/sql/checks.result b/test/sql/checks.result
index 50347bc3a..72b1fa26f 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -37,35 +37,35 @@ _ = box.space.test:create_index('pk')
 ---
 ...
 -- Invalid expression test.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X><5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X><5', true})
 ---
 - error: 'Failed to create check constraint ''CK_CONSTRAINT_01'': Syntax error near
     ''<'''
 ...
 -- Non-existent space test.
-box.space._ck_constraint:insert({550, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({550, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 ---
 - error: Space '550' does not exist
 ...
 -- Pass integer instead of expression.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 666})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 666, true})
 ---
 - error: 'Tuple field 5 type does not match one required by operation: expected string'
 ...
 -- Deferred CK constraints are not supported.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', true, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', true, 'SQL', 'X<5', true})
 ---
 - error: Tarantool does not support deferred ck constraints
 ...
 -- The only supported language is SQL.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'LUA', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'LUA', 'X<5', true})
 ---
 - error: Unsupported language 'LUA' specified for function 'CK_CONSTRAINT_01'
 ...
 -- Check constraints LUA creation test.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 ---
-- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5']
+- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true]
 ...
 box.space._ck_constraint:count({})
 ---
@@ -80,9 +80,9 @@ box.space.test:insert({5})
 ---
 - error: 'Check constraint failed ''CK_CONSTRAINT_01'': X<5'
 ...
-box.space._ck_constraint:replace({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+box.space._ck_constraint:replace({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 ---
-- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5']
+- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true]
 ...
 box.execute("INSERT INTO \"test\" VALUES(5);")
 ---
@@ -111,7 +111,7 @@ box.space._space:delete({513})
 ...
 box.space._ck_constraint:delete({513, 'CK_CONSTRAINT_01'})
 ---
-- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5']
+- [513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true]
 ...
 box.space._space:delete({513})
 ---
@@ -177,14 +177,14 @@ s = box.schema.create_space('test', {engine = engine})
 _ = s:create_index('pk')
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y'})
+_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y', true})
 ---
 - error: Tarantool does not support CK constraint for space without format
 ...
 s:format({{name='X', type='integer'}, {name='Y', type='integer'}})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y'})
+_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y', true})
 ---
 ...
 box.execute("INSERT INTO \"test\" VALUES(2, 1);")
@@ -229,7 +229,7 @@ s:insert({2, 1})
 ---
 - [2, 1]
 ...
-_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10'})
+_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10', true})
 ---
 - error: 'Failed to create check constraint ''conflict'': referencing space must be
     empty'
@@ -237,7 +237,7 @@ _ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10'})
 s:truncate()
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10'})
+_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10', true})
 ---
 ...
 box.execute("INSERT INTO \"test\" VALUES(1, 2);")
@@ -332,7 +332,7 @@ _ = s:create_index('pk')
 s:format({{name='X', type='any'}, {name='Y', type='integer'}, {name='Z', type='integer', is_nullable=true}})
 ---
 ...
-ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'X = 1 AND Z IS NOT NULL'})
+ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'X = 1 AND Z IS NOT NULL', true})
 ---
 ...
 s:insert({2, 1})
@@ -374,7 +374,7 @@ _ = s:create_index('pk')
 s:format({{name='X', type='any'}, {name='Y', type='integer'}, {name='Z', type='integer', is_nullable=true}})
 ---
 ...
-ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'Z IS NOT NULL'})
+ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'Z IS NOT NULL', true})
 ---
 ...
 s:insert({1, 2, box.NULL})
@@ -388,7 +388,7 @@ s:insert({1, 2})
 _ = box.space._ck_constraint:delete({s.id, 'ZnotNULL'})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y and Y < Z'})
+_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y and Y < Z', true})
 ---
 ...
 s:insert({'1', 2})
@@ -464,7 +464,7 @@ _ = s:create_index('pk', {parts = {3, 'integer'}})
 _ = s:create_index('unique', {parts = {1, 'integer'}})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'complex1', false, 'SQL', 'x+y==11 OR x*y==12 OR x/y BETWEEN 5 AND 8 OR -x == y+10'})
+_ = box.space._ck_constraint:insert({s.id, 'complex1', false, 'SQL', 'x+y==11 OR x*y==12 OR x/y BETWEEN 5 AND 8 OR -x == y+10', true})
 ---
 ...
 s:insert({1, 10, 1})
@@ -513,7 +513,7 @@ s:format({{name='X', type='integer'}, {name='Z', type='any'}})
 _ = s:create_index('pk', {parts = {1, 'integer'}})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'complex2', false, 'SQL', 'typeof(coalesce(z,0))==\'integer\''})
+_ = box.space._ck_constraint:insert({s.id, 'complex2', false, 'SQL', 'typeof(coalesce(z,0))==\'integer\'', true})
 ---
 ...
 s:insert({1, 'string'})
@@ -564,7 +564,7 @@ test_run:cmd("setopt delimiter ''");
 s:format(format65)
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'X1is666andX65is666', false, 'SQL', 'X1 == 666 and X65 == 666 and X63 IS NOT NULL'})
+_ = box.space._ck_constraint:insert({s.id, 'X1is666andX65is666', false, 'SQL', 'X1 == 666 and X65 == 666 and X63 IS NOT NULL', true})
 ---
 ...
 s:insert(s:frommap({X1 = 1, X65 = 1}))
@@ -642,24 +642,28 @@ _ = s2:create_check_constraint('greater', 'X > 20')
 s1.ck_constraint.physics
 ---
 - space_id: <ID>
+  is_enabled: true
   name: physics
   expr: X < Y
 ...
 s1.ck_constraint.greater
 ---
 - space_id: <ID>
+  is_enabled: true
   name: greater
   expr: X > 20
 ...
 s2.ck_constraint.physics
 ---
 - space_id: <ID>
+  is_enabled: true
   name: physics
   expr: X > Y
 ...
 s2.ck_constraint.greater
 ---
 - space_id: <ID>
+  is_enabled: true
   name: greater
   expr: X > 20
 ...
@@ -685,6 +689,7 @@ s2.ck_constraint.greater:drop()
 s2.ck_constraint.physics
 ---
 - space_id: <ID>
+  is_enabled: true
   name: physics
   expr: X > Y
 ...
@@ -716,12 +721,75 @@ s2:drop()
 physics_ck
 ---
 - space_id: <ID>
+  is_enabled: true
   name: physics
   expr: X > Y
 ...
 physics_ck:drop()
 ---
 ...
+--
+-- gh-4244: Add an ability to disable CK constraints
+-- Make sure that ck constraints are turned on/off with
+-- :enable configurator.
+--
+engine = test_run:get_cfg('engine')
+---
+...
+box.execute('pragma sql_default_engine=\''..engine..'\'')
+---
+- row_count: 0
+...
+box.execute("CREATE TABLE test(a INT PRIMARY KEY);");
+---
+- row_count: 1
+...
+box.execute('ALTER TABLE test ADD CONSTRAINT CK CHECK(a < 5);')
+---
+- row_count: 1
+...
+box.space.TEST:insert({10})
+---
+- error: 'Check constraint failed ''CK'': a < 5'
+...
+box.space.TEST.ck_constraint.CK:enable(false)
+---
+...
+assert(box.space.TEST.ck_constraint.CK.is_enabled == false)
+---
+- true
+...
+box.space.TEST:insert({11})
+---
+- [11]
+...
+-- Test is_enabled state persistency.
+test_run:cmd("restart server default")
+test_run = require('test_run').new()
+---
+...
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
+---
+- true
+...
+box.space.TEST:insert({12})
+---
+- [12]
+...
+box.space.TEST.ck_constraint.CK:enable(true)
+---
+...
+assert(box.space.TEST.ck_constraint.CK.is_enabled == true)
+---
+- true
+...
+box.space.TEST:insert({13})
+---
+- error: 'Check constraint failed ''CK'': a < 5'
+...
+box.space.TEST:drop()
+---
+...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
index cde213f8b..11918dced 100644
--- a/test/sql/checks.test.lua
+++ b/test/sql/checks.test.lua
@@ -18,23 +18,23 @@ s = box.space._space:insert(t)
 _ = box.space.test:create_index('pk')
 
 -- Invalid expression test.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X><5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X><5', true})
 -- Non-existent space test.
-box.space._ck_constraint:insert({550, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({550, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 -- Pass integer instead of expression.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 666})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 666, true})
 -- Deferred CK constraints are not supported.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', true, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', true, 'SQL', 'X<5', true})
 -- The only supported language is SQL.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'LUA', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'LUA', 'X<5', true})
 
 -- Check constraints LUA creation test.
-box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+box.space._ck_constraint:insert({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 box.space._ck_constraint:count({})
 
 box.execute("INSERT INTO \"test\" VALUES(5);")
 box.space.test:insert({5})
-box.space._ck_constraint:replace({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+box.space._ck_constraint:replace({513, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 box.execute("INSERT INTO \"test\" VALUES(5);")
 box.execute("INSERT INTO \"test\" VALUES(6);")
 box.space.test:insert({6})
@@ -64,9 +64,9 @@ box.space._ck_constraint:count() == 0
 -- Ck constraints are disallowed for spaces having no format.
 s = box.schema.create_space('test', {engine = engine})
 _ = s:create_index('pk')
-_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y'})
+_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y', true})
 s:format({{name='X', type='integer'}, {name='Y', type='integer'}})
-_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y'})
+_ = box.space._ck_constraint:insert({s.id, 'physics', false, 'SQL', 'X<Y', true})
 box.execute("INSERT INTO \"test\" VALUES(2, 1);")
 s:format({{name='Y', type='integer'}, {name='X', type='integer'}})
 box.execute("INSERT INTO \"test\" VALUES(1, 2);")
@@ -78,9 +78,9 @@ s:format()
 s:format({{name='Y1', type='integer'}, {name='X1', type='integer'}})
 -- Ck constraint creation is forbidden for non-empty space
 s:insert({2, 1})
-_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10'})
+_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10', true})
 s:truncate()
-_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10'})
+_ = box.space._ck_constraint:insert({s.id, 'conflict', false, 'SQL', 'X>10', true})
 box.execute("INSERT INTO \"test\" VALUES(1, 2);")
 box.execute("INSERT INTO \"test\" VALUES(11, 11);")
 box.execute("INSERT INTO \"test\" VALUES(12, 11);")
@@ -114,7 +114,7 @@ box.execute("DROP TABLE t1")
 s = box.schema.create_space('test', {engine = engine})
 _ = s:create_index('pk')
 s:format({{name='X', type='any'}, {name='Y', type='integer'}, {name='Z', type='integer', is_nullable=true}})
-ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'X = 1 AND Z IS NOT NULL'})
+ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'X = 1 AND Z IS NOT NULL', true})
 s:insert({2, 1})
 s:insert({1, 1})
 s:insert({1, 1, box.NULL})
@@ -129,11 +129,11 @@ s:drop()
 s = box.schema.create_space('test', {engine = engine})
 _ = s:create_index('pk')
 s:format({{name='X', type='any'}, {name='Y', type='integer'}, {name='Z', type='integer', is_nullable=true}})
-ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'Z IS NOT NULL'})
+ck_not_null = box.space._ck_constraint:insert({s.id, 'ZnotNULL', false, 'SQL', 'Z IS NOT NULL', true})
 s:insert({1, 2, box.NULL})
 s:insert({1, 2})
 _ = box.space._ck_constraint:delete({s.id, 'ZnotNULL'})
-_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y and Y < Z'})
+_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y and Y < Z', true})
 s:insert({'1', 2})
 s:insert({})
 s:insert({2, 1})
@@ -157,7 +157,7 @@ s = box.schema.create_space('test', {engine = engine})
 s:format({{name='X', type='integer'}, {name='Y', type='integer'}, {name='Z', type='integer'}})
 _ = s:create_index('pk', {parts = {3, 'integer'}})
 _ = s:create_index('unique', {parts = {1, 'integer'}})
-_ = box.space._ck_constraint:insert({s.id, 'complex1', false, 'SQL', 'x+y==11 OR x*y==12 OR x/y BETWEEN 5 AND 8 OR -x == y+10'})
+_ = box.space._ck_constraint:insert({s.id, 'complex1', false, 'SQL', 'x+y==11 OR x*y==12 OR x/y BETWEEN 5 AND 8 OR -x == y+10', true})
 s:insert({1, 10, 1})
 s:update({1}, {{'=', 1, 4}, {'=', 2, 3}})
 s:update({1}, {{'=', 1, 12}, {'=', 2, 2}})
@@ -171,7 +171,7 @@ s:drop()
 s = box.schema.create_space('test', {engine = engine})
 s:format({{name='X', type='integer'}, {name='Z', type='any'}})
 _ = s:create_index('pk', {parts = {1, 'integer'}})
-_ = box.space._ck_constraint:insert({s.id, 'complex2', false, 'SQL', 'typeof(coalesce(z,0))==\'integer\''})
+_ = box.space._ck_constraint:insert({s.id, 'complex2', false, 'SQL', 'typeof(coalesce(z,0))==\'integer\'', true})
 s:insert({1, 'string'})
 s:insert({1, {map=true}})
 s:insert({1, {'a', 'r','r','a','y'}})
@@ -191,7 +191,7 @@ for i = 1,66 do
 end
 test_run:cmd("setopt delimiter ''");
 s:format(format65)
-_ = box.space._ck_constraint:insert({s.id, 'X1is666andX65is666', false, 'SQL', 'X1 == 666 and X65 == 666 and X63 IS NOT NULL'})
+_ = box.space._ck_constraint:insert({s.id, 'X1is666andX65is666', false, 'SQL', 'X1 == 666 and X65 == 666 and X63 IS NOT NULL', true})
 s:insert(s:frommap({X1 = 1, X65 = 1}))
 s:insert(s:frommap({X1 = 666, X65 = 1}))
 s:insert(s:frommap({X1 = 1, X65 = 666}))
@@ -234,4 +234,27 @@ s2:drop()
 physics_ck
 physics_ck:drop()
 
+--
+-- gh-4244: Add an ability to disable CK constraints
+-- Make sure that ck constraints are turned on/off with
+-- :enable configurator.
+--
+engine = test_run:get_cfg('engine')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute("CREATE TABLE test(a INT PRIMARY KEY);");
+box.execute('ALTER TABLE test ADD CONSTRAINT CK CHECK(a < 5);')
+box.space.TEST:insert({10})
+box.space.TEST.ck_constraint.CK:enable(false)
+assert(box.space.TEST.ck_constraint.CK.is_enabled == false)
+box.space.TEST:insert({11})
+-- Test is_enabled state persistency.
+test_run:cmd("restart server default")
+test_run = require('test_run').new()
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
+box.space.TEST:insert({12})
+box.space.TEST.ck_constraint.CK:enable(true)
+assert(box.space.TEST.ck_constraint.CK.is_enabled == true)
+box.space.TEST:insert({13})
+box.space.TEST:drop()
+
 test_run:cmd("clear filter")
diff --git a/test/sql/errinj.result b/test/sql/errinj.result
index ecc194fb8..7ab522f49 100644
--- a/test/sql/errinj.result
+++ b/test/sql/errinj.result
@@ -410,7 +410,7 @@ errinj.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 ---
 - error: Failed to write to disk
 ...
@@ -418,7 +418,7 @@ errinj.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 ---
 ...
 box.execute("INSERT INTO \"test\" VALUES(5);")
@@ -430,7 +430,7 @@ errinj.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 ---
 - error: Failed to write to disk
 ...
@@ -438,7 +438,7 @@ errinj.set("ERRINJ_WAL_IO", false)
 ---
 - ok
 ...
-_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 ---
 ...
 box.execute("INSERT INTO \"test\" VALUES(5);")
@@ -484,10 +484,10 @@ _ = s:create_index('pk')
 s:format({{name='X', type='integer'}, {name='Y', type='integer'}})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y'})
+_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y', true})
 ---
 ...
-_ = box.space._ck_constraint:insert({s.id, 'Xgreater10', false, 'SQL', 'X > 10'})
+_ = box.space._ck_constraint:insert({s.id, 'Xgreater10', false, 'SQL', 'X > 10', true})
 ---
 ...
 box.execute("INSERT INTO \"test\" VALUES(1, 2);")
diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua
index 2b4f74a82..b978767ad 100644
--- a/test/sql/errinj.test.lua
+++ b/test/sql/errinj.test.lua
@@ -126,14 +126,14 @@ s = box.schema.space.create('test', {format = {{name = 'X', type = 'unsigned'}}}
 pk = box.space.test:create_index('pk')
 
 errinj.set("ERRINJ_WAL_IO", true)
-_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 errinj.set("ERRINJ_WAL_IO", false)
-_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5'})
+_ = box.space._ck_constraint:insert({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<5', true})
 box.execute("INSERT INTO \"test\" VALUES(5);")
 errinj.set("ERRINJ_WAL_IO", true)
-_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 errinj.set("ERRINJ_WAL_IO", false)
-_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5'})
+_ = box.space._ck_constraint:replace({s.id, 'CK_CONSTRAINT_01', false, 'SQL', 'X<=5', true})
 box.execute("INSERT INTO \"test\" VALUES(5);")
 errinj.set("ERRINJ_WAL_IO", true)
 _ = box.space._ck_constraint:delete({s.id, 'CK_CONSTRAINT_01'})
@@ -149,8 +149,8 @@ s:drop()
 s = box.schema.create_space('test')
 _ = s:create_index('pk')
 s:format({{name='X', type='integer'}, {name='Y', type='integer'}})
-_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y'})
-_ = box.space._ck_constraint:insert({s.id, 'Xgreater10', false, 'SQL', 'X > 10'})
+_ = box.space._ck_constraint:insert({s.id, 'XlessY', false, 'SQL', 'X < Y', true})
+_ = box.space._ck_constraint:insert({s.id, 'Xgreater10', false, 'SQL', 'X > 10', true})
 box.execute("INSERT INTO \"test\" VALUES(1, 2);")
 box.execute("INSERT INTO \"test\" VALUES(20, 10);")
 box.execute("INSERT INTO \"test\" VALUES(20, 100);")
-- 
2.23.0




More information about the Tarantool-patches mailing list