diff --git a/src/box/alter.cc b/src/box/alter.cc index fd62dbc23248dc435065ce6b2022e6dde932b385..965945af6e24a2f6b02d5907e35b6bc2e0971a57 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 Binary files a/src/box/bootstrap.snap and b/src/box/bootstrap.snap differ diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 14ad4de1cae9ffee3efc4ef3e4ba81e9b7c7af33..919003957f3916899472420dc59307355be33cc7 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -748,6 +748,20 @@ local function simplify_index_parts(parts) return new_parts end +local function check_sequence_part(parts, sequence_part, + index_name, space_name) + if sequence_part <= 0 or sequence_part > #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 100da0a7906a629be9e2cc37c3f4ce4d83e2c47a..e342bfcca88f7762f99fda32627e4cc3de63c114 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 89d6e3d52f732733253aca12c84d5ca1c1712b33..23f4df0190ed3986ba385941873b244290e2321e 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 44a43ee1eac728dd7fdc2b62f27b736d2b4a2c5e..9d3287f97299fa02d939fed463a9af3f07247663 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 eeeeb950ba9c700c1aa5b95c6905ab5583d27538..dea3fad19e7e1959b52fc93ef5637ca6ade0b53e 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 13a220d13dc2894b273254498950f666eec16c3d..c3eef71c62bbd0b9289305688f9ace814c401fc4 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 6051a2529ad63cee99cb3f72df8a45ccbea6047a..91b977de2567e15c5faadc5c15b24e4b372c44f3 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 c2aac553f4512bbcf22f75e8240848daa32b29e2..1261ab9c80cc93bb6ae70edb1394c0424ac85123 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 379f6c51fb15b4ba3d6d7a0e17712ee292a5488e..de90beee6bc9abaccfd42d201b9e65a275847562 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 36ebfae092007df32493b4d338439287f5183e57..877a9b533a079e3f1bbe0cb2c37a4eb1f86f685f 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 5eed0ef4cbbca4d39d1777a04c48936d3487c7a8..8a7c349cff72de3993c3dd1aa4989c5fcfc3d4e2 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} +--- +- ['c', 100, 100] +... +s:insert{'c', 101, box.NULL} +--- +- ['c', 101, 101] +... +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 6459419ede4f0b30a08b2f8e0e4d45c04bf33be0..d931a94e29c8eaccfbff401cc72c2a0910844751 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} +s:insert{'c', 101, box.NULL} +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()