Skip to content

Commit 6b20c8c

Browse files
api: return error if no tuples/objects
Before this patch, the behavior of `*_many` requests if empty array of tuples/objects were rather confusing. For example, due to format processing all `*_object_many` operations resulted in `nil, {}` -- error request with zero errors. `insert_many` and `replace_many` calls result in `nil, nil` -- no result, no error. `upsert_many` results in `{metadata = metadata}, nil` with no `rows` in response. Thus, for all six `*_many` calls trying to execute the request with empty array on input result in malformed response. After this patch, trying to run `*_many` request with empty array of tuples/objects will result in `nil, {err}`, similar to existing `*_many` API. Single tuple crud API already does not allow to run with no tuples/objects. Closes #377
1 parent 53ba2c6 commit 6b20c8c

8 files changed

+88
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111
* `deps.sh` installs the `vshard` instead of the `cartridge` by default (#364).
1212
You could to specify an environment variable `CARTIRDGE_VERSION` to install
1313
the `cartridge` and run tests cases with it.
14+
* Return explicit error for `*_many` call with no tuples/objects (#377).
1415

1516
### Fixed
1617
* `crud.readview` resource cleanup on garbage collect (#379).

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ local result, err = crud.insert_object_many(space_name, objects, opts)
288288
where:
289289

290290
* `space_name` (`string`) - name of the space to insert an object
291-
* `tuples` / `objects` (`table`) - array of tuples/objects to insert
291+
* `tuples` / `objects` (`table`) - array of tuples/objects to insert (at least one)
292292
* `opts`:
293293
* `timeout` (`?number`) - `vshard.call` timeout and vshard master
294294
discovery timeout (in seconds), default value is 2
@@ -639,7 +639,7 @@ local result, err = crud.replace_object_many(space_name, objects, opts)
639639
where:
640640

641641
* `space_name` (`string`) - name of the space to insert/replace an object
642-
* `tuples` / `objects` (`table`) - array of tuples/objects to replace
642+
* `tuples` / `objects` (`table`) - array of tuples/objects to replace (at least one)
643643
* `opts`:
644644
* `timeout` (`?number`) - `vshard.call` timeout and vshard master
645645
discovery timeout (in seconds), default value is 2
@@ -855,7 +855,7 @@ where:
855855
* `tuples_operation_data` / `objects_operation_data` (`table`) - array of
856856
tuples/objects to insert
857857
and update [operations](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/#box-space-update)
858-
in format {{tuple_1, operation_1}, ..., {tuple_n, operation_n}},
858+
in format {{tuple_1, operation_1}, ..., {tuple_n, operation_n}} (at least one),
859859
if there is tuple with duplicate key then existing tuple will
860860
be updated with update operations
861861
* `opts`:

crud/insert_many.lua

+8
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@ function insert_many.tuples(space_name, tuples, opts)
244244
fetch_latest_metadata = '?boolean',
245245
})
246246

247+
if next(tuples) == nil then
248+
return nil, {InsertManyError:new("At least one tuple expected")}
249+
end
250+
247251
opts = opts or {}
248252

249253
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)
@@ -284,6 +288,10 @@ function insert_many.objects(space_name, objs, opts)
284288
fetch_latest_metadata = '?boolean',
285289
})
286290

291+
if next(objs) == nil then
292+
return nil, {InsertManyError:new("At least one object expected")}
293+
end
294+
287295
opts = opts or {}
288296

289297
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)

crud/replace_many.lua

+8
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ function replace_many.tuples(space_name, tuples, opts)
247247
fetch_latest_metadata = '?boolean',
248248
})
249249

250+
if next(tuples) == nil then
251+
return nil, {ReplaceManyError:new("At least one tuple expected")}
252+
end
253+
250254
opts = opts or {}
251255

252256
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)
@@ -287,6 +291,10 @@ function replace_many.objects(space_name, objs, opts)
287291
fetch_latest_metadata = '?boolean',
288292
})
289293

294+
if next(objs) == nil then
295+
return nil, {ReplaceManyError:new("At least one object expected")}
296+
end
297+
290298
opts = opts or {}
291299

292300
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)

crud/upsert_many.lua

+8
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ function upsert_many.tuples(space_name, tuples_operation_data, opts)
263263
fetch_latest_metadata = '?boolean',
264264
})
265265

266+
if next(tuples_operation_data) == nil then
267+
return nil, {UpsertManyError:new("At least one tuple expected")}
268+
end
269+
266270
opts = opts or {}
267271

268272
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)
@@ -303,6 +307,10 @@ function upsert_many.objects(space_name, objs_operation_data, opts)
303307
fetch_latest_metadata = '?boolean',
304308
})
305309

310+
if next(objs_operation_data) == nil then
311+
return nil, {UpsertManyError:new("At least one object expected")}
312+
end
313+
306314
opts = opts or {}
307315

308316
local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)

test/integration/insert_many_test.lua

+20
Original file line numberDiff line numberDiff line change
@@ -2032,3 +2032,23 @@ pgroup.test_noreturn_opt = function(g)
20322032
t.assert_equals(#errs, 3)
20332033
t.assert_equals(result, nil)
20342034
end
2035+
2036+
pgroup.test_zero_tuples = function(g)
2037+
local result, errs = g.cluster.main_server.net_box:call(
2038+
'crud.insert_many', {'customers', {}})
2039+
2040+
t.assert_not_equals(errs, nil)
2041+
t.assert_equals(#errs, 1)
2042+
t.assert_str_contains(errs[1].err, "At least one tuple expected")
2043+
t.assert_equals(result, nil)
2044+
end
2045+
2046+
pgroup.test_zero_objects = function(g)
2047+
local result, errs = g.cluster.main_server.net_box:call(
2048+
'crud.insert_object_many', {'customers', {}})
2049+
2050+
t.assert_not_equals(errs, nil)
2051+
t.assert_equals(#errs, 1)
2052+
t.assert_str_contains(errs[1].err, "At least one object expected")
2053+
t.assert_equals(result, nil)
2054+
end

test/integration/replace_many_test.lua

+20
Original file line numberDiff line numberDiff line change
@@ -2043,3 +2043,23 @@ pgroup.test_noreturn_opt = function(g)
20432043
t.assert_equals(#errs, 3)
20442044
t.assert_equals(result, nil)
20452045
end
2046+
2047+
pgroup.test_zero_tuples = function(g)
2048+
local result, errs = g.cluster.main_server.net_box:call(
2049+
'crud.replace_many', {'customers', {}})
2050+
2051+
t.assert_not_equals(errs, nil)
2052+
t.assert_equals(#errs, 1)
2053+
t.assert_str_contains(errs[1].err, "At least one tuple expected")
2054+
t.assert_equals(result, nil)
2055+
end
2056+
2057+
pgroup.test_zero_objects = function(g)
2058+
local result, errs = g.cluster.main_server.net_box:call(
2059+
'crud.replace_object_many', {'customers', {}})
2060+
2061+
t.assert_not_equals(errs, nil)
2062+
t.assert_equals(#errs, 1)
2063+
t.assert_str_contains(errs[1].err, "At least one object expected")
2064+
t.assert_equals(result, nil)
2065+
end

test/integration/upsert_many_test.lua

+20
Original file line numberDiff line numberDiff line change
@@ -2044,3 +2044,23 @@ pgroup.test_noreturn_opt = function(g)
20442044
t.assert_equals(#errs, 3)
20452045
t.assert_equals(result, nil)
20462046
end
2047+
2048+
pgroup.test_zero_tuples = function(g)
2049+
local result, errs = g.cluster.main_server.net_box:call(
2050+
'crud.upsert_many', {'customers', {}})
2051+
2052+
t.assert_not_equals(errs, nil)
2053+
t.assert_equals(#errs, 1)
2054+
t.assert_str_contains(errs[1].err, "At least one tuple expected")
2055+
t.assert_equals(result, nil)
2056+
end
2057+
2058+
pgroup.test_zero_objects = function(g)
2059+
local result, errs = g.cluster.main_server.net_box:call(
2060+
'crud.upsert_object_many', {'customers', {}})
2061+
2062+
t.assert_not_equals(errs, nil)
2063+
t.assert_equals(#errs, 1)
2064+
t.assert_str_contains(errs[1].err, "At least one object expected")
2065+
t.assert_equals(result, nil)
2066+
end

0 commit comments

Comments
 (0)