From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: tarantool-patches@freelists.org, Nikita Pettik <korablev@tarantool.org> Subject: [tarantool-patches] Re: [PATCH 2/5] schema: add new system space for FK constraints Date: Wed, 18 Jul 2018 00:05:05 +0300 [thread overview] Message-ID: <83369e84-6fba-adbf-8751-2141a3e256b1@tarantool.org> (raw) In-Reply-To: <a9c3e72b567a8f375af376506b9646c792206007.1531443603.git.korablev@tarantool.org> Thanks for the patch! See 18 comments below and a patch on the branch. On 13/07/2018 05:04, Nikita Pettik wrote: > This patch introduces new system space to persist foreign keys 1. Typos: contraints, chils. > contraints. Format of the space: > > _fk_constraint (space id = 350) > > [<contraint name> STR, <parent id> UINT, <child id> UINT,> <is deferred> BOOL, <match> STR, <on delete action> STR, > <on update action> STR, <field links> ARRAY<MAP< UINT : UINT >>] > > FK constraint is local to space, so every pair <FK name, child id> > is unique (and it is PK in _fk_constraint space). > > After insertion into this space, new instance describing FK constraint > is created. FK constraints are held in data-dictionary as two lists > (for child and parent constraints) in struct space. > > There is a list of FK restrictions: > - At the time of FK creation parent and chils spaces must exist; > - VIEWs can't be involved into FK processing; > - Child space must be empty; > - Types of referencing and referenced fields must match; 2. How about not match, but fit? See field_type1_contains_type2() and its usages. > - Collations of referencing and referenced fields must match; > - Referenced fields must compose unique index; > > Until space (child) features FK constraints it isn't allowed to be > dropped. Implicitly referenced index also can't be dropped > (and that is why parent space can't be dropped). But :drop() method > of child space firstly deletes all FK constraint (the same as SQL > triggers, indexes etc) and then removes entry from _space. > > Part of #3271 > --- > src/box/CMakeLists.txt | 1 + > src/box/alter.cc | 432 ++++++++++++++++++++++++++++++++++++++++- > src/box/alter.h | 1 + > src/box/bootstrap.snap | Bin 1704 -> 1798 bytes > src/box/errcode.h | 4 + > src/box/fkey.c | 69 +++++++ > src/box/fkey.h | 163 ++++++++++++++++ > src/box/lua/schema.lua | 6 + > src/box/lua/space.cc | 2 + > src/box/lua/upgrade.lua | 16 ++ > src/box/schema.cc | 16 ++ > src/box/schema_def.h | 14 ++ > src/box/space.c | 2 + > src/box/space.h | 3 + > src/box/sql.c | 8 + > src/box/sql/fkey.c | 32 +-- > src/box/sql/tarantoolInt.h | 1 + > test/box/access_misc.result | 5 + > test/box/access_sysview.result | 6 +- > test/box/alter.result | 5 +- > test/box/misc.result | 2 + > test/engine/iterator.result | 2 +- > test/sql/foreign-keys.result | 316 ++++++++++++++++++++++++++++++ > test/sql/foreign-keys.test.lua | 144 ++++++++++++++ > 24 files changed, 1214 insertions(+), 36 deletions(-) > create mode 100644 src/box/fkey.c > create mode 100644 src/box/fkey.h > create mode 100644 test/sql/foreign-keys.result > create mode 100644 test/sql/foreign-keys.test.lua > > diff --git a/src/box/alter.cc b/src/box/alter.cc > index 89b11dcd3..aaa56bd21 100644 > --- a/src/box/alter.cc > +++ b/src/box/alter.cc > @@ -1889,6 +1916,22 @@ on_replace_dd_index(struct trigger * /* trigger */, void *event) > "can not add a secondary key before primary"); > } > > + /* > + * Can't drop index if foreign key constraints references > + * this index. > + */ > + if (new_tuple == NULL) { 3. But it can be non-drop, but alter, for example. Drop case is processed below: /* Case 1: drop the index, if it is dropped. */ if (old_index != NULL && new_tuple == NULL) { What if I want to rename an index? Also see index_def_change_requires_rebuild. > + struct fkey *fk = old_space->parent_fkey; > + while (fk != NULL) { > + if (old_space->parent_fkey->index_id == iid) { > + tnt_raise(ClientError, ER_ALTER_SPACE, > + space_name(old_space), > + "can not drop referenced index"); > + } > + fk = fk->fkey_parent_next; > + } > + } > + > @@ -3404,6 +3447,387 @@ on_replace_dd_trigger > txn_on_commit(txn, on_commit); > } > > +/** > + * Decode MsgPack array of links. It consists from maps: > + * {parent_id (UINT) : child_id (UINT)}. > + * > + * @param data MsgPack array of links. > + * @param[out] out_count Count of links. > + * @param constraint_name Constraint name to use in error > + * messages. > + * @param constraint_len Length of constraint name. > + * @param errcode Errcode for client errors. > + * @retval Array of links. > + */ > +static struct field_link * > +fkey_links_decode(const char *data, uint32_t *out_count, > + const char *constraint_name, uint32_t constraint_len, > + uint32_t errcode) > +{ > + assert(mp_typeof(*data) == MP_ARRAY); > + uint32_t count = mp_decode_array(&data); > + if (count == 0) { > + tnt_raise(ClientError, errcode, > + tt_cstr(constraint_name, constraint_len), > + "at least one link must be specified"); > + } > + *out_count = count; > + size_t size = count * sizeof(struct field_link); > + struct field_link *region_links = > + (struct field_link *) region_alloc_xc(&fiber()->gc, size); > + memset(region_links, 0, size); > + const char **map = &data; > + for (uint32_t i = 0; i < count; ++i) { > + uint32_t map_sz = mp_decode_map(map); > + if (map_sz != 2) { > + tnt_raise(ClientError, errcode, > + tt_cstr(constraint_name, constraint_len), > + tt_sprintf("link must be map with 2 fields")); > + } > + if (mp_typeof(**map) != MP_STR) { > + tnt_raise(ClientError, errcode, > + tt_cstr(constraint_name, constraint_len), > + tt_sprintf("link %d is not map "\ > + "with string keys", i)); > + } 4. What about a second key? mp_typeof(**map) right after decoding its header returns type of the first key. But second can be of another type. And why do we need this {child = , parent = }, ... sequence? Lets just use {<uint>, <uint>}, {<uint>, <uint>}, {<uint>, <uint>} ... It is more compact and simpler to parse. Or even just two arrays: first child and second parent field numbers. It is the most canonical way similar to SQL, it is not? When you specify two column ranges. But in the internal format I vote to keep field_link. In the next patches I see, that common usage case is to iterate over the pairs, so for cache it is better to keep them. > + for (uint8_t j = 0; j < map_sz; ++j) { > + uint32_t key_len; > + const char *key = mp_decode_str(map, &key_len); > + if (key_len == 6 && > + memcmp(key, "parent", key_len) == 0) { > + region_links[i].parent_field = > + mp_decode_uint(map); > + } else if (key_len == 5 && > + memcmp(key, "child", key_len) == 0) { > + region_links[i].child_field = > + mp_decode_uint(map); > + } else { > + char *errmsg = tt_static_buf(); > + snprintf(errmsg, TT_STATIC_BUF_LEN, > + "unexpected key of link %d '%.*s'", i, > + key_len, key); > + tnt_raise(ClientError, errcode, > + tt_cstr(constraint_name, > + constraint_len), errmsg); > + } > + } > + } > + return region_links; > +} > + > +/** > + * Replace entry in child's and parent's lists of > + * FK constraints. > + * > + * @param child Child space of FK constraint. > + * @param parent Parent space of FK constraint. > + * @param new_fkey Constraint to be added to child and parent. > + * @param[out] old_fkey Constraint to be found and replaced. > + */ > +static void > +fkey_list_replace(struct space *child, struct space *parent, const char *name, > + struct fkey *new_fkey, struct fkey **old_fkey) > +{ 5. I see that for fkey you actually have invented rlist. Please, see rlist.h and use it for fkeys. It already has rlist_foreach_entry, rlist_add, rlist_add_tail, rlist_del etc. The struct foreign key will have two rlist links: in_child and in_parent. > + *old_fkey = NULL; > + struct fkey **fk = &parent->parent_fkey; > + while (*fk != NULL && !(strcmp((*fk)->def->name, name) == 0 && > + (*fk)->def->child_id == child->def->id)) > + fk = &((*fk)->fkey_parent_next); > + if (*fk != NULL) { > + *old_fkey = *fk; > + *fk = (*fk)->fkey_parent_next; > + } > + if (new_fkey != NULL) { > + new_fkey->fkey_parent_next = parent->parent_fkey; > + parent->parent_fkey = new_fkey; > + } > + fk = &child->child_fkey; > + /* In child's list all constraints are unique by name. */ > + while (*fk != NULL && strcmp((*fk)->def->name, name) != 0) > + fk = &((*fk)->fkey_child_next); > + if (*fk != NULL) { > + assert(*old_fkey == *fk); > + *fk = (*fk)->fkey_child_next; > + } > + if (new_fkey != NULL) { > + new_fkey->fkey_child_next = child->child_fkey; > + child->child_fkey = new_fkey; > + } > +} > + > +/** > + * On rollback of creation we remove FK constraint from DD, i.e. > + * from parent's and child's lists of constraints and > + * release memory. > + */ > +static void > +on_create_fkey_rollback(struct trigger *trigger, void *event) > +{ > + (void) event; > + struct fkey *fk = (struct fkey *)trigger->data; > + struct space *parent = space_by_id(fk->def->parent_id); > + struct space *child = space_by_id(fk->def->child_id); > + struct fkey *fkey = NULL; > + fkey_list_replace(child, parent, fk->def->name, NULL, &fkey); > + fkey_delete(fkey); 6. After you fixed the previous remark, this function would collapse into two rlist_del, it is not? The same about the next function. Btw, these functions are identical. Can you keep only one? Same about on_drop_fkey_commit and on_replace_fkey_commit. > +} > + > +/** Return old FK and release memory for the new one. */ > +static void > +on_replace_fkey_rollback(struct trigger *trigger, void *event) > +{ > + (void) event; > + struct fkey *fk = (struct fkey *)trigger->data; > + struct space *parent = space_by_id(fk->def->parent_id); > + struct space *child = space_by_id(fk->def->child_id); > + struct fkey *old_fkey = NULL; > + fkey_list_replace(child, parent, fk->def->name, fk, &old_fkey); > + fkey_delete(old_fkey); > +} > + > +/** Release memory for old foreign key. */ > +static void > +on_replace_fkey_commit(struct trigger *trigger, void *event) > +{ > + (void) event; > + struct fkey *fk = (struct fkey *)trigger->data; > + fkey_delete(fk); > +} > + > +/** On rollback of drop simply return back FK to DD. */ > +static void > +on_drop_fkey_rollback(struct trigger *trigger, void *event) > +{ > + (void) event; > + struct fkey *fk_to_restore = (struct fkey *)trigger->data; > + struct space *parent = space_by_id(fk_to_restore->def->parent_id); > + struct space *child = space_by_id(fk_to_restore->def->child_id); > + struct fkey *old_fk; > + fkey_list_replace(child, parent, fk_to_restore->def->name, fk_to_restore, > + &old_fk); > + assert(old_fk == NULL); > +} > + > +/** > + * On commit of drop we have already deleted foreign key from > + * both (parent's and child's) lists, so just release memory. > + */ > +static void > +on_drop_fkey_commit(struct trigger *trigger, void *event) > +{ > + (void) event; > + struct fkey *fk = (struct fkey *)trigger->data; > + fkey_delete(fk); > +} > + > +/** A trigger invoked on replace in the _fk_constraint space. */ > +static void > +on_replace_dd_fk_constraint(struct trigger * /* trigger*/, void *event) > +{ > + struct txn *txn = (struct txn *) event; > + txn_check_singlestatement_xc(txn, "Space _fk_constraint"); > + struct txn_stmt *stmt = txn_current_stmt(txn); > + struct tuple *old_tuple = stmt->old_tuple; > + struct tuple *new_tuple = stmt->new_tuple; > + if (new_tuple != NULL) { > + /* Create or replace foreign key. */ > + struct fkey_def *fk_def = > + fkey_def_new_from_tuple(new_tuple, > + ER_CREATE_FK_CONSTRAINT); > + auto fkey_def_guard = make_scoped_guard([=] { free(fk_def); }); > + struct space *child_space = > + space_cache_find_xc(fk_def->child_id); > + if (child_space->def->opts.is_view) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, > + "referencing space can't be VIEW"); > + } > + struct space *parent_space = > + space_cache_find_xc(fk_def->parent_id); > + if (parent_space->def->opts.is_view) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, > + "referenced space can't be VIEW"); > + } > + /* > + * FIXME: until SQL triggers are completely > + * integrated into server (i.e. we are able to > + * invoke triggers even if DML occurred via Lua > + * interface), it makes no sense to provide any > + * checks on existing data in space. > + */ > + struct index *pk = space_index(child_space, 0); > + if (index_count(pk, ITER_ALL, NULL, 0) > 0) { 7. Lets better use index_size. Index_count on Vinyl takes O(N) time scanning disk. I think, it is ok to forbid new fk on a Vinyl space, that logically is empty, but actually still contains some non-compacted garbage. Anyway user now is able to force the compaction and try to create fk again. > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, > + "referencing space must be empty"); > + } > + /* Check types of referenced fields. */ > + for (uint32_t i = 0; i < fk_def->field_count; ++i) { > + uint32_t child_fieldno = fk_def->links[i].child_field; > + uint32_t parent_fieldno = fk_def->links[i].parent_field; > + if (child_fieldno >= child_space->def->field_count || > + parent_fieldno >= parent_space->def->field_count) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, "foreign key refers to " > + "nonexistent field"); > + } > + if (child_space->def->fields[child_fieldno].type != > + parent_space->def->fields[parent_fieldno].type) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, "field type mismatch"); > + } > + if (child_space->def->fields[child_fieldno].coll_id != > + parent_space->def->fields[parent_fieldno].coll_id) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, > + "field collation mismatch"); > + } > + } > + /* > + * Search for suitable index in parent space: > + * it must be unique and consist exactly from > + * referenced columns (but order may be different). 8. Should this index has the same collations? For a column we can define multiple collations in different indexes. And in one index the value may be unique, but in another may be not. > + */ > + struct index *fk_index = NULL; > + for (uint32_t i = 0; i < parent_space->index_count; ++i) { > + struct index *idx = space_index(parent_space, i); > + if (!idx->def->opts.is_unique) > + continue; > + if (idx->def->key_def->part_count != > + fk_def->field_count) > + continue; > + uint32_t j; > + for (j = 0; j < fk_def->field_count; ++j) { > + if (idx->def->key_def->parts[j].fieldno != > + fk_def->links[j].parent_field) > + break; > + } > + if (j != fk_def->field_count) > + continue; 9. See key_def_find. 10. Above you have said that order may be different, but here I see that once you have found a unique index, you merely check that it has sequentially the same field numbers as the fk. > + fk_index = idx; > + break; > + } > + if (fk_index == NULL) { > + tnt_raise(ClientError, ER_CREATE_FK_CONSTRAINT, > + fk_def->name, "referenced fields don't " > + "compose unique index"); > + } > + struct fkey *fkey = (struct fkey *) malloc(sizeof(*fkey)); 11. I know how you like memory mapping. How about merge fkey_def into fkey memory? I see, that it is relatively simple and linear thing. > + if (fkey == NULL) > + tnt_raise(OutOfMemory, sizeof(*fkey), "malloc", "fkey"); > + auto fkey_guard = make_scoped_guard([=] { free(fkey); }); > + memset(fkey, 0, sizeof(*fkey)); > + fkey->def = fk_def; > + fkey->index_id = fk_index->def->iid; > + struct fkey *old_fk; > + fkey_list_replace(child_space, parent_space, fk_def->name, > + fkey, &old_fk); > + if (old_tuple == NULL) { > + struct trigger *on_rollback = > + txn_alter_trigger_new(on_create_fkey_rollback, > + fkey); > + txn_on_rollback(txn, on_rollback); > + assert(old_fk == NULL); > + } else { > + struct trigger *on_rollback = > + txn_alter_trigger_new(on_replace_fkey_rollback, > + fkey); > + txn_on_rollback(txn, on_rollback); > + struct trigger *on_commit = > + txn_alter_trigger_new(on_replace_fkey_commit, > + old_fk); > + txn_on_commit(txn, on_commit); > + } > + fkey_def_guard.is_active = false; > + fkey_guard.is_active = false; > + } else if (new_tuple == NULL && old_tuple != NULL) { > + /* Drop foreign key. */ > + struct fkey_def *fk_def = > + fkey_def_new_from_tuple(old_tuple, > + ER_DROP_FK_CONSTRAINT); > + auto fkey_guard = make_scoped_guard([=] { free(fk_def); }); > + struct space *parent_space = > + space_cache_find_xc(fk_def->parent_id); > + struct space *child_space = > + space_cache_find_xc(fk_def->child_id); > + struct fkey *old_fkey = NULL; > + fkey_list_replace(child_space, parent_space, fk_def->name, NULL, > + &old_fkey); > + struct trigger *on_commit = > + txn_alter_trigger_new(on_drop_fkey_commit, old_fkey); > + txn_on_commit(txn, on_commit); > + struct trigger *on_rollback = > + txn_alter_trigger_new(on_drop_fkey_rollback, old_fkey); > + txn_on_rollback(txn, on_rollback); > + } > +} > + > diff --git a/src/box/fkey.c b/src/box/fkey.c > new file mode 100644 > index 000000000..e45889a0d > --- /dev/null > +++ b/src/box/fkey.c > +void > +fkey_trigger_delete(struct sqlite3 *db, struct sql_trigger *p) > +{ 12. Why do you need this function when sql_trigger_delete exists? > + if (p != NULL) { > + struct TriggerStep *step = p->step_list; > + sql_expr_delete(db, step->pWhere, false); > + sql_expr_list_delete(db, step->pExprList); > + sql_select_delete(db, step->pSelect); > + sql_expr_delete(db, p->pWhen, false); > + sqlite3DbFree(db, p); > + } > +} > + > diff --git a/src/box/fkey.h b/src/box/fkey.h > new file mode 100644 > index 000000000..1b6ea71d9 > --- /dev/null > +++ b/src/box/fkey.h > @@ -0,0 +1,163 @@ > +#ifndef TARANTOOL_BOX_FKEY_H_INCLUDED > +#define TARANTOOL_BOX_FKEY_H_INCLUDED > +/* > + * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file. > + * > + * Redistribution and use in source and binary forms, with or > + * without modification, are permitted provided that the following > + * conditions are met: > + * > + * 1. Redistributions of source code must retain the above > + * copyright notice, this list of conditions and the > + * following disclaimer. > + * > + * 2. Redistributions in binary form must reproduce the above > + * copyright notice, this list of conditions and the following > + * disclaimer in the documentation and/or other materials > + * provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF > + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > +#include <stdbool.h> > +#include <stdint.h> > + > +#include "space.h" > + > +#if defined(__cplusplus) > +extern "C" { > +#endif /* defined(__cplusplus) */ > + > +struct sqlite3; 13. Is it possible to drop this announcement after you replaced fkey_trigger_delete with sql_trigger_delete? > diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua > index 87c79bdde..30d8b0081 100644 > --- a/src/box/lua/schema.lua > +++ b/src/box/lua/schema.lua > @@ -506,6 +506,7 @@ box.schema.space.drop = function(space_id, space_name, opts) > local _vindex = box.space[box.schema.VINDEX_ID] > local _truncate = box.space[box.schema.TRUNCATE_ID] > local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID] > + local _fk_constraint = box.space[box.schema.FK_CONSTRAINT_ID] > local sequence_tuple = _space_sequence:delete{space_id} > if sequence_tuple ~= nil and sequence_tuple[3] == true then > -- Delete automatically generated sequence. > @@ -519,6 +520,11 @@ box.schema.space.drop = function(space_id, space_name, opts) > local v = keys[i] > _index:delete{v[1], v[2]} > end > + for _, t in _fk_constraint.index.primary:pairs() do > + if t.child_id == space_id then 14. Fullscan here looks bad. Can we create a secondary index on _fk_constraint by child_id? Or create a Lua C function, that takes space and returns names of its child fks? If we use the index, then we are able to optimize VDBE deletion of the FKs in the main patch (sql: introduce ADD CONSTRAINT statement), where you delete by the composite key {name, child_id}, but actually child_id is enough. > + _fk_constraint:delete{t.name, t.child_id} > + end > + end > diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua > index f112a93ae..772f55cb2 100644 > --- a/src/box/lua/upgrade.lua > +++ b/src/box/lua/upgrade.lua > @@ -509,6 +509,22 @@ local function upgrade_to_2_1_0() > {unique = true}, {{0, 'string'}, {1, 'string'}, > {5, 'scalar'}}} > > + local fk_constr_ft = {{name='name', type='string'}, > + {name='child_id', type='unsigned'}, > + {name='parent_id', type='unsigned'}, > + {name='deferred', type='boolean'}, 15. Flag, so 'is_deferred', it is not? > + {name='match', type='string'}, > + {name='on_delete', type='string'}, > + {name='on_update', type='string'}, > + {name='links', type='array'}} > + log.info("create space _fk_constraint") > + _space:insert{box.schema.FK_CONSTRAINT_ID, ADMIN, '_fk_constraint', 'memtx', > + 0, setmap({}), fk_constr_ft} > + > + log.info("create index primary on _fk_constraint") > + _index:insert{box.schema.FK_CONSTRAINT_ID, 0, 'primary', 'tree', > + {unique = true}, {{0, 'string'}, {1, 'unsigned'}}} > + > -- Nullability wasn't skipable. This was fixed in 1-7. > -- Now, abscent field means NULL, so we can safely set second > -- field in format, marking it nullable. > diff --git a/src/box/space.h b/src/box/space.h > index 7da2ee51f..fc5e8046f 100644 > --- a/src/box/space.h > +++ b/src/box/space.h > @@ -183,6 +183,9 @@ struct space { > * of index id. > */ > struct index **index; > + /** Foreign key constraints. */ 16. It would be good to have here an explanation about who is child, who is parent. > + struct fkey *parent_fkey; > + struct fkey *child_fkey; > }; > > diff --git a/test/box/access_misc.result b/test/box/access_misc.result > index 5a2563d55..62c92ca03 100644 > --- a/test/box/access_misc.result > +++ b/test/box/access_misc.result > @@ -809,6 +809,11 @@ box.space._space:select() > - [349, 1, '_sql_stat4', 'memtx', 0, {}, [{'name': 'tbl', 'type': 'string'}, {'name': 'idx', > 'type': 'string'}, {'name': 'neq', 'type': 'string'}, {'name': 'nlt', 'type': 'string'}, > {'name': 'ndlt', 'type': 'string'}, {'name': 'sample', 'type': 'scalar'}]] > + - [350, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'}, > + {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'}, > + {'name': 'deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'}, > + {'name': 'on_delete', 'type': 'string'}, {'name': 'on_update', 'type': 'string'}, > + {'name': 'links', 'type': 'array'}]] 17. Please, keep spare room after sql_stat4 in 8 identifiers for future needs. So as _fk_constraint was 356 (sql_stat1 348 + 8). > ... > box.space._func:select() > --- > diff --git a/test/engine/iterator.result b/test/engine/iterator.result > index a36761df8..ba9b0545a 100644 > --- a/test/engine/iterator.result > +++ b/test/engine/iterator.result > @@ -4211,7 +4211,7 @@ s:replace{35} > ... > state, value = gen(param,state) > --- > -- error: 'builtin/box/schema.lua:1049: usage: next(param, state)' > +- error: 'builtin/box/schema.lua:1055: usage: next(param, state)' 18. This test fails on each schema change. Lets remove this file:line from the error message alongside the patch.
next prev parent reply other threads:[~2018-07-17 21:05 UTC|newest] Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-07-13 2:04 [tarantool-patches] [PATCH 0/5] Move FK constraints to server Nikita Pettik 2018-07-13 2:04 ` [tarantool-patches] [PATCH 1/5] sql: prohibit creation of FK on unexisting tables Nikita Pettik 2018-07-17 21:05 ` [tarantool-patches] " Vladislav Shpilevoy 2018-07-25 10:03 ` n.pettik 2018-07-26 20:12 ` Vladislav Shpilevoy 2018-08-01 20:54 ` n.pettik 2018-08-02 22:15 ` Vladislav Shpilevoy 2018-08-06 0:27 ` n.pettik 2018-07-13 2:04 ` [tarantool-patches] [PATCH 2/5] schema: add new system space for FK constraints Nikita Pettik 2018-07-17 21:05 ` Vladislav Shpilevoy [this message] 2018-07-25 10:03 ` [tarantool-patches] " n.pettik 2018-07-26 20:12 ` Vladislav Shpilevoy 2018-08-01 20:54 ` n.pettik 2018-08-02 22:15 ` Vladislav Shpilevoy 2018-08-06 0:28 ` n.pettik 2018-08-06 18:24 ` Vladislav Shpilevoy 2018-07-13 2:04 ` [tarantool-patches] [PATCH 3/5] sql: introduce ADD CONSTRAINT statement Nikita Pettik 2018-07-17 21:05 ` [tarantool-patches] " Vladislav Shpilevoy 2018-07-25 10:03 ` n.pettik 2018-07-26 20:12 ` Vladislav Shpilevoy 2018-08-01 20:54 ` n.pettik 2018-08-02 22:15 ` Vladislav Shpilevoy 2018-08-06 0:28 ` n.pettik 2018-08-06 18:24 ` Vladislav Shpilevoy 2018-08-06 23:43 ` n.pettik 2018-07-13 2:04 ` [tarantool-patches] [PATCH 4/5] sql: display error on FK creation and drop failure Nikita Pettik 2018-07-17 21:04 ` [tarantool-patches] " Vladislav Shpilevoy 2018-07-25 10:03 ` n.pettik 2018-07-26 20:11 ` Vladislav Shpilevoy 2018-07-13 2:04 ` [tarantool-patches] [PATCH 5/5] sql: remove SQLITE_OMIT_FOREIGN_KEY define guard Nikita Pettik 2018-07-17 21:04 ` [tarantool-patches] Re: [PATCH 0/5] Move FK constraints to server Vladislav Shpilevoy 2018-08-07 14:57 ` Kirill Yukhin
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=83369e84-6fba-adbf-8751-2141a3e256b1@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=korablev@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='[tarantool-patches] Re: [PATCH 2/5] schema: add new system space for FK constraints' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox