* [PATCH v2] schema: rework index sequence API
@ 2019-05-28 14:17 Vladimir Davydov
2019-05-28 19:07 ` [tarantool-patches] " Konstantin Osipov
2019-05-29 8:17 ` Vladimir Davydov
0 siblings, 2 replies; 3+ messages in thread
From: Vladimir Davydov @ 2019-05-28 14:17 UTC (permalink / raw)
To: tarantool-patches
Rather than passing 'sequence_part' along with 'sequence' on index
create/alter, pass a table with the following fields:
- id: sequence id or name
- field: auto increment field id or name or path in case of json index
If id is omitted, the sequence will be auto-generated (equivalent to
'sequence = true'). If field is omitted, the first indexed field is
used. Old format, i.e. passing false/true or sequence name/id instead of
a table is still supported.
Follow-up #4009
---
https://github.com/tarantool/tarantool/commits/dv/rework-sequence-api
Changes in v2:
- Store field no and path in _space_sequence as well.
- Add tests checking index.alter behavior in presence
of an auto-increment field.
v1: https://www.freelists.org/post/tarantool-patches/PATCH-schema-rework-index-sequence-API
src/box/alter.cc | 86 +++++++--
src/box/bootstrap.snap | Bin 4379 -> 4393 bytes
src/box/lua/schema.lua | 306 +++++++++++++++++++++-----------
src/box/lua/space.cc | 12 +-
src/box/lua/upgrade.lua | 21 ++-
src/box/request.c | 12 +-
src/box/schema_def.h | 3 +-
src/box/space.h | 9 +-
src/box/sql/build.c | 36 ++--
src/box/sql/insert.c | 6 +-
test/box-py/bootstrap.result | 2 +-
test/box/access.result | 9 +
test/box/access.test.lua | 3 +
test/box/access_misc.result | 2 +-
test/box/alter.result | 8 +-
test/box/sequence.result | 176 +++++++++++++-----
test/box/sequence.test.lua | 80 ++++++---
test/box/stat.result | 32 ++--
test/replication/autobootstrap.result | 6 +
test/replication/autobootstrap.test.lua | 2 +
test/vinyl/ddl.result | 6 +-
21 files changed, 563 insertions(+), 254 deletions(-)
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 965945af..ed9e5590 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -124,14 +124,30 @@ 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, uint32_t sequence_part,
- const char *space_name)
+index_def_check_sequence(struct index_def *index_def, uint32_t sequence_fieldno,
+ const char *sequence_path, const char *space_name)
{
- if (sequence_part >= index_def->key_def->part_count) {
+ struct key_def *key_def = index_def->key_def;
+ struct key_part *sequence_part = NULL;
+ for (uint32_t i = 0; i < key_def->part_count; ++i) {
+ struct key_part *part = &key_def->parts[i];
+ if (part->fieldno != sequence_fieldno)
+ continue;
+ if ((part->path == NULL && sequence_path == NULL) ||
+ (part->path != NULL && sequence_path != NULL &&
+ json_path_cmp(part->path, part->path_len,
+ sequence_path, strlen(sequence_path),
+ TUPLE_INDEX_BASE) == 0)) {
+ sequence_part = part;
+ break;
+ }
+ }
+ if (sequence_part == NULL) {
tnt_raise(ClientError, ER_MODIFY_INDEX, index_def->name,
- space_name, "sequence part is out of bounds");
+ space_name, "sequence field must be a part of "
+ "the index");
}
- enum field_type type = index_def->key_def->parts[sequence_part].type;
+ enum field_type type = 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 "
@@ -284,8 +300,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->sequence_part,
- space_name(space));
+ index_def_check_sequence(index_def, space->sequence_fieldno,
+ space->sequence_path, space_name(space));
index_def_guard.is_active = false;
return index_def;
}
@@ -861,7 +877,8 @@ 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;
+ alter->new_space->sequence_fieldno = alter->old_space->sequence_fieldno;
+ alter->new_space->sequence_path = alter->old_space->sequence_path;
memcpy(alter->new_space->access, alter->old_space->access,
sizeof(alter->old_space->access));
@@ -3340,12 +3357,6 @@ 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);
@@ -3378,21 +3389,58 @@ 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, sequence_part,
+
+ /* Sequence field was added in 2.2.1. */
+ uint32_t sequence_fieldno;
+ const char *sequence_path_raw;
+ uint32_t sequence_path_len;
+ if (tuple_field_count(tuple) > BOX_SPACE_SEQUENCE_FIELD_FIELDNO) {
+ sequence_fieldno = tuple_field_u32_xc(tuple,
+ BOX_SPACE_SEQUENCE_FIELD_FIELDNO);
+ sequence_path_raw = tuple_field_str_xc(tuple,
+ BOX_SPACE_SEQUENCE_FIELD_PATH,
+ &sequence_path_len);
+ } else {
+ struct key_part *part = &pk->def->key_def->parts[0];
+ sequence_fieldno = part->fieldno;
+ sequence_path_raw = part->path;
+ sequence_path_len = part->path_len;
+ }
+ char *sequence_path = NULL;
+ if (sequence_path_len > 0) {
+ sequence_path = (char *)malloc(sequence_path_len + 1);
+ if (sequence_path == NULL)
+ tnt_raise(OutOfMemory, sequence_path_len + 1,
+ "malloc", "sequence path");
+ memcpy(sequence_path, sequence_path_raw,
+ sequence_path_len);
+ sequence_path[sequence_path_len] = 0;
+ }
+ auto sequence_path_guard = make_scoped_guard([=] {
+ free(sequence_path);
+ });
+
+ index_def_check_sequence(pk->def, sequence_fieldno,
+ sequence_path,
space_name(space));
- if (seq->is_generated && seq != space->sequence) {
+ if (seq->is_generated) {
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;
+ space->sequence_fieldno = sequence_fieldno;
+ free(space->sequence_path);
+ space->sequence_path = sequence_path;
+ sequence_path_guard.is_active = false;
} else { /* DELETE */
assert(space->sequence == seq);
- assert(space->sequence_part == sequence_part);
+ seq->is_generated = false;
space->sequence = NULL;
- space->sequence_part = 0;
+ space->sequence_fieldno = 0;
+ free(space->sequence_path);
+ space->sequence_path = NULL;
}
}
diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8c3b10e8417494edba07e6eba78377226ee5a31a..bb8fbeba114b1e72a5585548fb7f22796931d90f 100644
GIT binary patch
literal 4393
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
literal 4379
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
V;^=W8u!k^X`7N3e^#Root?kdRMV<fv
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index dcaa1365..39bd8da6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -593,7 +593,7 @@ end
-- field 1-based index or full JSON path. A particular case of a
-- full JSON path is the format field name.
--
-local function format_field_resolve(format, path, part_idx)
+local function format_field_resolve(format, path, what)
assert(type(path) == 'number' or type(path) == 'string')
local idx = nil
local relative_path = nil
@@ -636,13 +636,12 @@ local function format_field_resolve(format, path, part_idx)
end
-- Can't resolve field index by path.
assert(idx == nil)
- box.error(box.error.ILLEGAL_PARAMS, "options.parts[" .. part_idx .. "]: " ..
+ box.error(box.error.ILLEGAL_PARAMS, what .. ": " ..
"field was not found by name '" .. path .. "'")
::done::
if idx <= 0 then
- box.error(box.error.ILLEGAL_PARAMS,
- "options.parts[" .. part_idx .. "]: " ..
+ box.error(box.error.ILLEGAL_PARAMS, what .. ": " ..
"field (number) must be one-based")
end
return idx - 1, relative_path
@@ -696,7 +695,8 @@ local function update_index_parts(format, parts)
end
end
if type(part.field) == 'number' or type(part.field) == 'string' then
- local idx, path = format_field_resolve(format, part.field, i)
+ local idx, path = format_field_resolve(format, part.field,
+ "options.parts[" .. i .. "]")
part.field = idx
part.path = path or part.path
parts_can_be_simplified = parts_can_be_simplified and part.path == nil
@@ -749,20 +749,196 @@ 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")
+--
+-- Raise an error if a sequence isn't compatible with a given
+-- index definition.
+--
+local function space_sequence_check(sequence, parts, space_name, index_name)
+ local sequence_part = nil
+ if sequence.field ~= nil then
+ sequence.path = sequence.path or ''
+ -- Look up the index part corresponding to the given field.
+ for _, part in ipairs(parts) do
+ local field = part.field or part[1]
+ local path = part.path or ''
+ if sequence.field == field and sequence.path == path then
+ sequence_part = part
+ break
+ end
+ end
+ if sequence_part == nil then
+ box.error(box.error.MODIFY_INDEX, index_name, space_name,
+ "sequence field must be a part of the index")
+ end
+ else
+ -- If the sequence field is omitted, use the first
+ -- indexed field.
+ sequence_part = parts[1]
+ sequence.field = sequence_part.field or sequence_part[1]
+ sequence.path = sequence_part.path or ''
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
+ -- Check the type of the auto-increment field.
+ local t = sequence_part.type or sequence_part[2]
+ if t ~= 'integer' and t ~= 'unsigned' then
box.error(box.error.MODIFY_INDEX, index_name, space_name,
"sequence cannot be used with a non-integer key")
end
end
+--
+-- The first stage of a space sequence modification operation.
+-- Called before altering the space definition. Checks sequence
+-- options and detaches the old sequence from the space.
+-- Returns a proxy object that is supposed to be passed to
+-- space_sequence_alter_commit() to complete the operation.
+--
+local function space_sequence_alter_prepare(format, parts, options,
+ space_id, index_id,
+ space_name, index_name)
+ local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
+
+ -- A sequence can only be attached to a primary index.
+ if index_id ~= 0 then
+ -- Ignore 'sequence = false' for secondary indexes.
+ if not options.sequence then
+ return nil
+ end
+ box.error(box.error.MODIFY_INDEX, index_name, space_name,
+ "sequence cannot be used with a secondary key")
+ end
+
+ -- Look up the currently attached sequence, if any.
+ local old_sequence
+ local tuple = _space_sequence:get(space_id)
+ if tuple ~= nil then
+ old_sequence = {
+ id = tuple.sequence_id,
+ is_generated = tuple.is_generated,
+ field = tuple.field,
+ path = tuple.path,
+ }
+ else
+ old_sequence = nil
+ end
+
+ if options.sequence == nil then
+ -- No sequence option, just check that the old sequence
+ -- is compatible with the new index definition.
+ if old_sequence ~= nil and old_sequence.field ~= nil then
+ space_sequence_check(old_sequence, parts, space_name, index_name)
+ end
+ return nil
+ end
+
+ -- Convert the provided option to the table format.
+ local new_sequence
+ if type(options.sequence) == 'table' then
+ -- Sequence is given as a table, just copy it.
+ -- Silently ignore unknown fields.
+ new_sequence = {
+ id = options.sequence.id,
+ field = options.sequence.field,
+ }
+ elseif options.sequence == true then
+ -- Create an auto-generated sequence.
+ new_sequence = {}
+ elseif options.sequence == false then
+ -- Drop the currently attached sequence.
+ new_sequence = nil
+ else
+ -- Attach a sequence with the given id.
+ new_sequence = {id = options.sequence}
+ end
+
+ if new_sequence ~= nil then
+ -- Resolve the sequence name.
+ if new_sequence.id ~= nil then
+ local id = sequence_resolve(new_sequence.id)
+ if id == nil then
+ box.error(box.error.NO_SUCH_SEQUENCE, new_sequence.id)
+ end
+ local tuple = _space_sequence.index.sequence:select(id)[1]
+ if tuple ~= nil and tuple.is_generated then
+ box.error(box.error.ALTER_SPACE, space_name,
+ "can not attach generated sequence")
+ end
+ new_sequence.id = id
+ end
+ -- Resolve the sequence field.
+ if new_sequence.field ~= nil then
+ local field, path = format_field_resolve(format, new_sequence.field,
+ "sequence field")
+ new_sequence.field = field
+ new_sequence.path = path
+ end
+ -- Inherit omitted options from the attached sequence.
+ if old_sequence ~= nil then
+ if new_sequence.id == nil and old_sequence.is_generated then
+ new_sequence.id = old_sequence.id
+ new_sequence.is_generated = true
+ end
+ if new_sequence.field == nil then
+ new_sequence.field = old_sequence.field
+ new_sequence.path = old_sequence.path
+ end
+ end
+ -- Check that the sequence is compatible with
+ -- the index definition.
+ space_sequence_check(new_sequence, parts, space_name, index_name)
+ -- If sequence id is omitted, we are supposed to create
+ -- a new auto-generated sequence for the given space.
+ if new_sequence.id == nil then
+ local seq = box.schema.sequence.create(space_name .. '_seq')
+ new_sequence.id = seq.id
+ new_sequence.is_generated = true
+ end
+ new_sequence.is_generated = new_sequence.is_generated or false
+ end
+
+ if old_sequence ~= nil then
+ -- Detach the old sequence before altering the space.
+ _space_sequence:delete(space_id)
+ end
+
+ return {
+ space_id = space_id,
+ new_sequence = new_sequence,
+ old_sequence = old_sequence,
+ }
+end
+
+--
+-- The second stage of a space sequence modification operation.
+-- Called after altering the space definition. Attaches the sequence
+-- to the space and drops the old sequence if required. 'proxy' is
+-- an object returned by space_sequence_alter_prepare().
+--
+local function space_sequence_alter_commit(proxy)
+ local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
+
+ if proxy == nil then
+ -- No sequence option, nothing to do.
+ return
+ end
+
+ local space_id = proxy.space_id
+ local old_sequence = proxy.old_sequence
+ local new_sequence = proxy.new_sequence
+
+ if new_sequence ~= nil then
+ -- Attach the new sequence.
+ _space_sequence:insert{space_id, new_sequence.id,
+ new_sequence.is_generated,
+ new_sequence.field, new_sequence.path}
+ end
+
+ if old_sequence ~= nil and old_sequence.is_generated and
+ (new_sequence == nil or old_sequence.id ~= new_sequence.id) then
+ -- Drop automatically generated sequence.
+ box.schema.sequence.drop(old_sequence.id)
+ end
+end
+
-- Historically, some properties of an index
-- are stored as tuple fields, others in a
-- single field containing msgpack map.
@@ -787,8 +963,7 @@ local alter_index_template = {
name = 'string',
type = 'string',
parts = 'table',
- sequence = 'boolean, number, string',
- sequence_part = 'number',
+ sequence = 'boolean, number, string, table',
}
for k, v in pairs(index_options) do
alter_index_template[k] = v
@@ -898,41 +1073,15 @@ box.schema.index.create = function(space_id, name, options)
"please use '%s' instead", field_type, part.type)
end
end
- 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
- 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
- sequence_is_generated = true
- else
- sequence = sequence_resolve(sequence)
- if sequence == nil then
- box.error(box.error.NO_SUCH_SEQUENCE, options.sequence)
- end
- end
- end
-- save parts in old format if possible
if parts_can_be_simplified then
parts = simplify_index_parts(parts)
end
+ local sequence_proxy = space_sequence_alter_prepare(format, parts, options,
+ space_id, iid,
+ space.name, name)
_index:insert{space_id, iid, name, options.type, index_opts, parts}
- if sequence ~= nil then
- _space_sequence:insert{space_id, sequence, sequence_is_generated,
- sequence_part - 1}
- end
+ space_sequence_alter_commit(sequence_proxy)
return space.index[name]
end
@@ -1044,71 +1193,12 @@ box.schema.index.alter = function(space_id, index_id, options)
parts = simplify_index_parts(parts)
end
end
- 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 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
- end
- if sequence ~= nil or sequence_part ~= nil then
- sequence_tuple = _space_sequence:get(space_id)
- 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
- else
- -- Space already has an automatically generated sequence.
- sequence = sequence_tuple.sequence_id
- end
- sequence_is_generated = true
- elseif sequence then
- sequence = sequence_resolve(sequence)
- if sequence == nil then
- box.error(box.error.NO_SUCH_SEQUENCE, options.sequence)
- end
- end
- if sequence == false then
- _space_sequence:delete(space_id)
- end
+ local sequence_proxy = space_sequence_alter_prepare(format, parts, options,
+ space_id, index_id,
+ space.name, options.name)
_index:replace{space_id, index_id, options.name, options.type,
index_opts, parts}
- 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
- sequence_tuple.sequence_id ~= sequence then
- -- Delete automatically generated sequence.
- box.schema.sequence.drop(sequence_tuple.sequence_id)
- end
+ space_sequence_alter_commit(sequence_proxy)
end
-- a static box_tuple_t ** instance for calling box_index_* API
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index e342bfcc..87adaeb1 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -309,9 +309,17 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
*/
lua_rawset(L, -3);
- lua_pushstring(L, "sequence_part");
+ lua_pushstring(L, "sequence_fieldno");
if (k == 0 && space->sequence != NULL)
- lua_pushnumber(L, space->sequence_part + 1);
+ lua_pushnumber(L, space->sequence_fieldno +
+ TUPLE_INDEX_BASE);
+ else
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+
+ lua_pushstring(L, "sequence_path");
+ if (k == 0 && space->sequence_path != NULL)
+ lua_pushstring(L, space->sequence_path);
else
lua_pushnil(L);
lua_rawset(L, -3);
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 23f4df01..07066269 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -749,17 +749,28 @@ end
-- Tarantool 2.2.1
--------------------------------------------------------------------------------
--- Add sequence part field to _space_sequence table
+-- Add sequence field to _space_sequence table
local function upgrade_sequence_to_2_2_1()
- log.info("add key part field to space _space_sequence")
+ log.info("add sequence field to space _space_sequence")
+ local _index = box.space[box.schema.INDEX_ID]
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}})
+ if #v > 3 then
+ -- Must be a sequence created after upgrade.
+ -- It doesn't need to get updated.
+ goto continue
end
+ -- Explicitly attach the sequence to the first index part.
+ local pk = _index:get{v[1], 0}
+ local part = pk[6][1]
+ local field = part.field or part[1]
+ local path = part.path or ''
+ _space_sequence:update(v[1], {{'!', 4, field}, {'!', 5, path}})
+ ::continue::
end
local format = _space_sequence:format()
- format[4] = {name = 'part', type = 'unsigned'}
+ format[4] = {name = 'field', type = 'unsigned'}
+ format[5] = {name = 'path', type = 'string'}
_space_sequence:format(format)
end
diff --git a/src/box/request.c b/src/box/request.c
index e2a98fdf..82232a15 100644
--- a/src/box/request.c
+++ b/src/box/request.c
@@ -158,13 +158,14 @@ request_handle_sequence(struct request *request, struct space *space)
return 0;
/*
- * Look up the first field of the primary key.
+ * Look up the auto-increment field.
*/
+ int fieldno = space->sequence_fieldno;
+ const char *path = space->sequence_path;
+
const char *data = request->tuple;
const char *data_end = request->tuple_end;
int len = mp_decode_array(&data);
- struct key_part *part = &pk->def->key_def->parts[space->sequence_part];
- int fieldno = part->fieldno;
if (unlikely(len < fieldno + 1))
return 0;
@@ -175,9 +176,8 @@ request_handle_sequence(struct request *request, struct space *space)
} while (--fieldno > 0);
}
- if (part->path != NULL) {
- tuple_go_to_path(&key, part->path, part->path_len,
- MULTIKEY_NONE);
+ if (path != NULL) {
+ tuple_go_to_path(&key, path, strlen(path), MULTIKEY_NONE);
if (key == NULL)
return 0; /* field not found */
}
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
index dea3fad1..b817b49f 100644
--- a/src/box/schema_def.h
+++ b/src/box/schema_def.h
@@ -216,7 +216,8 @@ 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,
+ BOX_SPACE_SEQUENCE_FIELD_FIELDNO = 3,
+ BOX_SPACE_SEQUENCE_FIELD_PATH = 4,
};
/** _trigger fields. */
diff --git a/src/box/space.h b/src/box/space.h
index c3eef71c..be1178b9 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -176,11 +176,10 @@ 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;
+ /** Auto increment field number. */
+ uint32_t sequence_fieldno;
+ /** Path to data in the auto-increment field. */
+ char *sequence_path;
/** Enable/disable triggers. */
bool run_triggers;
/**
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 91b977de..e2353d8c 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -939,15 +939,26 @@ emitNewSysSequenceRecord(Parse *pParse, int reg_seq_id, const char *seq_name)
return first_col;
}
-int
-emitNewSysSpaceSequenceRecord(Parse *pParse, int space_id, const char reg_seq_id)
+static int
+emitNewSysSpaceSequenceRecord(Parse *pParse, int reg_space_id, int reg_seq_id,
+ struct index_def *idx_def)
{
+ struct key_part *part = &idx_def->key_def->parts[0];
+ int fieldno = part->fieldno;
+ char *path = NULL;
+ if (part->path != NULL) {
+ path = sqlDbStrNDup(pParse->db, part->path, part->path_len);
+ if (path == NULL)
+ return -1;
+ path[part->path_len] = 0;
+ }
+
Vdbe *v = sqlGetVdbe(pParse);
int first_col = pParse->nMem + 1;
- pParse->nMem += 4; /* 3 fields + new record pointer */
+ pParse->nMem += 6; /* 5 fields + new record pointer */
/* 1. Space id */
- sqlVdbeAddOp2(v, OP_SCopy, space_id, first_col + 1);
+ sqlVdbeAddOp2(v, OP_SCopy, reg_space_id, first_col + 1);
/* 2. Sequence id */
sqlVdbeAddOp2(v, OP_IntCopy, reg_seq_id, first_col + 2);
@@ -955,10 +966,15 @@ emitNewSysSpaceSequenceRecord(Parse *pParse, int space_id, const char reg_seq_id
/* 3. Autogenerated. */
sqlVdbeAddOp2(v, OP_Bool, true, first_col + 3);
- /* 4. Part id. */
- sqlVdbeAddOp2(v, OP_Integer, 0, first_col + 4);
+ /* 4. Field id. */
+ sqlVdbeAddOp2(v, OP_Integer, fieldno, first_col + 4);
- sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 4, first_col);
+ /* 5. Field path. */
+ sqlVdbeAddOp4(v, OP_String8, 0, first_col + 5, 0,
+ path != NULL ? path : "",
+ path != NULL ? P4_DYNAMIC : P4_STATIC );
+
+ sqlVdbeAddOp3(v, OP_MakeRecord, first_col + 1, 5, first_col);
return first_col;
}
@@ -1173,9 +1189,9 @@ sqlEndTable(struct Parse *pParse)
save_record(pParse, BOX_SEQUENCE_ID, reg_seq_record + 1, 1,
v->nOp - 1);
/* Do an insertion into _space_sequence. */
- int reg_space_seq_record =
- emitNewSysSpaceSequenceRecord(pParse, reg_space_id,
- reg_seq_id);
+ int reg_space_seq_record = emitNewSysSpaceSequenceRecord(pParse,
+ reg_space_id, reg_seq_id,
+ new_space->index[0]->def);
sqlVdbeAddOp3(v, OP_SInsert, BOX_SPACE_SEQUENCE_ID, 0,
reg_space_seq_record);
save_record(pParse, BOX_SPACE_SEQUENCE_ID,
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 2813415b..f84507e5 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -82,11 +82,9 @@ static uint32_t
sql_space_autoinc_fieldno(struct space *space)
{
assert(space != NULL);
- struct index *pk = space_index(space, 0);
- if (pk == NULL || pk->def->key_def->part_count != 1 ||
- space->sequence == NULL)
+ if (space->sequence == NULL)
return UINT32_MAX;
- return pk->def->key_def->parts[space->sequence_part].fieldno;
+ return space->sequence_fieldno;
}
/**
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index de90beee..0684914c 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -73,7 +73,7 @@ box.space._space:select{}
'type': 'unsigned'}]]
- [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
{'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'},
- {'name': 'part', 'type': 'unsigned'}]]
+ {'name': 'field', 'type': 'unsigned'}, {'name': 'path', 'type': 'string'}]]
- [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.result b/test/box/access.result
index 9c190240..44a74e17 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -610,6 +610,9 @@ box.schema.user.grant('twostep', 'write', 'space', '_space')
box.schema.user.grant('twostep', 'write', 'space', '_index')
---
...
+box.schema.user.grant('twostep', 'read', 'space', '_space_sequence')
+---
+...
box.schema.user.grant('twostep', 'read,write', 'space', '_func')
---
...
@@ -1481,6 +1484,9 @@ box.schema.user.grant('tester', 'create' , 'sequence')
box.schema.user.grant('tester', 'read', 'space', '_sequence')
---
...
+box.schema.user.grant('tester', 'read', 'space', '_space_sequence')
+---
+...
box.schema.user.grant('tester', 'read', 'space', '_trigger')
---
...
@@ -1922,6 +1928,9 @@ box.schema.user.grant("test", "read", "space", "space1")
box.schema.user.grant("test", "write", "space", "_index")
---
...
+box.schema.user.grant("test", "read", "space", "_space_sequence")
+---
+...
box.session.su("test")
---
...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 4baeb2ef..ee408f53 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -250,6 +250,7 @@ box.schema.user.grant('twostep', 'create', 'function')
box.schema.user.grant('twostep', 'write', 'space', '_schema')
box.schema.user.grant('twostep', 'write', 'space', '_space')
box.schema.user.grant('twostep', 'write', 'space', '_index')
+box.schema.user.grant('twostep', 'read', 'space', '_space_sequence')
box.schema.user.grant('twostep', 'read,write', 'space', '_func')
box.session.su('twostep')
twostep = box.schema.space.create('twostep')
@@ -552,6 +553,7 @@ box.schema.user.grant('tester', 'create', 'space')
box.schema.user.grant('tester', 'create', 'function')
box.schema.user.grant('tester', 'create' , 'sequence')
box.schema.user.grant('tester', 'read', 'space', '_sequence')
+box.schema.user.grant('tester', 'read', 'space', '_space_sequence')
box.schema.user.grant('tester', 'read', 'space', '_trigger')
box.schema.user.grant('tester', 'read', 'space', '_fk_constraint')
box.session.su("tester")
@@ -746,6 +748,7 @@ box.schema.user.create("test")
_ = box.schema.space.create("space1")
box.schema.user.grant("test", "read", "space", "space1")
box.schema.user.grant("test", "write", "space", "_index")
+box.schema.user.grant("test", "read", "space", "_space_sequence")
box.session.su("test")
box.space.space1:create_index("pk")
box.session.su("admin")
diff --git a/test/box/access_misc.result b/test/box/access_misc.result
index 877a9b53..24bdd9d6 100644
--- a/test/box/access_misc.result
+++ b/test/box/access_misc.result
@@ -813,7 +813,7 @@ box.space._space:select()
'type': 'unsigned'}]]
- [340, 1, '_space_sequence', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'},
{'name': 'sequence_id', 'type': 'unsigned'}, {'name': 'is_generated', 'type': 'boolean'},
- {'name': 'part', 'type': 'unsigned'}]]
+ {'name': 'field', 'type': 'unsigned'}, {'name': 'path', 'type': 'string'}]]
- [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/alter.result b/test/box/alter.result
index 75d6dae2..e83c0b7e 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -674,10 +674,10 @@ s.index.pk
- type: unsigned
is_nullable: false
fieldno: 2
+ type: TREE
id: 0
space_id: 731
name: pk
- type: TREE
...
s:select{}
---
@@ -704,10 +704,10 @@ s.index.pk
- type: unsigned
is_nullable: false
fieldno: 1
- id: 0
space_id: 731
- name: pk
+ id: 0
type: TREE
+ name: pk
...
s.index.secondary
---
@@ -716,10 +716,10 @@ s.index.secondary
- type: unsigned
is_nullable: false
fieldno: 2
+ type: TREE
id: 1
space_id: 731
name: secondary
- type: TREE
...
s.index.secondary:select{}
---
diff --git a/test/box/sequence.result b/test/box/sequence.result
index 3cac4dc9..990d15db 100644
--- a/test/box/sequence.result
+++ b/test/box/sequence.result
@@ -590,17 +590,20 @@ 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
+s:create_index('pk', {sequence = {id = 'no_such_sequence'}}) -- error
---
-- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot
- be used without sequence'
+- error: Sequence 'no_such_sequence' does not exist
...
-s:create_index('pk', {sequence = true, sequence_part = 2}) -- error
+s:create_index('pk', {sequence = {field = 2}}) -- error
---
-- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part is
- out of bounds'
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence field must
+ be a part of the index'
...
-s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, sequence_part = 2}) -- error
+s:create_index('pk', {sequence = {field = 'no.such.field'}}) -- error
+---
+- error: 'Illegal parameters, sequence field: field was not found by name ''no.such.field'''
+...
+s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = {field = 2}}) -- error
---
- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
be used with a non-integer key'
@@ -608,23 +611,33 @@ s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = true, seq
pk = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned'}}) -- ok
---
...
-pk:alter{sequence_part = 1} -- error
+pk:alter{sequence = {id = 'no_such_sequence', field = 2}} -- error
---
-- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot
- be used without sequence'
+- error: Sequence 'no_such_sequence' does not exist
...
-pk:alter{sequence = true, sequence_part = 1} -- error
+pk:alter{sequence = {id = 'test', field = 2}} -- ok
+---
+...
+pk:alter{sequence = {id = 'test', field = 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 = false} -- ok
+---
+...
+pk:alter{sequence = {field = 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 = {field = 2}} -- ok
---
...
-pk:alter{sequence = false, sequence_part = 2} -- error
+pk:alter{sequence = {field = 1}} -- error
---
-- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence part cannot
- be used without sequence'
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence cannot
+ be used with a non-integer key'
...
pk:alter{sequence = false} -- ok
---
@@ -657,10 +670,10 @@ 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
+s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = {field = 2}}) -- error
---
-- error: 'Can''t create or modify index ''nil'' in space ''test'': sequence part cannot
- be used without sequence'
+- error: 'Can''t create or modify index ''secondary'' in space ''test'': sequence
+ cannot be used with a secondary key'
...
sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok
---
@@ -747,15 +760,15 @@ 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, 0} -- 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
+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'
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence field must
+ be a part of the index'
...
sk:drop()
---
@@ -763,14 +776,15 @@ sk:drop()
pk:drop()
---
...
-box.space._space_sequence:insert{s.id, sq.id, false, 0} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0, ''} -- error
---
- error: 'No index #0 is defined in space ''test'''
...
-s:create_index('pk', {sequence = {}}) -- error
+pk = s:create_index('pk', {sequence = {}}) -- ok
+---
+...
+pk:drop()
---
-- error: 'Illegal parameters, options parameter ''sequence'' should be one of types:
- boolean, number, string'
...
s:create_index('pk', {sequence = 'abc'}) -- error
---
@@ -807,10 +821,8 @@ s.index.pk.sequence_id == nil
---
- true
...
-pk:alter{sequence = {}} -- error
+pk:alter{sequence = {}} -- ok
---
-- error: 'Illegal parameters, options parameter ''sequence'' should be one of types:
- boolean, number, string'
...
pk:alter{sequence = 'abc'} -- error
---
@@ -1169,11 +1181,14 @@ _ = s1:create_index('pk', {sequence = true})
s2 = box.schema.space.create('test2')
---
...
-_ = s2:create_index('pk', {sequence = 'test1_seq'}) -- error
+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, 0} -- error
+_ = s2:create_index('pk')
+---
+...
+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'
...
@@ -1664,15 +1679,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, 0} -- 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, 0} -- 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, 0} -- error
+box.space._space_sequence:replace{s2.id, sq1.id, false, 0, ''} -- error
---
- error: Read access to sequence 'seq1' is denied for user 'user'
...
@@ -1962,7 +1977,7 @@ s:drop()
s = box.schema.space.create('test')
---
...
-_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = true, sequence_part = 2})
+_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = {field = 2}})
---
...
sequence_id = s.index.pk.sequence_id
@@ -1972,9 +1987,9 @@ sequence_id ~= nil
---
- true
...
-s.index.pk.sequence_part == 2
+s.index.pk.sequence_fieldno -- 2
---
-- true
+- 2
...
s:insert{'a', box.NULL, 1}
---
@@ -1992,12 +2007,12 @@ s:insert{'b', box.NULL, 11}
---
- ['b', 11, 11]
...
-s.index.pk:alter{sequence_part = 3}
+s.index.pk:alter{sequence = {field = 3}}
---
...
-s.index.pk.sequence_part == 3
+s.index.pk.sequence_fieldno -- 3
---
-- true
+- 3
...
s.index.pk.sequence_id == sequence_id
---
@@ -2011,12 +2026,12 @@ s:insert{'c', 101, box.NULL}
---
- ['c', 101, 101]
...
-s.index.pk:alter{sequence = true, sequence_part = 2}
+s.index.pk:alter{sequence = {field = 2}}
---
...
-s.index.pk.sequence_part == 2
+s.index.pk.sequence_fieldno -- 2
---
-- true
+- 2
...
s.index.pk.sequence_id == sequence_id
---
@@ -2039,9 +2054,20 @@ s:drop()
s = box.schema.space.create('test')
---
...
-_ = s:create_index('pk', {parts = {{'[1].a.b[1]', 'unsigned'}}, sequence = true})
+s:format{{'x', 'map'}}
---
...
+_ = s:create_index('pk', {parts = {{'x.a.b[1]', 'unsigned'}}, sequence = {field = 'x.a.b[1]'}})
+---
+...
+s.index.pk.sequence_fieldno -- 1
+---
+- 1
+...
+s.index.pk.sequence_path -- .a.b[1]
+---
+- .a.b[1]
+...
s:replace{} -- error
---
- error: Tuple field [1]["a"]["b"][1] required by space format is missing
@@ -2062,6 +2088,68 @@ s:replace{{a = {b = {box.NULL}}}} -- ok
---
- [{'a': {'b': [1]}}]
...
+s.index.pk:alter{sequence = false}
+---
+...
+s.index.pk:alter{sequence = {field = 'x.a.b[1]'}}
+---
+...
+s:replace{{a = {b = {box.NULL}}}} -- ok
+---
+- [{'a': {'b': [1]}}]
+...
+s:drop()
+---
+...
+--
+-- Check that altering parts of a primary index with a sequence
+-- attached requires sequence update. Renaming fields does not.
+--
+s = box.schema.space.create('test')
+---
+...
+s:format({{'x', 'map'}})
+---
+...
+pk = s:create_index('pk', {parts = {{'x.a', 'unsigned'}}})
+---
+...
+pk:alter{sequence = true} -- ok
+---
+...
+s:insert{{a = box.NULL, b = 1}}
+---
+- [{'a': 1, 'b': 1}]
+...
+s:format{{'y', 'map'}} -- ok
+---
+...
+s:insert{{a = box.NULL, b = 2}}
+---
+- [{'a': 2, 'b': 2}]
+...
+pk:alter{sequence = {field = 'y.b'}} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence field must
+ be a part of the index'
+...
+pk:alter{parts = {{'y.b', 'unsigned'}}} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence field must
+ be a part of the index'
+...
+pk:alter{parts = {{'y.b', 'unsigned'}}, sequence = {field = 'y.a'}} -- error
+---
+- error: 'Can''t create or modify index ''pk'' in space ''test'': sequence field must
+ be a part of the index'
+...
+pk:alter{parts = {{'y.b', 'unsigned'}}, sequence = {field = 'y.b'}} -- ok
+---
+...
+s:insert{{a = 3, b = box.NULL}}
+---
+- [{'a': 3, 'b': 3}]
+...
s:drop()
---
...
diff --git a/test/box/sequence.test.lua b/test/box/sequence.test.lua
index c39f1d41..3375572e 100644
--- a/test/box/sequence.test.lua
+++ b/test/box/sequence.test.lua
@@ -196,15 +196,19 @@ 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
+s:create_index('pk', {sequence = {id = 'no_such_sequence'}}) -- error
+s:create_index('pk', {sequence = {field = 2}}) -- error
+s:create_index('pk', {sequence = {field = 'no.such.field'}}) -- error
+s:create_index('pk', {parts = {1, 'unsigned', 2, 'string'}, sequence = {field = 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 = {id = 'no_such_sequence', field = 2}} -- error
+pk:alter{sequence = {id = 'test', field = 2}} -- ok
+pk:alter{sequence = {id = 'test', field = 1}} -- error
+pk:alter{sequence = false} -- ok
+pk:alter{sequence = {field = 1}} -- error
+pk:alter{sequence = {field = 2}} -- ok
+pk:alter{sequence = {field = 1}} -- error
pk:alter{sequence = false} -- ok
pk:drop()
@@ -216,7 +220,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
+s:create_index('secondary', {parts = {2, 'unsigned'}, sequence = {field = 2}}) -- error
sk = s:create_index('secondary', {parts = {2, 'unsigned'}}) -- ok
sk:alter{sequence = 'test'} -- error
sk:alter{sequence = true} -- error
@@ -240,13 +244,14 @@ 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, 0} -- error
-box.space._space_sequence:insert{s.id, sq.id, false, 2} -- 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, 0} -- error
+box.space._space_sequence:insert{s.id, sq.id, false, 0, ''} -- error
-s:create_index('pk', {sequence = {}}) -- error
+pk = s:create_index('pk', {sequence = {}}) -- ok
+pk:drop()
s:create_index('pk', {sequence = 'abc'}) -- error
s:create_index('pk', {sequence = 12345}) -- error
pk = s:create_index('pk', {sequence = 'test'}) -- ok
@@ -257,7 +262,7 @@ s.index.pk.sequence_id == sq.id
pk:drop()
pk = s:create_index('pk', {sequence = false}) -- ok
s.index.pk.sequence_id == nil
-pk:alter{sequence = {}} -- error
+pk:alter{sequence = {}} -- ok
pk:alter{sequence = 'abc'} -- error
pk:alter{sequence = 12345} -- error
pk:alter{sequence = 'test'} -- ok
@@ -371,8 +376,9 @@ sq:drop()
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, 0} -- error
+s2:create_index('pk', {sequence = 'test1_seq'}) -- error
+_ = s2:create_index('pk')
+box.space._space_sequence:insert{s2.id, box.sequence.test1_seq.id, false, 0, ''} -- error
s1:drop()
s2:drop()
@@ -552,9 +558,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, 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
+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')
@@ -666,21 +672,21 @@ 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})
+_ = s:create_index('pk', {parts = {1, 'string', 2, 'unsigned', 3, 'unsigned'}, sequence = {field = 2}})
sequence_id = s.index.pk.sequence_id
sequence_id ~= nil
-s.index.pk.sequence_part == 2
+s.index.pk.sequence_fieldno -- 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:alter{sequence = {field = 3}}
+s.index.pk.sequence_fieldno -- 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:alter{sequence = {field = 2}}
+s.index.pk.sequence_fieldno -- 2
s.index.pk.sequence_id == sequence_id
s:insert{'d', 1000, 1000}
s:insert{'d', box.NULL, 1001}
@@ -690,10 +696,34 @@ s:drop()
-- gh-4210: using sequence with a json path key part.
--
s = box.schema.space.create('test')
-_ = s:create_index('pk', {parts = {{'[1].a.b[1]', 'unsigned'}}, sequence = true})
+s:format{{'x', 'map'}}
+_ = s:create_index('pk', {parts = {{'x.a.b[1]', 'unsigned'}}, sequence = {field = 'x.a.b[1]'}})
+s.index.pk.sequence_fieldno -- 1
+s.index.pk.sequence_path -- .a.b[1]
s:replace{} -- error
s:replace{{c = {}}} -- error
s:replace{{a = {c = {}}}} -- error
s:replace{{a = {b = {}}}} -- error
s:replace{{a = {b = {box.NULL}}}} -- ok
+s.index.pk:alter{sequence = false}
+s.index.pk:alter{sequence = {field = 'x.a.b[1]'}}
+s:replace{{a = {b = {box.NULL}}}} -- ok
+s:drop()
+
+--
+-- Check that altering parts of a primary index with a sequence
+-- attached requires sequence update. Renaming fields does not.
+--
+s = box.schema.space.create('test')
+s:format({{'x', 'map'}})
+pk = s:create_index('pk', {parts = {{'x.a', 'unsigned'}}})
+pk:alter{sequence = true} -- ok
+s:insert{{a = box.NULL, b = 1}}
+s:format{{'y', 'map'}} -- ok
+s:insert{{a = box.NULL, b = 2}}
+pk:alter{sequence = {field = 'y.b'}} -- error
+pk:alter{parts = {{'y.b', 'unsigned'}}} -- error
+pk:alter{parts = {{'y.b', 'unsigned'}}, sequence = {field = 'y.a'}} -- error
+pk:alter{parts = {{'y.b', 'unsigned'}}, sequence = {field = 'y.b'}} -- ok
+s:insert{{a = 3, b = box.NULL}}
s:drop()
diff --git a/test/box/stat.result b/test/box/stat.result
index 757ec0bc..55f29fe5 100644
--- a/test/box/stat.result
+++ b/test/box/stat.result
@@ -59,7 +59,7 @@ box.stat.REPLACE.total
...
box.stat.SELECT.total
---
-- 4
+- 5
...
-- check exceptions
space:get('Impossible value')
@@ -77,22 +77,22 @@ space:get(1)
...
box.stat.SELECT.total
---
-- 5
-...
-space:get(11)
----
-...
-box.stat.SELECT.total
----
- 6
...
+space:get(11)
+---
+...
+box.stat.SELECT.total
+---
+- 7
+...
space:select(5)
---
- - [5, 'tuple5']
...
box.stat.SELECT.total
---
-- 7
+- 8
...
space:select(15)
---
@@ -100,15 +100,15 @@ space:select(15)
...
box.stat.SELECT.total
---
-- 8
-...
-for _ in space:pairs() do end
----
-...
-box.stat.SELECT.total
----
- 9
...
+for _ in space:pairs() do end
+---
+...
+box.stat.SELECT.total
+---
+- 10
+...
-- reset
box.stat.reset()
---
diff --git a/test/replication/autobootstrap.result b/test/replication/autobootstrap.result
index 7b770a5e..743982d4 100644
--- a/test/replication/autobootstrap.result
+++ b/test/replication/autobootstrap.result
@@ -124,6 +124,9 @@ box.schema.user.grant('test_u', 'write', 'space', '_schema')
box.schema.user.grant('test_u', 'write', 'space', '_index')
---
...
+box.schema.user.grant('test_u', 'read', 'space', '_space_sequence')
+---
+...
box.session.su('test_u')
---
...
@@ -140,6 +143,9 @@ box.space.test_u:select()
---
- - [1, 2, 3, 4]
...
+box.schema.user.revoke('test_u', 'read', 'space', '_space_sequence')
+---
+...
box.schema.user.revoke('test_u', 'write', 'space', '_index')
---
...
diff --git a/test/replication/autobootstrap.test.lua b/test/replication/autobootstrap.test.lua
index 3b1397eb..055ea427 100644
--- a/test/replication/autobootstrap.test.lua
+++ b/test/replication/autobootstrap.test.lua
@@ -59,12 +59,14 @@ box.schema.user.grant('test_u', 'create', 'space')
box.schema.user.grant('test_u', 'read,write', 'space', '_space')
box.schema.user.grant('test_u', 'write', 'space', '_schema')
box.schema.user.grant('test_u', 'write', 'space', '_index')
+box.schema.user.grant('test_u', 'read', 'space', '_space_sequence')
box.session.su('test_u')
_ = box.schema.space.create('test_u'):create_index('pk')
box.session.su('admin')
_ = box.space.test_u:replace({1, 2, 3, 4})
box.space.test_u:select()
+box.schema.user.revoke('test_u', 'read', 'space', '_space_sequence')
box.schema.user.revoke('test_u', 'write', 'space', '_index')
box.schema.user.revoke('test_u', 'write', 'space', '_schema')
box.schema.user.revoke('test_u', 'read,write', 'space', '_space')
diff --git a/test/vinyl/ddl.result b/test/vinyl/ddl.result
index 864050b3..d584708b 100644
--- a/test/vinyl/ddl.result
+++ b/test/vinyl/ddl.result
@@ -575,15 +575,15 @@ box.space.test.index.pk
- type: unsigned
is_nullable: false
fieldno: 1
- id: 0
- space_id: 512
options:
page_size: 8192
run_count_per_level: 2
run_size_ratio: 3.5
bloom_fpr: 0.05
- name: pk
+ id: 0
+ space_id: 512
type: TREE
+ name: pk
...
box.space.test:drop()
---
--
2.11.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [tarantool-patches] Re: [PATCH v2] schema: rework index sequence API
2019-05-28 14:17 [PATCH v2] schema: rework index sequence API Vladimir Davydov
@ 2019-05-28 19:07 ` Konstantin Osipov
2019-05-29 8:17 ` Vladimir Davydov
1 sibling, 0 replies; 3+ messages in thread
From: Konstantin Osipov @ 2019-05-28 19:07 UTC (permalink / raw)
To: tarantool-patches
* Vladimir Davydov <vdavydov.dev@gmail.com> [19/05/28 20:47]:
Hi,
Thank you for working on this.
LGTM.
--
Konstantin Osipov, Moscow, Russia
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2] schema: rework index sequence API
2019-05-28 14:17 [PATCH v2] schema: rework index sequence API Vladimir Davydov
2019-05-28 19:07 ` [tarantool-patches] " Konstantin Osipov
@ 2019-05-29 8:17 ` Vladimir Davydov
1 sibling, 0 replies; 3+ messages in thread
From: Vladimir Davydov @ 2019-05-29 8:17 UTC (permalink / raw)
To: tarantool-patches
Pushed to master.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2019-05-29 8:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-28 14:17 [PATCH v2] schema: rework index sequence API Vladimir Davydov
2019-05-28 19:07 ` [tarantool-patches] " Konstantin Osipov
2019-05-29 8:17 ` Vladimir Davydov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox