Skip to content

Commit

Permalink
[WIP] ddl: support sequences
Browse files Browse the repository at this point in the history
Closes #122
  • Loading branch information
DifferentialOrange committed Mar 20, 2024
1 parent 88251b9 commit a03cf9c
Show file tree
Hide file tree
Showing 14 changed files with 822 additions and 57 deletions.
2 changes: 2 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
redefined = false
globals = {"box", "_TARANTOOL", "tonumber64", "utf8", "table"}
include_files = {"**/*.lua", "*.rockspec", "*.luacheckrc"}
exclude_files = {".rocks/", "tmp/", ".history/"}
max_line_length = 120
max_comment_line_length = 150
69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,16 @@ format = {
},
...
},
sequences = { -- Not implemented yet
[seqence_name] = {
start
min
max
cycle
cache
step

}
}
sequences = {
[sequence_name] = {
start = start,
min = min,
max = max,
cycle = cycle,
cache = cache,
step = step,
},
},
}
```

Expand Down Expand Up @@ -292,8 +291,52 @@ local schema = {
}},
sharding_key = {'customer_id'},
sharding_func = 'vshard.router.bucket_id_mpcrc32',
}
}
},
tickets = {
engine = 'memtx',
is_local = false,
temporary = false,
format = {
{name = 'ticket_id', is_nullable = false, type = 'unsigned'},
{name = 'customer_id', is_nullable = false, type = 'unsigned'},
{name = 'bucket_id', is_nullable = false, type = 'unsigned'},
{name = 'contents', is_nullable = false, type = 'string'},
},
indexes = {{
name = 'ticket_id',
type = 'TREE',
unique = true,
parts = {
{path = 'ticket_id', is_nullable = false, type = 'unsigned'}
},
sequence = 'ticket_seq',
}, {,
name = 'customer_id',
type = 'TREE',
unique = false,
parts = {
{path = 'customer_id', is_nullable = false, type = 'unsigned'}
}
}, {
name = 'bucket_id',
type = 'TREE',
unique = false,
parts = {
{path = 'bucket_id', is_nullable = false, type = 'unsigned'}
}
}},
sharding_key = {'customer_id'},
sharding_func = 'vshard.router.bucket_id_mpcrc32',
},
},
sequences = {
ticket_seq = {
start = 1,
min = 1,
max = 10000,
cycle = false,
},
},
}
```

Expand Down
65 changes: 61 additions & 4 deletions ddl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local ddl_get = require('ddl.get')
local ddl_set = require('ddl.set')
local ddl_check = require('ddl.check')
local ddl_db = require('ddl.db')
local ddl_compare = require('ddl.compare')
local utils = require('ddl.utils')

local function check_schema_format(schema)
Expand All @@ -22,12 +23,14 @@ local function check_schema_format(schema)
return nil, string.format("functions: not supported")
end

if type(schema.sequences) ~= 'nil' then
return nil, string.format("sequences: not supported")
if type(schema.sequences) ~= 'nil' and type(schema.sequences) ~= 'table' then
return nil, string.format(
"sequences: must be a table or nil, got %s", type(schema.sequences)
)
end

do -- check redundant keys
local k = utils.redundant_key(schema, {'spaces'})
local k = utils.redundant_key(schema, {'spaces', 'sequences'})
if k ~= nil then
return nil, string.format(
"Invalid schema: redundant key %q", k
Expand All @@ -39,11 +42,44 @@ local function check_schema_format(schema)
end

local function _check_schema(schema)
-- Create sequences before spaces since space indexes use sequences.
local sequences = schema.sequences or {}
for sequence_name, sequence_schema in pairs(sequences) do
local ok, err = ddl_check.check_sequence(sequence_name, sequence_schema)
if not ok then
return nil, err
end

if box.sequence[sequence_name] ~= nil then
local current_schema = ddl_get.get_sequence_schema(sequence_name)
local _, err = ddl_compare.assert_equiv_sequence_schema(sequence_schema, current_schema)
if err ~= nil then
return nil, string.format(
"Incompatible schema: sequences[%q] %s", sequence_name, err)
end
else
local ok, err = pcall(
ddl_set.create_sequence,
sequence_name, sequence_schema, {dummy = true}
)

local dummy = box.sequence['_ddl_dummy']
if dummy then
pcall(box.schema.sequence.drop, dummy.id)
end

if not ok then
return nil, tostring(err):gsub('_ddl_dummy', sequence_name)
end
end
end

for space_name, space_schema in pairs(schema.spaces) do
local ok, err = ddl_check.check_space(space_name, space_schema)
local ok, err = ddl_check.check_space(space_name, space_schema, sequences)
if not ok then
return nil, err
end

if box.space[space_name] ~= nil then
local diff = {}
local current_schema = ddl_get.get_space_schema(space_name)
Expand Down Expand Up @@ -127,6 +163,12 @@ local function _set_schema(schema)
}
)

for sequence_name, sequence_schema in pairs(schema.sequences or {}) do
if box.sequence[sequence_name] == nil then
ddl_set.create_sequence(sequence_name, sequence_schema)
end
end

for space_name, space_schema in pairs(schema.spaces) do
if box.space[space_name] == nil then
ddl_set.create_space(space_name, space_schema)
Expand All @@ -153,8 +195,23 @@ local function get_schema()
end
end

local sequences = {}
for _, sequence in box.space._sequence:pairs() do
sequences[sequence.name] = ddl_get.get_sequence_schema(sequence.name)
end

local next_k = next(sequences)
local no_sequences = next_k == nil

if no_sequences then
-- For backward compatibility.
sequences = nil
end


return {
spaces = spaces,
sequences = sequences,
}
end

Expand Down
125 changes: 119 additions & 6 deletions ddl/check.lua
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,9 @@ local function check_index_parts(index, space)
return true
end

local function check_index(i, index, space)
local function check_index(i, index, space, sequences)
sequences = sequences or {}

if type(index) ~= 'table' then
return nil, string.format(
"spaces[%q].indexes[%d]: bad value" ..
Expand Down Expand Up @@ -709,9 +711,30 @@ local function check_index(i, index, space)
end
end

local keys = {'type', 'name', 'unique', 'parts', 'field'}
do
local index_sequence_name = index.sequence

if index_sequence_name ~= nil then
if type(index_sequence_name) ~= 'string' then
return nil, string.format(
"spaces[%q].indexes[%q].sequence: incorrect value (string expected, got %s)",
space.name, index.name, type(index_sequence_name)
)
end

local sequence = sequences[index_sequence_name]
if sequence == nil then
return nil, string.format(
"spaces[%q].indexes[%q].sequence: missing sequence %q in sequences section",
space.name, index.name, index_sequence_name
)
end
end
end

local keys = {'type', 'name', 'unique', 'parts', 'field', 'sequence'}
if index.type == 'RTREE' then
keys = {'type', 'name', 'unique', 'parts', 'field', 'dimension', 'distance'}
keys = {'type', 'name', 'unique', 'parts', 'field', 'dimension', 'distance', 'sequence'}
end

local k = utils.redundant_key(index, keys)
Expand Down Expand Up @@ -961,7 +984,7 @@ local function check_sharding_metadata(space)
end


local function check_space(space_name, space)
local function check_space(space_name, space, sequences)
if type(space_name) ~= 'string' then
return nil, string.format(
"spaces[%s]: invalid space name (string expected, got %s)",
Expand Down Expand Up @@ -1074,7 +1097,7 @@ local function check_space(space_name, space)
name = space_name,
engine = space.engine,
fields = space_fields,
})
}, sequences)

if not ok then
return nil, err
Expand Down Expand Up @@ -1118,6 +1141,95 @@ local function check_space(space_name, space)
return true
end

local function check_sequence_nullable_option_type(sequence_name, sequence,
option_name, type_checker)
local option = sequence[option_name]
if option == nil then
return true
end

local _, err = type_checker(option)

if err ~= nil then
return nil, string.format(
"sequences[%q].%s: bad value (%s)",
sequence_name, option_name, err
)
else
return true
end
end

local function check_sequence_multiple_nullable_options_type(sequence_name, sequence,
option_names_array, type_checker)
for _, option_name in ipairs(option_names_array) do
local _, err = check_sequence_nullable_option_type(sequence_name, sequence,
option_name, type_checker)
if err ~= nil then
return err
end
end

return true
end

local function check_sequence(sequence_name, sequence)
if type(sequence_name) ~= 'string' then
return nil, string.format(
"sequences[%s]: invalid sequence name (string expected, got %s)",
sequence_name, type(sequence_name)
)
end

if type(sequence) ~= 'table' then
return nil, string.format(
"sequences[%q]: bad value (table expected, got %s)",
sequence_name, type(sequence)
)
end

local number_nullable_options = {'start', 'min', 'max', 'cache', 'step'}
local number_checker = function(v)
if not utils.is_number(v) then
local actual_type = type(v)
return nil, ('number expected, got %s'):format(actual_type)
else
return true
end
end
local _, err = check_sequence_multiple_nullable_options_type(sequence_name, sequence,
number_nullable_options, number_checker)
if err ~= nil then
return nil, err
end

local boolean_nullable_options = {'cycle'}
local boolean_checker = function(v)
if type(v) ~= 'boolean' then
local actual_type = type(v)
return nil, ('boolean expected, got %s'):format(actual_type)
else
return true
end
end
local _, err = check_sequence_multiple_nullable_options_type(sequence_name, sequence,
boolean_nullable_options, boolean_checker)
if err ~= nil then
return nil, err
end

local allowed_options = utils.concat_arrays(number_nullable_options, boolean_nullable_options)
local k = utils.redundant_key(sequence, allowed_options)
if k ~= nil then
return nil, string.format(
"sequences[%q]: redundant key %q",
sequence_name, k
)
end

return true
end


return {
check_space = check_space,
Expand All @@ -1128,7 +1240,8 @@ return {
check_index_parts = check_index_parts,
check_index = check_index,
check_field = check_field,
check_sequence = check_sequence,
internal = {
is_callable = is_callable,
}
},
}
Loading

0 comments on commit a03cf9c

Please sign in to comment.