From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 6631A23102 for ; Thu, 12 Sep 2019 04:06:49 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id SML1fxD-zGlN for ; Thu, 12 Sep 2019 04:06:49 -0400 (EDT) Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 89E6A230F0 for ; Thu, 12 Sep 2019 04:06:48 -0400 (EDT) From: Kirill Shcherbatov Subject: [tarantool-patches] [PATCH v2 1/3] box: an ability to disable CK constraints Date: Thu, 12 Sep 2019 11:06:41 +0300 Message-Id: <9ff97ab5cf810e52988267f150c330826d2e4910.1568275504.git.kshcherbatov@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.org, korablev@tarantool.org Cc: Kirill Shcherbatov @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 #include +#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: + is_enabled: true name: physics expr: X < Y ... s1.ck_constraint.greater --- - space_id: + is_enabled: true name: greater expr: X > 20 ... s2.ck_constraint.physics --- - space_id: + is_enabled: true name: physics expr: X > Y ... s2.ck_constraint.greater --- - space_id: + is_enabled: true name: greater expr: X > 20 ... @@ -685,6 +689,7 @@ s2.ck_constraint.greater:drop() s2.ck_constraint.physics --- - space_id: + is_enabled: true name: physics expr: X > Y ... @@ -716,12 +721,119 @@ s2:drop() physics_ck --- - space_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