[tarantool-patches] [PATCH v2 1/3] box: an ability to disable CK constraints

Kirill Shcherbatov kshcherbatov at tarantool.org
Thu Sep 12 11:06:41 MSK 2019


@TarantoolBot document
Title: an ability to disable CK constraints

Now it is possible to disable and enable ck constraints.
This option is not persistent. All ck constraints are enabled
by default when Tarantool is configured. Ck constraints checks
are not performed during standard recovery, but performed during
force_recovery - all conflicting tuples are skipped in case of
ck_constraint conflict.

Example:
box.space.T6.ck_constraint.ck_unnamed_T6_1:enable(false)
box.space.T6.ck_constraint.ck_unnamed_T6_1
- space_id: 512
  is_enabled: false
  name: ck_unnamed_T6_1
  expr: a < 10
x.space.T6:insert({11})
-- passed

Closes #4244
---
 extra/exports            |   1 +
 src/box/ck_constraint.c  |  23 +++++++-
 src/box/ck_constraint.h  |  19 +++++++
 src/box/lua/schema.lua   |  13 +++++
 src/box/lua/space.cc     |   3 ++
 src/box/memtx_engine.c   |  15 ++++++
 test/sql/checks.result   | 112 +++++++++++++++++++++++++++++++++++++++
 test/sql/checks.test.lua |  41 ++++++++++++++
 8 files changed, 226 insertions(+), 1 deletion(-)

diff --git a/extra/exports b/extra/exports
index 7b84a1452..ecc7d102b 100644
--- a/extra/exports
+++ b/extra/exports
@@ -78,6 +78,7 @@ tarantool_exit
 log_pid
 space_by_id
 space_run_triggers
+space_ck_constraint_enable
 space_bsize
 box_schema_version
 
diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index 1cde27022..f65715096 100644
--- a/src/box/ck_constraint.c
+++ b/src/box/ck_constraint.c
@@ -28,6 +28,7 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+#include "memtx_engine.h"
 #include "box/session.h"
 #include "bind.h"
 #include "ck_constraint.h"
@@ -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->is_enabled &&
+		    ck_constraint_program_run(ck_constraint, field_ref) != 0)
 			diag_raise();
 	}
 }
@@ -223,6 +225,13 @@ ck_constraint_new(struct ck_constraint_def *ck_constraint_def,
 	}
 	ck_constraint->def = NULL;
 	ck_constraint->stmt = NULL;
+
+	struct memtx_engine *memtx =
+		(struct memtx_engine *)engine_by_name("memtx");
+	assert(memtx != NULL);
+	bool is_recovery = memtx->state != MEMTX_OK;
+	ck_constraint->is_enabled = !is_recovery;
+
 	rlist_create(&ck_constraint->link);
 	struct Expr *expr =
 		sql_expr_compile(sql_get(), ck_constraint_def->expr_str,
@@ -269,3 +278,15 @@ space_ck_constraint_by_name(struct space *space, const char *name,
 	}
 	return NULL;
 }
+
+int
+space_ck_constraint_enable(struct space *space, const char *name,
+			   bool is_enabled)
+{
+	struct ck_constraint *ck =
+		space_ck_constraint_by_name(space, name, strlen(name));
+	if (ck == NULL)
+		return -1;
+	ck->is_enabled = is_enabled;
+	return 0;
+}
diff --git a/src/box/ck_constraint.h b/src/box/ck_constraint.h
index f26f77a38..4f2a3c20e 100644
--- a/src/box/ck_constraint.h
+++ b/src/box/ck_constraint.h
@@ -93,6 +93,12 @@ struct ck_constraint {
 	 * message when ck condition unsatisfied.
 	 */
 	struct sql_stmt *stmt;
+	/**
+	 * Whether the CK constraint object is enabled: the
+	 * checks wouldn't be performed when this flag is
+	 * set 'false'.
+	*/
+	bool is_enabled;
 	/**
 	 * Organize check constraint structs into linked list
 	 * with space::ck_constraint.
@@ -214,6 +220,19 @@ struct ck_constraint *
 space_ck_constraint_by_name(struct space *space, const char *name,
 			    uint32_t name_len);
 
+/**
+ * Find check constraint object in a given space by a given name
+ * and change is_enabled status.
+ * @param space The space to lookup check constraint.
+ * @param name The check constraint name.
+ * @param is_enabled The new is_enabled status for ck constraints.
+ * @return 0 if ck constraint is successfully found and new
+ *         is_enabled value is set, -1 otherwise.
+ */
+int
+space_ck_constraint_enable(struct space *space, const char *name,
+			   bool is_enabled);
+
 #if defined(__cplusplus)
 } /* extern "C" { */
 #endif
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 98067f795..a9c05bb0d 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -29,6 +29,8 @@ ffi.cdef[[
     struct space *space_by_id(uint32_t id);
     extern uint32_t box_schema_version();
     void space_run_triggers(struct space *space, bool yesno);
+    void space_ck_constraint_enable(struct space *space, const char *name,
+                                    bool is_enabled);
     size_t space_bsize(struct space *space);
 
     typedef struct tuple box_tuple_t;
@@ -1759,6 +1761,17 @@ 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
+    if builtin.space_ck_constraint_enable(s, ck_constraint.name, yesno) ~= nil then
+        box.error(box.error.NO_SUCH_CONSTRAINT, tostring(ck_constraint.name))
+    end
+    ck_constraint.is_enabled = 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..2c686e818 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->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/memtx_engine.c b/src/box/memtx_engine.c
index f6a33282c..819571819 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -35,6 +35,7 @@
 #include <small/small.h>
 #include <small/mempool.h>
 
+#include "ck_constraint.h"
 #include "fiber.h"
 #include "errinj.h"
 #include "coio_file.h"
@@ -86,6 +87,18 @@ memtx_end_build_primary_key(struct space *space, void *param)
 	return 0;
 }
 
+static int
+space_run_ck_constraints(struct space *space, void *param)
+{
+	if (space_is_system(space))
+		return 0;
+	struct ck_constraint *ck;
+	rlist_foreach_entry(ck, &space->ck_constraint, link)
+		ck->is_enabled = (bool)param;
+	(void)trigger_run(&on_alter_space, space);
+	return 0;
+}
+
 /**
  * Secondary indexes are built in bulk after all data is
  * recovered. This function enables secondary keys on a space.
@@ -315,6 +328,8 @@ memtx_engine_end_recovery(struct engine *engine)
 		if (space_foreach(memtx_build_secondary_keys, memtx) != 0)
 			return -1;
 	}
+	if (space_foreach(space_run_ck_constraints, (void *)true) != 0)
+		unreachable();
 	xdir_collect_inprogress(&memtx->snap_dir);
 	return 0;
 }
diff --git a/test/sql/checks.result b/test/sql/checks.result
index 50347bc3a..3f121226b 100644
--- a/test/sql/checks.result
+++ b/test/sql/checks.result
@@ -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,119 @@ s2:drop()
 physics_ck
 ---
 - space_id: <ID>
+  is_enabled: true
   name: physics
   expr: X > Y
 ...
 physics_ck:drop()
 ---
 ...
+--
+-- gh-4244: An ability to disable CK constraints
+-- Make shure that ck constraints are turning on and of 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 CHECK (a < 5) primary key);");
+---
+- row_count: 1
+...
+box.space.TEST:insert({10})
+---
+- error: 'Check constraint failed ''ck_unnamed_TEST_1'': a < 5'
+...
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(false)
+---
+...
+box.space.TEST:insert({11})
+---
+- [11]
+...
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(true)
+---
+...
+box.space.TEST:insert({12})
+---
+- error: 'Check constraint failed ''ck_unnamed_TEST_1'': a < 5'
+...
+-- Enshure that ck constraints are not validated during
+-- normal recovery. Now TEST space has a tuple {12} violating
+-- defined CK constraint.
+test_run:cmd("restart server default")
+box.space.TEST:select()
+---
+- - [11]
+...
+box.space.TEST:insert({12})
+---
+- error: 'Check constraint failed ''ck_unnamed_TEST_1'': a < 5'
+...
+box.execute("DROP TABLE test;")
+---
+- row_count: 1
+...
+-- Enshure that ck constraints are validated during
+-- force recovery.
+test_run:cmd('create server test with script = "xlog/force_recovery.lua"')
+---
+- true
+...
+test_run:cmd("start server test")
+---
+- true
+...
+test_run:cmd("switch test")
+---
+- true
+...
+engine = test_run:get_cfg('engine')
+---
+...
+box.execute('pragma sql_default_engine=\''..engine..'\'')
+---
+- row_count: 0
+...
+box.execute("CREATE TABLE test(a  INT CHECK (a < 10) primary key);");
+---
+- row_count: 1
+...
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(false)
+---
+...
+box.space.TEST:insert({11})
+---
+- [11]
+...
+box.space.TEST:insert({2})
+---
+- [2]
+...
+test_run:cmd("restart server test")
+box.space.TEST:select()
+---
+- - [2]
+...
+box.execute("DROP TABLE test;")
+---
+- row_count: 1
+...
+test_run = require('test_run').new()
+---
+...
+test_run:cmd('switch default')
+---
+- true
+...
+test_run:cmd('stop server test')
+---
+- true
+...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
index cde213f8b..9716647d0 100644
--- a/test/sql/checks.test.lua
+++ b/test/sql/checks.test.lua
@@ -234,4 +234,45 @@ s2:drop()
 physics_ck
 physics_ck:drop()
 
+--
+-- gh-4244: An ability to disable CK constraints
+-- Make shure that ck constraints are turning on and of with
+-- :enable configurator.
+--
+engine = test_run:get_cfg('engine')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute("CREATE TABLE test(a  INT CHECK (a < 5) primary key);");
+box.space.TEST:insert({10})
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(false)
+box.space.TEST:insert({11})
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(true)
+box.space.TEST:insert({12})
+-- Enshure that ck constraints are not validated during
+-- normal recovery. Now TEST space has a tuple {12} violating
+-- defined CK constraint.
+test_run:cmd("restart server default")
+box.space.TEST:select()
+box.space.TEST:insert({12})
+box.execute("DROP TABLE test;")
+
+-- Enshure that ck constraints are validated during
+-- force recovery.
+test_run:cmd('create server test with script = "xlog/force_recovery.lua"')
+test_run:cmd("start server test")
+test_run:cmd("switch test")
+
+engine = test_run:get_cfg('engine')
+box.execute('pragma sql_default_engine=\''..engine..'\'')
+box.execute("CREATE TABLE test(a  INT CHECK (a < 10) primary key);");
+box.space.TEST.ck_constraint.ck_unnamed_TEST_1:enable(false)
+box.space.TEST:insert({11})
+box.space.TEST:insert({2})
+test_run:cmd("restart server test")
+box.space.TEST:select()
+box.execute("DROP TABLE test;")
+
+test_run = require('test_run').new()
+test_run:cmd('switch default')
+test_run:cmd('stop server test')
+
 test_run:cmd("clear filter")
-- 
2.23.0





More information about the Tarantool-patches mailing list