From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Vladimir Davydov Subject: [PATCH 3/4] schema: allow to set sequence for any index part, not just the first Date: Wed, 15 May 2019 13:33:48 +0300 Message-Id: <924606cabe248b6d1cb8fb126de2c5638537398d.1557916311.git.vdavydov.dev@gmail.com> In-Reply-To: References: In-Reply-To: References: To: tarantool-patches@freelists.org List-ID: Closes #4009 @TarantoolBot document Title: Sequence can now be set for an index part other than the first Initially one could attach a sequence (aka autoincrement) only to the first index part. Now it's possible to attach a sequence to any primary index part. The part still must be integer though. Syntax: ``` box.schema.space.create('test') box.space.test:create_index('primary', { parts = {{1, 'string'}, {2, 'unsigned'}, {3, 'unsigned'}}, sequence = true, sequence_part = 2 }) box.space.test:insert{'a', box.null, 1} -- inserts {'a', 1, 1} ``` Note, `sequence_part` option is 1-base. If `sequence_part` is omitted, 1 is used, which assures backward compatibility with the original behavior. One can also attach a sequence to another index part using `index.alter` (the code below continues the example above): ``` box.space.test.index.primary:alter{sequence_part = 3} box.space.test:insert{'a', 1, box.null, 'x'} -- inserts {'a', 1, 2, 'x'} ``` --- src/box/alter.cc | 27 +++++++-- src/box/bootstrap.snap | Bin 4374 -> 4379 bytes src/box/lua/schema.lua | 68 +++++++++++++++------ src/box/lua/space.cc | 7 +++ src/box/lua/upgrade.lua | 35 ++++++++++- src/box/request.c | 2 +- src/box/schema_def.h | 1 + src/box/space.h | 5 ++ src/box/sql/build.c | 7 ++- src/box/sql/insert.c | 2 +- test/box-py/bootstrap.result | 5 +- test/box/access_misc.result | 3 +- test/box/sequence.result | 141 +++++++++++++++++++++++++++++++++++++++++-- test/box/sequence.test.lua | 50 +++++++++++++-- 14 files changed, 312 insertions(+), 41 deletions(-) diff --git a/src/box/alter.cc b/src/box/alter.cc index 9279426d..2d43a9d2 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -124,9 +124,14 @@ access_check_ddl(const char *name, uint32_t object_id, uint32_t owner_uid, * is incompatible with a sequence. */ static void -index_def_check_sequence(struct index_def *index_def, const char *space_name) +index_def_check_sequence(struct index_def *index_def, uint32_t sequence_part, + const char *space_name) { - enum field_type type = index_def->key_def->parts[0].type; + if (sequence_part >= index_def->key_def->part_count) { + tnt_raise(ClientError, ER_MODIFY_INDEX, index_def->name, + space_name, "sequence part is out of bounds"); + } + enum field_type type = index_def->key_def->parts[sequence_part].type; if (type != FIELD_TYPE_UNSIGNED && type != FIELD_TYPE_INTEGER) { tnt_raise(ClientError, ER_MODIFY_INDEX, index_def->name, space_name, "sequence cannot be used with " @@ -279,7 +284,8 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *space) index_def_check_xc(index_def, space_name(space)); space_check_index_def_xc(space, index_def); if (index_def->iid == 0 && space->sequence != NULL) - index_def_check_sequence(index_def, space_name(space)); + index_def_check_sequence(index_def, space->sequence_part, + space_name(space)); index_def_guard.is_active = false; return index_def; } @@ -855,6 +861,7 @@ alter_space_do(struct txn *txn, struct alter_space *alter) space_prepare_alter_xc(alter->old_space, alter->new_space); alter->new_space->sequence = alter->old_space->sequence; + alter->new_space->sequence_part = alter->old_space->sequence_part; memcpy(alter->new_space->access, alter->old_space->access, sizeof(alter->old_space->access)); @@ -3333,6 +3340,12 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event) BOX_SPACE_SEQUENCE_FIELD_SEQUENCE_ID); bool is_generated = tuple_field_bool_xc(tuple, BOX_SPACE_SEQUENCE_FIELD_IS_GENERATED); + /* Sequence part was added in 2.2.1. */ + uint32_t sequence_part = 0; + if (tuple_field_count(tuple) > BOX_SPACE_SEQUENCE_FIELD_PART) { + sequence_part = tuple_field_u32_xc(tuple, + BOX_SPACE_SEQUENCE_FIELD_PART); + } struct space *space = space_cache_find_xc(space_id); struct sequence *seq = sequence_cache_find(sequence_id); @@ -3365,17 +3378,21 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event) if (stmt->new_tuple != NULL) { /* INSERT, UPDATE */ struct index *pk = index_find_xc(space, 0); - index_def_check_sequence(pk->def, space_name(space)); - if (seq->is_generated) { + index_def_check_sequence(pk->def, sequence_part, + space_name(space)); + if (seq->is_generated && seq != space->sequence) { tnt_raise(ClientError, ER_ALTER_SPACE, space_name(space), "can not attach generated sequence"); } seq->is_generated = is_generated; space->sequence = seq; + space->sequence_part = sequence_part; } else { /* DELETE */ assert(space->sequence == seq); + assert(space->sequence_part == sequence_part); space->sequence = NULL; + space->sequence_part = 0; } } diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap index 871a93f9856f97636ad55b7f5260e5c6957fef36..8c3b10e8417494edba07e6eba78377226ee5a31a 100644 GIT binary patch delta 3671 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 delta 3647 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 #parts then + box.error(box.error.MODIFY_INDEX, index_name, space_name, + "sequence part is out of bounds") + end + sequence_part = parts[sequence_part] + local sequence_part_type = sequence_part.type or sequence_part[2] + if sequence_part_type ~= 'integer' and sequence_part_type ~= 'unsigned' then + box.error(box.error.MODIFY_INDEX, index_name, space_name, + "sequence cannot be used with a non-integer key") + end +end + -- Historically, some properties of an index -- are stored as tuple fields, others in a -- single field containing msgpack map. @@ -773,6 +787,7 @@ local alter_index_template = { type = 'string', parts = 'table', sequence = 'boolean, number, string', + sequence_part = 'number', } for k, v in pairs(index_options) do alter_index_template[k] = v @@ -885,16 +900,18 @@ box.schema.index.create = function(space_id, name, options) local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID] local sequence_is_generated = false local sequence = options.sequence or nil -- ignore sequence = false + local sequence_part = options.sequence_part + if sequence_part ~= nil and sequence == nil then + box.error(box.error.MODIFY_INDEX, options.name, space.name, + "sequence part cannot be used without sequence") + end if sequence ~= nil then if iid ~= 0 then box.error(box.error.MODIFY_INDEX, name, space.name, "sequence cannot be used with a secondary key") end - if #parts >= 1 and parts[1].type ~= 'integer' and - parts[1].type ~= 'unsigned' then - box.error(box.error.MODIFY_INDEX, name, space.name, - "sequence cannot be used with a non-integer key") - end + sequence_part = sequence_part or 1 + check_sequence_part(parts, sequence_part, name, space.name) if sequence == true then sequence = box.schema.sequence.create(space.name .. '_seq') sequence = sequence.id @@ -912,7 +929,8 @@ box.schema.index.create = function(space_id, name, options) end _index:insert{space_id, iid, name, options.type, index_opts, parts} if sequence ~= nil then - _space_sequence:insert{space_id, sequence, sequence_is_generated} + _space_sequence:insert{space_id, sequence, sequence_is_generated, + sequence_part - 1} end return space.index[name] end @@ -1028,32 +1046,45 @@ box.schema.index.alter = function(space_id, index_id, options) local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID] local sequence_is_generated = false local sequence = options.sequence + local sequence_part = options.sequence_part local sequence_tuple if index_id ~= 0 then - if sequence then + if sequence or sequence_part ~= nil then box.error(box.error.MODIFY_INDEX, options.name, space.name, "sequence cannot be used with a secondary key") end -- ignore 'sequence = false' for secondary indexes sequence = nil - else + end + if sequence ~= nil or sequence_part ~= nil then sequence_tuple = _space_sequence:get(space_id) - if (sequence or (sequence ~= false and sequence_tuple ~= nil)) and - #parts >= 1 and (parts[1].type or parts[1][2]) ~= 'integer' and - (parts[1].type or parts[1][2]) ~= 'unsigned' then - box.error(box.error.MODIFY_INDEX, options.name, space.name, - "sequence cannot be used with a non-integer key") + if sequence_tuple ~= nil then + -- Inherit omitted options from the attached sequence. + if sequence == nil then + sequence = sequence_tuple.sequence_id + sequence_is_generated = sequence_tuple.is_generated + end + if sequence and sequence_part == nil then + sequence_part = sequence_tuple.sequence_part + end end end + if sequence then + sequence_part = sequence_part or 1 + check_sequence_part(parts, sequence_part, options.name, space.name) + elseif sequence_part ~= nil then + box.error(box.error.MODIFY_INDEX, options.name, space.name, + "sequence part cannot be used without sequence") + end if sequence == true then if sequence_tuple == nil or sequence_tuple.is_generated == false then sequence = box.schema.sequence.create(space.name .. '_seq') sequence = sequence.id - sequence_is_generated = true else -- Space already has an automatically generated sequence. - sequence = nil + sequence = sequence_tuple.sequence_id end + sequence_is_generated = true elseif sequence then sequence = sequence_resolve(sequence) if sequence == nil then @@ -1065,8 +1096,11 @@ box.schema.index.alter = function(space_id, index_id, options) end _index:replace{space_id, index_id, options.name, options.type, index_opts, parts} - if sequence then - _space_sequence:replace{space_id, sequence, sequence_is_generated} + if sequence and (sequence_tuple == nil or + sequence_tuple.sequence_id ~= sequence or + sequence_tuple.sequence_part ~= sequence_part) then + _space_sequence:replace{space_id, sequence, sequence_is_generated, + sequence_part - 1} end if sequence ~= nil and sequence_tuple ~= nil and sequence_tuple.is_generated == true and diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc index 100da0a7..e342bfcc 100644 --- a/src/box/lua/space.cc +++ b/src/box/lua/space.cc @@ -309,6 +309,13 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i) */ lua_rawset(L, -3); + lua_pushstring(L, "sequence_part"); + if (k == 0 && space->sequence != NULL) + lua_pushnumber(L, space->sequence_part + 1); + else + lua_pushnil(L); + lua_rawset(L, -3); + if (space_is_vinyl(space)) { lua_pushstring(L, "options"); lua_newtable(L); diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua index 89d6e3d5..23f4df01 100644 --- a/src/box/lua/upgrade.lua +++ b/src/box/lua/upgrade.lua @@ -625,8 +625,12 @@ local function upgrade_to_2_1_2() update_collation_strength_field() end +-------------------------------------------------------------------------------- +-- Tarantool 2.1.3 +-------------------------------------------------------------------------------- + -- Add new collations -local function upgrade_to_2_1_3() +local function upgrade_collation_to_2_1_3() local coll_lst = { {name="af", loc_str="af"}, -- Afrikaans {name="am", loc_str="am"}, -- Amharic (no character changes, just re-ordering) @@ -737,6 +741,34 @@ local function upgrade_to_2_1_3() end end +local function upgrade_to_2_1_3() + upgrade_collation_to_2_1_3() +end + +-------------------------------------------------------------------------------- +-- Tarantool 2.2.1 +-------------------------------------------------------------------------------- + +-- Add sequence part field to _space_sequence table +local function upgrade_sequence_to_2_2_1() + log.info("add key part field to space _space_sequence") + local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID] + for _, v in _space_sequence:pairs() do + if #v == 3 then + _space_sequence:update(v[1], {{'!', 4, 0}}) + end + end + local format = _space_sequence:format() + format[4] = {name = 'part', type = 'unsigned'} + _space_sequence:format(format) +end + +local function upgrade_to_2_2_1() + upgrade_sequence_to_2_2_1() +end + +-------------------------------------------------------------------------------- + local function get_version() local version = box.space._schema:get{'version'} if version == nil then @@ -768,6 +800,7 @@ local function upgrade(options) {version = mkversion(2, 1, 1), func = upgrade_to_2_1_1, auto = true}, {version = mkversion(2, 1, 2), func = upgrade_to_2_1_2, auto = true}, {version = mkversion(2, 1, 3), func = upgrade_to_2_1_3, auto = true}, + {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true}, } for _, handler in ipairs(handlers) do diff --git a/src/box/request.c b/src/box/request.c index 44a43ee1..9d3287f9 100644 --- a/src/box/request.c +++ b/src/box/request.c @@ -163,7 +163,7 @@ request_handle_sequence(struct request *request, struct space *space) const char *data = request->tuple; const char *data_end = request->tuple_end; int len = mp_decode_array(&data); - int fieldno = pk->def->key_def->parts[0].fieldno; + int fieldno = pk->def->key_def->parts[space->sequence_part].fieldno; if (unlikely(len < fieldno + 1)) return 0; diff --git a/src/box/schema_def.h b/src/box/schema_def.h index eeeeb950..dea3fad1 100644 --- a/src/box/schema_def.h +++ b/src/box/schema_def.h @@ -216,6 +216,7 @@ enum { BOX_SPACE_SEQUENCE_FIELD_ID = 0, BOX_SPACE_SEQUENCE_FIELD_SEQUENCE_ID = 1, BOX_SPACE_SEQUENCE_FIELD_IS_GENERATED = 2, + BOX_SPACE_SEQUENCE_FIELD_PART = 3, }; /** _trigger fields. */ diff --git a/src/box/space.h b/src/box/space.h index 13a220d1..c3eef71c 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -176,6 +176,11 @@ struct space { struct space_def *def; /** Sequence attached to this space or NULL. */ struct sequence *sequence; + /** + * Auto increment part of the primary index. + * Makes sense only if sequence is set. + */ + uint32_t sequence_part; /** Enable/disable triggers. */ bool run_triggers; /** diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 6051a252..91b977de 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -951,9 +951,14 @@ emitNewSysSpaceSequenceRecord(Parse *pParse, int space_id, const char reg_seq_id /* 2. Sequence id */ sqlVdbeAddOp2(v, OP_IntCopy, reg_seq_id, first_col + 2); + + /* 3. Autogenerated. */ sqlVdbeAddOp2(v, OP_Bool, true, first_col + 3); - sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 3, first_col); + /* 4. Part id. */ + sqlVdbeAddOp2(v, OP_Integer, 0, first_col + 4); + + sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 4, first_col); return first_col; } diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index c2aac553..1261ab9c 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -98,7 +98,7 @@ sql_space_autoinc_fieldno(struct space *space) if (pk == NULL || pk->def->key_def->part_count != 1 || space->sequence == NULL) return UINT32_MAX; - return pk->def->key_def->parts[0].fieldno; + return pk->def->key_def->parts[space->sequence_part].fieldno; } /** diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result index 379f6c51..de90beee 100644 --- a/test/box-py/bootstrap.result +++ b/test/box-py/bootstrap.result @@ -4,7 +4,7 @@ box.internal.bootstrap() box.space._schema:select{} --- - - ['max_id', 511] - - ['version', 2, 1, 3] + - ['version', 2, 2, 1] ... box.space._cluster:select{} --- @@ -72,7 +72,8 @@ box.space._space:select{} - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count', 'type': 'unsigned'}]] - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, - {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]] + {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}, + {'name': 'part', 'type': 'unsigned'}]] - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'}, {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'}, {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'}, diff --git a/test/box/access_misc.result b/test/box/access_misc.result index 36ebfae0..877a9b53 100644 --- a/test/box/access_misc.result +++ b/test/box/access_misc.result @@ -812,7 +812,8 @@ box.space._space:select() - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count', 'type': 'unsigned'}]] - [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, - {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}]] + {'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'}, + {'name': 'part', 'type': 'unsigned'}]] - [356, 1, '_fk_constraint', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'}, {'name': 'child_id', 'type': 'unsigned'}, {'name': 'parent_id', 'type': 'unsigned'}, {'name': 'is_deferred', 'type': 'boolean'}, {'name': 'match', 'type': 'string'}, diff --git a/test/box/sequence.result b/test/box/sequence.result index 5eed0ef4..4f962347 100644 --- a/test/box/sequence.result +++ b/test/box/sequence.result @@ -590,6 +590,48 @@ s:create_index('pk', {parts = {1, 'number'}, sequence = 'test'}) -- error - error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot be used with a non-integer key' ... +s:create_index('pk', {sequence_part = 1}) -- error +--- +- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot + be used without sequence' +... +s:create_index('pk', {sequence = true, sequence_part = 2}) -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part is + out of bounds' +... +s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, sequence_part = 2}) -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot + be used with a non-integer key' +... +pk = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned'}}) -- ok +--- +... +pk:alter{sequence_part = 1} -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot + be used without sequence' +... +pk:alter{sequence = true, sequence_part = 1} -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot + be used with a non-integer key' +... +pk:alter{sequence = true, sequence_part = 2} -- ok +--- +... +pk:alter{sequence = false, sequence_part = 2} -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot + be used without sequence' +... +pk:alter{sequence = false} -- ok +--- +... +pk:drop() +--- +... pk = s:create_index('pk', {parts = {1, 'integer'}, sequence = 'test'}) -- ok --- ... @@ -615,6 +657,11 @@ s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = true}) -- error - error: 'Can''t create or modify index ''secondary'' in space ''test'': sequence cannot be used with a secondary key' ... +s:create_index('secondary', {parts = {2, 'unsigned'}, sequence_part = 1}) -- error +--- +- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot + be used without sequence' +... sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok --- ... @@ -700,18 +747,23 @@ sk:alter{sequence = 'test'} -- error - error: 'Can''t create or modify index ''sk'' in space ''test'': sequence cannot be used with a secondary key' ... -box.space._space_sequence:insert{s.id, sq.id, false} -- error +box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error --- - error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot be used with a non-integer key' ... +box.space._space_sequence:insert{s.id, sq.id, false, 2} -- error +--- +- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part is + out of bounds' +... sk:drop() --- ... pk:drop() --- ... -box.space._space_sequence:insert{s.id, sq.id, false} -- error +box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error --- - error: 'No index #0 is defined in space ''test''' ... @@ -1121,7 +1173,7 @@ _ = s2:create_index('pk', {sequence = 'test1_seq'}) -- error --- - error: 'Can''t modify space ''test2'': can not attach generated sequence' ... -box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false} -- error +box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false, 0} -- error --- - error: 'Can''t modify space ''test2'': can not attach generated sequence' ... @@ -1612,15 +1664,15 @@ s1.index.pk:alter({sequence = 'seq1'}) -- error --- - error: Alter access to space 'space1' is denied for user 'user' ... -box.space._space_sequence:replace{s1.id, sq1.id, false} -- error +box.space._space_sequence:replace{s1.id, sq1.id, false, 0} -- error --- - error: Read access to sequence 'seq1' is denied for user 'user' ... -box.space._space_sequence:replace{s1.id, sq2.id, false} -- error +box.space._space_sequence:replace{s1.id, sq2.id, false, 0} -- error --- - error: Alter access to space 'space1' is denied for user 'user' ... -box.space._space_sequence:replace{s2.id, sq1.id, false} -- error +box.space._space_sequence:replace{s2.id, sq1.id, false, 0} -- error --- - error: Read access to sequence 'seq1' is denied for user 'user' ... @@ -1904,3 +1956,80 @@ sequence_id == s.index.pk.sequence_id s:drop() --- ... +-- +-- gh-4009: setting sequence for an index part other than the first. +-- +s = box.schema.space.create('test') +--- +... +_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = true, sequence_part = 2}) +--- +... +sequence_id = s.index.pk.sequence_id +--- +... +sequence_id ~= nil +--- +- true +... +s.index.pk.sequence_part == 2 +--- +- true +... +s:insert{'a', box.null, 1} +--- +- ['a', 1, 1] +... +s:insert{'a', box.null, 2} +--- +- ['a', 2, 2] +... +s:insert{'b', 10, 10} +--- +- ['b', 10, 10] +... +s:insert{'b', box.null, 11} +--- +- ['b', 11, 11] +... +s.index.pk:alter{sequence_part = 3} +--- +... +s.index.pk.sequence_part == 3 +--- +- true +... +s.index.pk.sequence_id == sequence_id +--- +- true +... +s:insert{'c', 100, 100, 'x'} +--- +- ['c', 100, 100, 'x'] +... +s:insert{'c', 101, box.null, 'y'} +--- +- ['c', 101, 101, 'y'] +... +s.index.pk:alter{sequence = true, sequence_part = 2} +--- +... +s.index.pk.sequence_part == 2 +--- +- true +... +s.index.pk.sequence_id == sequence_id +--- +- true +... +s:insert{'d', 1000, 1000} +--- +- ['d', 1000, 1000] +... +s:insert{'d', box.null, 1001} +--- +- ['d', 1001, 1001] +... +s:drop() +--- +... diff --git a/test/box/sequence.test.lua b/test/box/sequence.test.lua index 6459419e..d419e369 100644 --- a/test/box/sequence.test.lua +++ b/test/box/sequence.test.lua @@ -196,6 +196,18 @@ s:create_index('pk', {parts = {1, 'string'}, sequence = 'test'}) -- error s:create_index('pk', {parts = {1, 'scalar'}, sequence = 'test'}) -- error s:create_index('pk', {parts = {1, 'number'}, sequence = 'test'}) -- error +s:create_index('pk', {sequence_part = 1}) -- error +s:create_index('pk', {sequence = true, sequence_part = 2}) -- error +s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, sequence_part = 2}) -- error + +pk = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned'}}) -- ok +pk:alter{sequence_part = 1} -- error +pk:alter{sequence = true, sequence_part = 1} -- error +pk:alter{sequence = true, sequence_part = 2} -- ok +pk:alter{sequence = false, sequence_part = 2} -- error +pk:alter{sequence = false} -- ok +pk:drop() + pk = s:create_index('pk', {parts = {1, 'integer'}, sequence = 'test'}) -- ok pk:drop() pk = s:create_index('pk', {parts = {1, 'unsigned'}, sequence = 'test'}) -- ok @@ -204,6 +216,7 @@ pk:drop() pk = s:create_index('pk') -- ok s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = 'test'}) -- error s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = true}) -- error +s:create_index('secondary', {parts = {2, 'unsigned'}, sequence_part = 1}) -- error sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok sk:alter{sequence = 'test'} -- error sk:alter{sequence = true} -- error @@ -227,10 +240,11 @@ box.space._index:delete{s.id, pk.id} -- error pk:alter{parts = {1, 'string'}, sequence = false} -- ok sk = s:create_index('sk', {parts = {2, 'unsigned'}}) sk:alter{sequence = 'test'} -- error -box.space._space_sequence:insert{s.id, sq.id, false} -- error +box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error +box.space._space_sequence:insert{s.id, sq.id, false, 2} -- error sk:drop() pk:drop() -box.space._space_sequence:insert{s.id, sq.id, false} -- error +box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error s:create_index('pk', {sequence = {}}) -- error s:create_index('pk', {sequence = 'abc'}) -- error @@ -358,7 +372,7 @@ s1 = box.schema.space.create('test1') _ = s1:create_index('pk', {sequence = true}) s2 = box.schema.space.create('test2') _ = s2:create_index('pk', {sequence = 'test1_seq'}) -- error -box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false} -- error +box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false, 0} -- error s1:drop() s2:drop() @@ -538,9 +552,9 @@ box.schema.user.grant('user', 'read', 'space', '_space_sequence') box.session.su('user') _ = s2:create_index('pk', {sequence = 'seq1'}) -- error s1.index.pk:alter({sequence = 'seq1'}) -- error -box.space._space_sequence:replace{s1.id, sq1.id, false} -- error -box.space._space_sequence:replace{s1.id, sq2.id, false} -- error -box.space._space_sequence:replace{s2.id, sq1.id, false} -- error +box.space._space_sequence:replace{s1.id, sq1.id, false, 0} -- error +box.space._space_sequence:replace{s1.id, sq2.id, false, 0} -- error +box.space._space_sequence:replace{s2.id, sq1.id, false, 0} -- error s2.index.pk:alter({sequence = 'seq2'}) -- ok box.session.su('admin') @@ -647,3 +661,27 @@ s.index.pk.parts[1].type s.index.pk:alter{sequence = true} sequence_id == s.index.pk.sequence_id s:drop() + +-- +-- gh-4009: setting sequence for an index part other than the first. +-- +s = box.schema.space.create('test') +_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = true, sequence_part = 2}) +sequence_id = s.index.pk.sequence_id +sequence_id ~= nil +s.index.pk.sequence_part == 2 +s:insert{'a', box.null, 1} +s:insert{'a', box.null, 2} +s:insert{'b', 10, 10} +s:insert{'b', box.null, 11} +s.index.pk:alter{sequence_part = 3} +s.index.pk.sequence_part == 3 +s.index.pk.sequence_id == sequence_id +s:insert{'c', 100, 100, 'x'} +s:insert{'c', 101, box.null, 'y'} +s.index.pk:alter{sequence = true, sequence_part = 2} +s.index.pk.sequence_part == 2 +s.index.pk.sequence_id == sequence_id +s:insert{'d', 1000, 1000} +s:insert{'d', box.null, 1001} +s:drop() -- 2.11.0