* [PATCH 1/4] schema: use tuple field names in Lua
2019-05-15 10:33 [PATCH 0/4] A few fixes/improvements for autoincrement indexes Vladimir Davydov
@ 2019-05-15 10:33 ` Vladimir Davydov
2019-05-15 10:33 ` [PATCH 2/4] schema: fix error while altering index with sequence Vladimir Davydov
` (3 subsequent siblings)
4 siblings, 0 replies; 17+ messages in thread
From: Vladimir Davydov @ 2019-05-15 10:33 UTC (permalink / raw)
To: tarantool-patches
When schema.lua was introduced, there was no such thing as space format
and we had to access tuple fields by no. Now we can use human readable
names. Let's do it - this should improve code readability.
A note about box/alter.test.lua: for some reason it clears format of
_space and _index system spaces, which apparently breaks our assumption
about field names. Let's zap those pointless test cases.
---
src/box/lua/schema.lua | 98 ++++++++++++++++++++++++-------------------------
test/box/alter.result | 15 --------
test/box/alter.test.lua | 6 ---
3 files changed, 49 insertions(+), 70 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index f31cf7f2..036e5f2d 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -155,7 +155,7 @@ local function user_or_role_resolve(user)
if tuple == nil then
return nil
end
- return tuple[1]
+ return tuple.id
end
local function role_resolve(name_or_id)
@@ -166,10 +166,10 @@ local function role_resolve(name_or_id)
elseif type(name_or_id) ~= 'nil' then
tuple = _vuser:get{name_or_id}
end
- if tuple == nil or tuple[4] ~= 'role' then
+ if tuple == nil or tuple.type ~= 'role' then
return nil
else
- return tuple[1]
+ return tuple.id
end
end
@@ -181,10 +181,10 @@ local function user_resolve(name_or_id)
elseif type(name_or_id) ~= 'nil' then
tuple = _vuser:get{name_or_id}
end
- if tuple == nil or tuple[4] ~= 'user' then
+ if tuple == nil or tuple.type ~= 'user' then
return nil
else
- return tuple[1]
+ return tuple.id
end
end
@@ -197,7 +197,7 @@ local function sequence_resolve(name_or_id)
tuple = _vsequence:get{name_or_id}
end
if tuple ~= nil then
- return tuple[1], tuple
+ return tuple.id, tuple
else
return nil
end
@@ -209,7 +209,7 @@ local function revoke_object_privs(object_type, object_id)
local _priv = box.space[box.schema.PRIV_ID]
local privs = _vpriv.index.object:select{object_type, object_id}
for k, tuple in pairs(privs) do
- local uid = tuple[2]
+ local uid = tuple.grantee
_priv:delete{uid, object_type, object_id}
end
end
@@ -453,13 +453,13 @@ box.schema.space.create = function(name, options)
local _schema = box.space._schema
local max_id = _schema:update({'max_id'}, {{'+', 2, 1}})
if max_id == nil then
- id = _space.index.primary:max()[1]
+ id = _space.index.primary:max().id
if id < box.schema.SYSTEM_ID_MAX then
id = box.schema.SYSTEM_ID_MAX
end
max_id = _schema:insert{'max_id', id + 1}
end
- id = max_id[2]
+ id = max_id.value
end
local uid = session.euid()
if options.user then
@@ -492,7 +492,7 @@ function box.schema.space.format(id, format)
if tuple == nil then
box.error(box.error.NO_SUCH_SPACE, '#' .. tostring(id))
end
- return tuple[7]
+ return tuple.format
else
check_param(format, 'format', 'table')
format = update_format(format)
@@ -514,9 +514,9 @@ box.schema.space.drop = function(space_id, space_name, opts)
local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
local _fk_constraint = box.space[box.schema.FK_CONSTRAINT_ID]
local sequence_tuple = _space_sequence:delete{space_id}
- if sequence_tuple ~= nil and sequence_tuple[3] == true then
+ if sequence_tuple ~= nil and sequence_tuple.is_generated == true then
-- Delete automatically generated sequence.
- box.schema.sequence.drop(sequence_tuple[2])
+ box.schema.sequence.drop(sequence_tuple.sequence_id)
end
for _, t in _trigger.index.space_id:pairs({space_id}) do
_trigger:delete({t.name})
@@ -527,7 +527,7 @@ box.schema.space.drop = function(space_id, space_name, opts)
local keys = _vindex:select(space_id)
for i = #keys, 1, -1 do
local v = keys[i]
- _index:delete{v[1], v[2]}
+ _index:delete{v.id, v.iid}
end
revoke_object_privs('space', space_id)
_truncate:delete{space_id}
@@ -847,9 +847,9 @@ box.schema.index.create = function(space_id, name, options)
local tuple = _vindex.index[0]
:select(space_id, { limit = 1, iterator = 'LE' })[1]
if tuple then
- local id = tuple[1]
+ local id = tuple.id
if id == space_id then
- iid = tuple[2] + 1
+ iid = tuple.iid + 1
end
end
end
@@ -923,9 +923,9 @@ box.schema.index.drop = function(space_id, index_id)
if index_id == 0 then
local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
local sequence_tuple = _space_sequence:delete{space_id}
- if sequence_tuple ~= nil and sequence_tuple[3] == true then
+ if sequence_tuple ~= nil and sequence_tuple.is_generated == true then
-- Delete automatically generated sequence.
- box.schema.sequence.drop(sequence_tuple[2])
+ box.schema.sequence.drop(sequence_tuple.sequence_id)
end
end
local _index = box.space[box.schema.INDEX_ID]
@@ -993,25 +993,23 @@ box.schema.index.alter = function(space_id, index_id, options)
local tuple = _index:get{space_id, index_id }
local parts = {}
local index_opts = {}
- local OPTS = 5
- local PARTS = 6
- if type(tuple[OPTS]) == 'number' then
+ if type(tuple.opts) == 'number' then
-- old format
- index_opts.unique = tuple[OPTS] == 1
- local part_count = tuple[PARTS]
+ index_opts.unique = tuple[5] == 1
+ local part_count = tuple[6]
for i = 1, part_count do
table.insert(parts, {tuple[2 * i + 4], tuple[2 * i + 5]});
end
else
-- new format
- index_opts = tuple[OPTS]
- parts = tuple[PARTS]
+ index_opts = tuple.opts
+ parts = tuple.parts
end
if options.name == nil then
- options.name = tuple[3]
+ options.name = tuple.name
end
if options.type == nil then
- options.type = tuple[4]
+ options.type = tuple.type
end
for k, t in pairs(index_options) do
if options[k] ~= nil then
@@ -1048,7 +1046,7 @@ box.schema.index.alter = function(space_id, index_id, options)
end
end
if sequence == true then
- if sequence_tuple == nil or sequence_tuple[3] == false 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
@@ -1070,10 +1068,10 @@ box.schema.index.alter = function(space_id, index_id, options)
if sequence then
_space_sequence:replace{space_id, sequence, sequence_is_generated}
end
- if sequence_tuple ~= nil and sequence_tuple[3] == true and
- sequence_tuple[2] ~= sequence then
+ if 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[2])
+ box.schema.sequence.drop(sequence_tuple.sequence_id)
end
end
@@ -1662,13 +1660,13 @@ end
local function sequence_on_alter(old_tuple, new_tuple)
if old_tuple and not new_tuple then
- local old_name = old_tuple[3]
+ local old_name = old_tuple.name
box.sequence[old_name] = nil
elseif not old_tuple and new_tuple then
local seq = sequence_new(new_tuple)
box.sequence[seq.name] = seq
else
- local old_name = old_tuple[3]
+ local old_name = old_tuple.name
local seq = box.sequence[old_name]
if not seq then
seq = sequence_new(seq, new_tuple)
@@ -1919,7 +1917,7 @@ local function object_resolve(object_type, object_name)
func = _vfunc:get{object_name}
end
if func then
- return func[1]
+ return func.id
else
box.error(box.error.NO_SUCH_FUNCTION, object_name)
end
@@ -1945,8 +1943,8 @@ local function object_resolve(object_type, object_name)
else
role_or_user = _vuser:get{object_name}
end
- if role_or_user and role_or_user[4] == object_type then
- return role_or_user[1]
+ if role_or_user and role_or_user.type == object_type then
+ return role_or_user.id
elseif object_type == 'role' then
box.error(box.error.NO_SUCH_ROLE, object_name)
else
@@ -1973,7 +1971,7 @@ local function object_name(object_type, object_id)
else
box.error(box.error.UNKNOWN_SCHEMA_OBJECT, object_type)
end
- return space:get{object_id}[3]
+ return space:get{object_id}.name
end
box.schema.func = {}
@@ -2010,7 +2008,7 @@ box.schema.func.drop = function(name, opts)
tuple = _vfunc:get{name}
end
if tuple then
- fid = tuple[1]
+ fid = tuple.id
end
if fid == nil then
if not opts.if_exists then
@@ -2096,7 +2094,7 @@ end
box.internal.collation.id_by_name = function(name)
local _coll = box.space[box.schema.COLLATION_ID]
local coll = _coll.index.name:get{name}
- return coll[1]
+ return coll.id
end
box.schema.user = {}
@@ -2148,7 +2146,7 @@ box.schema.user.create = function(name, opts)
auth_mech_list["chap-sha1"] = box.schema.user.password(opts.password)
end
local _user = box.space[box.schema.USER_ID]
- uid = _user:auto_increment{session.euid(), name, 'user', auth_mech_list}[1]
+ uid = _user:auto_increment{session.euid(), name, 'user', auth_mech_list}.id
-- grant role 'public' to the user
box.schema.user.grant(uid, 'public')
-- Grant privilege 'alter' on itself, so that it can
@@ -2201,7 +2199,7 @@ local function grant(uid, name, privilege, object_type,
local tuple = _vpriv:get{uid, object_type, oid}
local old_privilege
if tuple ~= nil then
- old_privilege = tuple[5]
+ old_privilege = tuple.privilege
else
old_privilege = 0
end
@@ -2255,8 +2253,8 @@ local function revoke(uid, name, privilege, object_type, object_name, options)
object_type, object_name)
end
end
- local old_privilege = tuple[5]
- local grantor = tuple[1]
+ local old_privilege = tuple.privilege
+ local grantor = tuple.grantor
-- sic:
-- a user may revoke more than he/she granted
-- (erroneous user input)
@@ -2282,25 +2280,25 @@ local function drop(uid, opts)
local _vpriv = box.space[box.schema.VPRIV_ID]
local spaces = box.space[box.schema.VSPACE_ID].index.owner:select{uid}
for k, tuple in pairs(spaces) do
- box.space[tuple[1]]:drop()
+ box.space[tuple.id]:drop()
end
local funcs = box.space[box.schema.VFUNC_ID].index.owner:select{uid}
for k, tuple in pairs(funcs) do
- box.schema.func.drop(tuple[1])
+ box.schema.func.drop(tuple.id)
end
-- if this is a role, revoke this role from whoever it was granted to
local grants = _vpriv.index.object:select{'role', uid}
for k, tuple in pairs(grants) do
- revoke(tuple[2], tuple[2], uid)
+ revoke(tuple.grantee, tuple.grantee, uid)
end
local sequences = box.space[box.schema.VSEQUENCE_ID].index.owner:select{uid}
for k, tuple in pairs(sequences) do
- box.schema.sequence.drop(tuple[1])
+ box.schema.sequence.drop(tuple.id)
end
-- xxx: hack, we have to revoke session and usage privileges
-- of a user using a setuid function in absence of create/drop
-- privileges and grant option
- if box.space._vuser:get{uid}[4] == 'user' then
+ if box.space._vuser:get{uid}.type == 'user' then
box.session.su('admin', box.schema.user.revoke, uid,
'session,usage', 'universe', nil, {if_exists = true})
end
@@ -2309,7 +2307,8 @@ local function drop(uid, opts)
for k, tuple in pairs(privs) do
-- we need an additional box.session.su() here, because of
-- unnecessary check for privilege PRIV_REVOKE in priv_def_check()
- box.session.su("admin", revoke, uid, uid, tuple[5], tuple[3], tuple[4])
+ box.session.su("admin", revoke, uid, uid, tuple.privilege,
+ tuple.object_type, tuple.object_id)
end
box.space[box.schema.USER_ID]:delete{uid}
end
@@ -2369,7 +2368,8 @@ local function info(id)
for _, v in pairs(_priv:select{id}) do
table.insert(
privs,
- {privilege_name(v[5]), v[3], object_name(v[3], v[4])}
+ {privilege_name(v.privilege), v.object_type,
+ object_name(v.object_type, v.object_id)}
)
end
return privs
diff --git a/test/box/alter.result b/test/box/alter.result
index c1b1de13..75d6dae2 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -52,25 +52,10 @@ _space:insert{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
---
- error: Duplicate key exists in unique index 'primary' in space '_space'
...
-_space:replace{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
----
-- [280, 1, '_space', 'memtx', 0, {}, []]
-...
_space:insert{_index.id, ADMIN, '_index', 'memtx', 0, EMPTY_MAP, {}}
---
- error: Duplicate key exists in unique index 'primary' in space '_space'
...
-_space:replace{_index.id, ADMIN, '_index', 'memtx', 0, EMPTY_MAP, {}}
----
-- [288, 1, '_index', 'memtx', 0, {}, []]
-...
---
--- Can't change properties of a space
---
-_space:replace{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
----
-- [280, 1, '_space', 'memtx', 0, {}, []]
-...
--
-- Can't drop a system space
--
diff --git a/test/box/alter.test.lua b/test/box/alter.test.lua
index 733d27c5..3cb0c4f8 100644
--- a/test/box/alter.test.lua
+++ b/test/box/alter.test.lua
@@ -24,13 +24,7 @@ _space:insert{_space.id, ADMIN, 'test', 'world', 0, EMPTY_MAP, {}}
-- There is already a tuple for the system space
--
_space:insert{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
-_space:replace{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
_space:insert{_index.id, ADMIN, '_index', 'memtx', 0, EMPTY_MAP, {}}
-_space:replace{_index.id, ADMIN, '_index', 'memtx', 0, EMPTY_MAP, {}}
---
--- Can't change properties of a space
---
-_space:replace{_space.id, ADMIN, '_space', 'memtx', 0, EMPTY_MAP, {}}
--
-- Can't drop a system space
--
--
2.11.0
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 3/4] schema: allow to set sequence for any index part, not just the first
2019-05-15 10:33 [PATCH 0/4] A few fixes/improvements for autoincrement indexes Vladimir Davydov
2019-05-15 10:33 ` [PATCH 1/4] schema: use tuple field names in Lua Vladimir Davydov
2019-05-15 10:33 ` [PATCH 2/4] schema: fix error while altering index with sequence Vladimir Davydov
@ 2019-05-15 10:33 ` Vladimir Davydov
2019-05-16 7:45 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 10:33 ` [PATCH 4/4] schema: explicitly forbid setting sequence for json path key part Vladimir Davydov
2019-05-21 10:42 ` [tarantool-patches] Re: [PATCH 0/4] A few fixes/improvements for autoincrement indexes Kirill Yukhin
4 siblings, 1 reply; 17+ messages in thread
From: Vladimir Davydov @ 2019-05-15 10:33 UTC (permalink / raw)
To: tarantool-patches
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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
Rfj#7n<+o@q*9X-Qt?gkf;xqsN
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 14ad4de1..91900395 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 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
^ permalink raw reply [flat|nested] 17+ messages in thread