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

Kirill Shcherbatov kshcherbatov at tarantool.org
Mon Oct 7 17:21:07 MSK 2019


Note:
Please pay attention that master (last) state that my branch refers to is
broken (tarantoolctl.test.lua is failed).

Changes:
I've reworked this path to make is_enabled configuration persistent.


> I think set_state is more general than necessary.
> space_ck_constraint_enable is fine, even if it can be used to 
> disable the constraint as well.
>> +ck_constraint_mt.enable = function(ck_constraint, yesno)
Ok, done.

>> +#include "ck_constraint.h"
> 
> Why is this stray include?
It was redundant.

@korablev:
> AFAIU, this patch adds only ability to turn on/off constraints;
> it doesn't affect recovery process. What is more, it would be
> nice to see any rationale for introducing such ability.
I've updated commit message correspondingly.

> 
>> box: an ability to disable CK constraints
> 
> Nit: commit subject really lacks a verb:
> box: add an ability to disable CK constraints
fixed.

>> +#include "memtx_engine.h"
> 
> Seems to be redundant include.
Yes.

>> +/**
>> + * Find check constraint object in a given space by a given name
>> + * and change it's is_enabled state.
> 
> Nit: it's -> its.
No such function anymore.

>> +#include "ck_constraint.h"
> 
> Same: redundant include (?).
Dropped.


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

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 useful when all information processed is (by some reason)
trusted and ck constraints validations are not required.

Part of #4244
---
 src/box/alter.cc             |  31 ++++++--
 src/box/bootstrap.snap       | Bin 5934 -> 5944 bytes
 src/box/ck_constraint.c      |   7 +-
 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, 286 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

diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index 1cde27022..fafa7be12 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();
 	}
 }
@@ -223,6 +225,7 @@ ck_constraint_new(struct ck_constraint_def *ck_constraint_def,
 	}
 	ck_constraint->def = NULL;
 	ck_constraint->stmt = NULL;
+
 	rlist_create(&ck_constraint->link);
 	struct Expr *expr =
 		sql_expr_compile(sql_get(), ck_constraint_def->expr_str,
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 b4f114ea8..a8c56b287 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -999,7 +999,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);
@@ -1008,8 +1008,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);
@@ -1019,9 +1020,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
 ---
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