Skip to content

Commit 74597c1

Browse files
committed
Introduce crud.min and crud.max
1 parent d2d45e9 commit 74597c1

File tree

7 files changed

+425
-34
lines changed

7 files changed

+425
-34
lines changed

crud.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ local upsert = require('crud.upsert')
1010
local delete = require('crud.delete')
1111
local select = require('crud.select')
1212
local truncate = require('crud.truncate')
13+
local borders = require('crud.borders')
1314
local utils = require('crud.common.utils')
1415

1516
local crud = {}
@@ -69,6 +70,9 @@ crud.unflatten_rows = utils.unflatten_rows
6970
-- @function truncate
7071
crud.truncate = truncate.call
7172

73+
crud.min = borders.min
74+
crud.max = borders.max
75+
7276
-- @refer utils.cut_rows
7377
-- @function cut_rows
7478
crud.cut_rows = utils.cut_rows
@@ -97,6 +101,7 @@ function crud.init_storage()
97101
delete.init()
98102
select.init()
99103
truncate.init()
104+
borders.init()
100105
end
101106

102107
function crud.init_router()

crud/borders.lua

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
local checks = require('checks')
2+
local errors = require('errors')
3+
local vshard = require('vshard')
4+
5+
local dev_checks = require('crud.common.dev_checks')
6+
local call = require('crud.common.call')
7+
local utils = require('crud.common.utils')
8+
local schema = require('crud.common.schema')
9+
local Keydef = require('crud.compare.keydef')
10+
11+
local BorderError = errors.new_class('Border', {capture_stack = false})
12+
13+
local borders = {}
14+
15+
local STAT_FUNC_NAME = '_crud.get_border_on_storage'
16+
17+
18+
local function get_border_on_storage(border_name, space_name, index_name, field_names)
19+
dev_checks('string', 'string', 'string', '?table')
20+
21+
assert(border_name == 'min' or border_name == 'max')
22+
23+
local space = box.space[space_name]
24+
if space == nil then
25+
return nil, BorderError:new("Space %q doesn't exist", space_name)
26+
end
27+
28+
local index = space.index[index_name]
29+
if index == nil then
30+
return nil, BorderError:new("Index %q of space doesn't exist", index_name, space_name)
31+
end
32+
33+
local function get_border(index)
34+
return index[border_name](index)
35+
end
36+
37+
return schema.wrap_func_result(space, get_border, {index}, {
38+
add_space_schema_hash = true,
39+
field_names = field_names,
40+
})
41+
end
42+
43+
function borders.init()
44+
_G._crud.get_border_on_storage = get_border_on_storage
45+
end
46+
47+
local function is_closer(border_name, keydef, tuple, res_tuple)
48+
assert(border_name == 'min' or border_name == 'max')
49+
50+
if res_tuple == nil then
51+
return true
52+
end
53+
54+
local cmp = keydef:compare(tuple, res_tuple)
55+
56+
return border_name == 'min' and cmp < 0 or border_name == 'max' and cmp > 0
57+
end
58+
59+
local function get_border(border_name, space_name, index_name, opts)
60+
checks('string', 'string', '?string', {
61+
timeout = '?number',
62+
fields = '?table',
63+
})
64+
65+
opts = opts or {}
66+
67+
local space = utils.get_space(space_name, vshard.router.routeall())
68+
if space == nil then
69+
return nil, BorderError:new("Space %q doesn't exist", space_name), true
70+
end
71+
72+
if index_name == nil then
73+
index_name = space.index[0].name
74+
end
75+
local index = space.index[index_name]
76+
if index == nil then
77+
return nil, BorderError:new("Index %q of space doesn't exist", index_name, space_name)
78+
end
79+
80+
local primary_index = space.index[0]
81+
82+
local cmp_key_parts = utils.merge_primary_key_parts(index.parts, primary_index.parts)
83+
local field_names = utils.enrich_field_names_with_cmp_key(opts.fields, cmp_key_parts, space:format())
84+
85+
local replicasets = vshard.router.routeall()
86+
local call_opts = {
87+
mode = 'read',
88+
replicasets = replicasets,
89+
timeout = opts.timeout,
90+
}
91+
local results, err = call.map(
92+
STAT_FUNC_NAME,
93+
{border_name, space_name, index_name, field_names},
94+
call_opts
95+
)
96+
97+
if err ~= nil then
98+
return nil, BorderError:new("Failed to get %s: %s", border_name, err)
99+
end
100+
101+
local keydef = Keydef.new(replicasets, space_name, field_names, index_name)
102+
103+
local tuples = {}
104+
for _, result in pairs(results) do
105+
if result[1].err ~= nil then
106+
return nil, BorderError:new("Failed to get %s: %s", border_name, result.err)
107+
end
108+
109+
local tuple = result[1].res
110+
if tuple ~= nil then
111+
table.insert(tuples, tuple)
112+
end
113+
end
114+
115+
local res_tuple = nil
116+
for _, tuple in ipairs(tuples) do
117+
if tuple ~= nil and is_closer(border_name, keydef, tuple, res_tuple) then
118+
res_tuple = tuple
119+
end
120+
end
121+
122+
local result = utils.format_result({res_tuple}, space, field_names)
123+
124+
if opts.fields ~= nil then
125+
result = utils.cut_rows(result.rows, result.metadata, opts.fields)
126+
end
127+
128+
return result
129+
end
130+
131+
--- Find the minimum value in the specified index
132+
--
133+
-- @function min
134+
--
135+
-- @param string space_name
136+
-- A space name
137+
--
138+
-- @param ?string index_name
139+
-- An index name (by default, primary index is used)
140+
--
141+
-- @tparam ?number opts.timeout
142+
-- Function call timeout
143+
--
144+
-- @tparam ?table opts.fields
145+
-- Field names for getting only a subset of fields
146+
--
147+
-- @return[1] result
148+
-- @treturn[2] nil
149+
-- @treturn[2] table Error description
150+
function borders.min(space_name, index_name, opts)
151+
return get_border('min', space_name, index_name, opts)
152+
end
153+
154+
--- Find the maximum value in the specified index
155+
--
156+
-- @function min
157+
--
158+
-- @param string space_name
159+
-- A space name
160+
--
161+
-- @param ?string index_name
162+
-- An index name (by default, primary index is used)
163+
--
164+
-- @tparam ?number opts.timeout
165+
-- Function call timeout
166+
--
167+
-- @tparam ?table opts.fields
168+
-- Field names for getting only a subset of fields
169+
--
170+
-- @return[1] result
171+
-- @treturn[2] nil
172+
-- @treturn[2] table Error description
173+
function borders.max(space_name, index_name, opts)
174+
return get_border('max', space_name, index_name, opts)
175+
end
176+
177+
return borders

crud/common/schema.lua

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,19 +197,14 @@ function schema.truncate_row_trailing_fields(tuple, field_names)
197197
return tuple
198198
end
199199

200-
-- schema.wrap_box_space_func_result pcalls some box.space function
201-
-- and returns its result as a table
202-
-- `{res = ..., err = ..., space_schema_hash = ...}`
203-
-- space_schema_hash is computed if function failed and
204-
-- `add_space_schema_hash` is true
205-
function schema.wrap_box_space_func_result(space, func_name, args, opts)
206-
dev_checks('table', 'string', 'table', 'table')
200+
function schema.wrap_func_result(space, func, args, opts)
201+
dev_checks('table', 'function', 'table', 'table')
207202

208203
local result = {}
209204

210205
opts = opts or {}
211206

212-
local ok, func_res = pcall(space[func_name], space, unpack(args))
207+
local ok, func_res = pcall(func, unpack(args))
213208
if not ok then
214209
result.err = func_res
215210
if opts.add_space_schema_hash then
@@ -222,6 +217,19 @@ function schema.wrap_box_space_func_result(space, func_name, args, opts)
222217
return result
223218
end
224219

220+
-- schema.wrap_box_space_func_result pcalls some box.space function
221+
-- and returns its result as a table
222+
-- `{res = ..., err = ..., space_schema_hash = ...}`
223+
-- space_schema_hash is computed if function failed and
224+
-- `add_space_schema_hash` is true
225+
function schema.wrap_box_space_func_result(space, func_name, args, opts)
226+
dev_checks('table', 'string', 'table', 'table')
227+
228+
return schema.wrap_func_result(space, function(args)
229+
return space[func_name](space, args)
230+
end, args, opts)
231+
end
232+
225233
-- schema.result_needs_reload checks that schema reload can
226234
-- be helpful to avoid storage error.
227235
-- It checks if space_schema_hash returned by storage

crud/common/utils.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,30 @@ function utils.merge_primary_key_parts(key_parts, pk_parts)
166166
return merged_parts
167167
end
168168

169+
function utils.enrich_field_names_with_cmp_key(field_names, key_parts, space_format)
170+
if field_names == nil then
171+
return nil
172+
end
173+
174+
local enriched_field_names = {}
175+
local key_field_names = {}
176+
177+
for _, field_name in ipairs(field_names) do
178+
table.insert(enriched_field_names, field_name)
179+
key_field_names[field_name] = true
180+
end
181+
182+
for _, part in ipairs(key_parts) do
183+
local field_name = space_format[part.fieldno].name
184+
if not key_field_names[field_name] then
185+
table.insert(enriched_field_names, field_name)
186+
key_field_names[field_name] = true
187+
end
188+
end
189+
190+
return enriched_field_names
191+
end
192+
169193
local enabled_tarantool_features = {}
170194

171195
local function determine_enabled_features()

crud/select/plan.lua

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -142,30 +142,6 @@ local function construct_after_tuple_by_fields(space_format, field_names, tuple)
142142
return transformed_tuple
143143
end
144144

145-
local function enrich_field_names_with_cmp_key(field_names, key_parts, space_format)
146-
if field_names == nil then
147-
return nil
148-
end
149-
150-
local enriched_field_names = {}
151-
local key_field_names = {}
152-
153-
for _, field_name in ipairs(field_names) do
154-
table.insert(enriched_field_names, field_name)
155-
key_field_names[field_name] = true
156-
end
157-
158-
for _, part in ipairs(key_parts) do
159-
local field_name = space_format[part.fieldno].name
160-
if not key_field_names[field_name] then
161-
table.insert(enriched_field_names, field_name)
162-
key_field_names[field_name] = true
163-
end
164-
end
165-
166-
return enriched_field_names
167-
end
168-
169145
function select_plan.new(space, conditions, opts)
170146
dev_checks('table', '?table', {
171147
first = '?number',
@@ -222,7 +198,7 @@ function select_plan.new(space, conditions, opts)
222198
end
223199

224200
local cmp_key_parts = utils.merge_primary_key_parts(scan_index.parts, primary_index.parts)
225-
local field_names = enrich_field_names_with_cmp_key(opts.field_names, cmp_key_parts, space_format)
201+
local field_names = utils.enrich_field_names_with_cmp_key(opts.field_names, cmp_key_parts, space_format)
226202

227203
-- handle opts.first
228204
local total_tuples_count

0 commit comments

Comments
 (0)