Skip to content

Commit d13fe63

Browse files
authored
Fix opts table damaging by CRUD methods (#200)
If we have `opts` variable and we pass it to some crud method (`upsert_object` for example) and then use `opts` variable again for crud method (`get` for example) we receive an error because `*_object` method damages `opts` variable adding `add_space_schema_hash` parameter to `opts` variable.
1 parent 66113b7 commit d13fe63

File tree

11 files changed

+330
-9
lines changed

11 files changed

+330
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1616
versions (< 1.10.8) anymore.
1717
* Names of errors generated by CRUD operations have been unified.
1818

19+
### Fixed
20+
21+
* Damaging of opts table by CRUD methods.
22+
1923
### Added
2024

2125
* Added integration with service coveralls.io.

crud/common/utils.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local errors = require('errors')
22
local ffi = require('ffi')
33
local vshard = require('vshard')
4+
local fun = require('fun')
45

56
local schema = require('crud.common.schema')
67
local dev_checks = require('crud.common.dev_checks')
@@ -524,4 +525,13 @@ function utils.flatten_obj_reload(space_name, obj)
524525
return schema.wrap_func_reload(flatten_obj, space_name, obj)
525526
end
526527

528+
-- Merge two options map.
529+
--
530+
-- `opts_a` and/or `opts_b` can be `nil`.
531+
--
532+
-- If `opts_a.foo` and `opts_b.foo` exists, prefer `opts_b.foo`.
533+
function utils.merge_options(opts_a, opts_b)
534+
return fun.chain(opts_a or {}, opts_b or {}):tomap()
535+
end
536+
527537
return utils

crud/insert.lua

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,8 @@ end
144144
function insert.object(space_name, obj, opts)
145145
checks('string', 'table', '?table')
146146

147-
opts = opts or {}
148-
149147
-- insert can fail if router uses outdated schema to flatten object
150-
opts.add_space_schema_hash = true
148+
opts = utils.merge_options(opts, {add_space_schema_hash = true})
151149

152150
local tuple, err = utils.flatten_obj_reload(space_name, obj)
153151
if err ~= nil then

crud/replace.lua

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,8 @@ end
148148
function replace.object(space_name, obj, opts)
149149
checks('string', 'table', '?table')
150150

151-
opts = opts or {}
152-
153151
-- replace can fail if router uses outdated schema to flatten object
154-
opts.add_space_schema_hash = true
152+
opts = utils.merge_options(opts, {add_space_schema_hash = true})
155153

156154
local tuple, err = utils.flatten_obj_reload(space_name, obj)
157155
if err ~= nil then

crud/upsert.lua

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,8 @@ end
157157
function upsert.object(space_name, obj, user_operations, opts)
158158
checks('string', 'table', 'table', '?table')
159159

160-
opts = opts or {}
161-
162160
-- upsert can fail if router uses outdated schema to flatten object
163-
opts.add_space_schema_hash = true
161+
opts = utils.merge_options(opts, {add_space_schema_hash = true})
164162

165163
local tuple, err = utils.flatten_obj_reload(space_name, obj)
166164
if err ~= nil then

test/integration/borders_test.lua

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,51 @@ pgroup.test_equal_secondary_keys = function(g)
311311
local objects = crud.unflatten_rows(result.rows, result.metadata)
312312
t.assert_equals(objects, helpers.get_objects_by_idxs(customers, {2}))
313313
end
314+
315+
pgroup.test_opts_not_damaged = function(g)
316+
helpers.insert_objects(g, 'customers', {
317+
{
318+
id = 1, name = "Elizabeth", last_name = "Jackson",
319+
age = 21, city = "New York",
320+
}, {
321+
id = 2, name = "Mary", last_name = "Brown",
322+
age = 33, city = "Los Angeles",
323+
}, {
324+
id = 3, name = "David", last_name = "Smith",
325+
age = 12, city = "Los Angeles",
326+
}, {
327+
id = 4, name = "William", last_name = "White",
328+
age = 8, city = "Chicago",
329+
},
330+
})
331+
332+
-- min
333+
local min_opts = {timeout = 1, fields = {'name', 'age'}}
334+
local new_min_opts, err = g.cluster.main_server:eval([[
335+
local crud = require('crud')
336+
337+
local min_opts = ...
338+
339+
local _, err = crud.min('customers', nil, min_opts)
340+
341+
return min_opts, err
342+
]], {min_opts})
343+
344+
t.assert_equals(err, nil)
345+
t.assert_equals(new_min_opts, min_opts)
346+
347+
-- max
348+
local max_opts = {timeout = 1, fields = {'name', 'age'}}
349+
local new_max_opts, err = g.cluster.main_server:eval([[
350+
local crud = require('crud')
351+
352+
local max_opts = ...
353+
354+
local _, err = crud.max('customers', nil, max_opts)
355+
356+
return max_opts, err
357+
]], {min_opts})
358+
359+
t.assert_equals(err, nil)
360+
t.assert_equals(new_max_opts, max_opts)
361+
end

test/integration/len_test.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,19 @@ pgroup.test_len_empty_space = function(g)
6262
t.assert_equals(err, nil)
6363
t.assert_equals(result, 0)
6464
end
65+
66+
pgroup.test_opts_not_damaged = function(g)
67+
local len_opts = {timeout = 1}
68+
local new_len_opts, err = g.cluster.main_server:eval([[
69+
local crud = require('crud')
70+
71+
local len_opts = ...
72+
73+
local _, err = crud.len('customers', len_opts)
74+
75+
return len_opts, err
76+
]], {len_opts})
77+
78+
t.assert_equals(err, nil)
79+
t.assert_equals(new_len_opts, len_opts)
80+
end

test/integration/pairs_test.lua

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,3 +754,54 @@ pgroup.test_pairs_timeout = function(g)
754754
]])
755755
t.assert_equals(objects, raw_rows)
756756
end
757+
758+
pgroup.test_opts_not_damaged = function(g)
759+
local customers = helpers.insert_objects(g, 'customers', {
760+
{
761+
id = 1, name = "Elizabeth", last_name = "Jackson",
762+
age = 12, city = "Los Angeles",
763+
}, {
764+
id = 2, name = "Mary", last_name = "Brown",
765+
age = 46, city = "London",
766+
}, {
767+
id = 3, name = "David", last_name = "Smith",
768+
age = 33, city = "Los Angeles",
769+
}, {
770+
id = 4, name = "William", last_name = "White",
771+
age = 46, city = "Chicago",
772+
},
773+
})
774+
775+
table.sort(customers, function(obj1, obj2) return obj1.id < obj2.id end)
776+
777+
local expected_customers = {
778+
{id = 3, name = "David", age = 33},
779+
{id = 4, name = "William", age = 46},
780+
}
781+
782+
-- after tuple should be in `fields` format + primary key
783+
local fields = {'name', 'age'}
784+
local after = {"Mary", 46, 2}
785+
786+
local pairs_opts = {
787+
timeout = 1, bucket_id = 1161,
788+
batch_size = 105, first = 2, after = after,
789+
fields = fields, mode = 'read', prefer_replica = false,
790+
balance = false, force_map_call = false, use_tomap = true,
791+
}
792+
local new_pairs_opts, objects = g.cluster.main_server:eval([[
793+
local crud = require('crud')
794+
795+
local pairs_opts = ...
796+
797+
local objects = {}
798+
for _, object in crud.pairs('customers', nil, pairs_opts) do
799+
table.insert(objects, object)
800+
end
801+
802+
return pairs_opts, objects
803+
]], {pairs_opts})
804+
805+
t.assert_equals(objects, expected_customers)
806+
t.assert_equals(new_pairs_opts, pairs_opts)
807+
end

test/integration/select_test.lua

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,3 +1538,46 @@ pgroup.test_select_timeout = function(g)
15381538
{4, 1161, "William", "White", 81, "Chicago"},
15391539
})
15401540
end
1541+
1542+
pgroup.test_opts_not_damaged = function(g)
1543+
local customers = helpers.insert_objects(g, 'customers', {
1544+
{
1545+
id = 1, name = "Elizabeth", last_name = "Jackson",
1546+
age = 12, city = "Los Angeles",
1547+
}, {
1548+
id = 2, name = "Mary", last_name = "Brown",
1549+
age = 46, city = "London",
1550+
}, {
1551+
id = 3, name = "David", last_name = "Smith",
1552+
age = 33, city = "Los Angeles",
1553+
}, {
1554+
id = 4, name = "William", last_name = "White",
1555+
age = 46, city = "Chicago",
1556+
},
1557+
})
1558+
1559+
table.sort(customers, function(obj1, obj2) return obj1.id < obj2.id end)
1560+
1561+
-- after tuple should be in `fields` format + primary key
1562+
local fields = {'name', 'age'}
1563+
local after = {"Mary", 46, 2}
1564+
1565+
local select_opts = {
1566+
timeout = 1, bucket_id = 1161,
1567+
batch_size = 105, first = 2, after = after,
1568+
fields = fields, mode = 'read', prefer_replica = false,
1569+
balance = false, force_map_call = false,
1570+
}
1571+
local new_select_opts, err = g.cluster.main_server:eval([[
1572+
local crud = require('crud')
1573+
1574+
local select_opts = ...
1575+
1576+
local _, err = crud.select('customers', nil, select_opts)
1577+
1578+
return select_opts, err
1579+
]], {select_opts})
1580+
1581+
t.assert_equals(err, nil)
1582+
t.assert_equals(new_select_opts, select_opts)
1583+
end

test/integration/simple_operations_test.lua

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,3 +1007,142 @@ pgroup.test_partial_result_bad_input = function(g)
10071007
t.assert_equals(result, nil)
10081008
t.assert_str_contains(err.err, 'Space format doesn\'t contain field named "lastname"')
10091009
end
1010+
1011+
pgroup.test_opts_not_damaged = function(g)
1012+
-- insert
1013+
local insert_opts = {timeout = 1, bucket_id = 655, fields = {'name', 'age'}}
1014+
local new_insert_opts, err = g.cluster.main_server:eval([[
1015+
local crud = require('crud')
1016+
1017+
local insert_opts = ...
1018+
1019+
local _, err = crud.insert('customers', {22, box.NULL, 'Elizabeth', 24}, insert_opts)
1020+
1021+
return insert_opts, err
1022+
]], {insert_opts})
1023+
1024+
t.assert_equals(err, nil)
1025+
t.assert_equals(new_insert_opts, insert_opts)
1026+
1027+
-- insert_object
1028+
local insert_opts = {timeout = 1, bucket_id = 477, fields = {'name', 'age'}}
1029+
local new_insert_opts, err = g.cluster.main_server:eval([[
1030+
local crud = require('crud')
1031+
1032+
local insert_opts = ...
1033+
1034+
local _, err = crud.insert_object('customers', {id = 1, name = 'Fedor', age = 59}, insert_opts)
1035+
1036+
return insert_opts, err
1037+
]], {insert_opts})
1038+
1039+
t.assert_equals(err, nil)
1040+
t.assert_equals(new_insert_opts, insert_opts)
1041+
1042+
-- upsert
1043+
local upsert_opts = {timeout = 1, bucket_id = 907, fields = {'name', 'age'}}
1044+
local new_upsert_opts, err = g.cluster.main_server:eval([[
1045+
local crud = require('crud')
1046+
1047+
local upsert_opts = ...
1048+
1049+
local _, err = crud.upsert('customers', {33, box.NULL, 'Peter', 35}, {{'+', 'age', 1}}, upsert_opts)
1050+
1051+
return upsert_opts, err
1052+
]], {upsert_opts})
1053+
1054+
t.assert_equals(err, nil)
1055+
t.assert_equals(new_upsert_opts, upsert_opts)
1056+
1057+
-- upsert_object
1058+
local upsert_opts = {timeout = 1, bucket_id = 401, fields = {'name', 'age'}}
1059+
local new_upsert_opts, err = g.cluster.main_server:eval([[
1060+
local crud = require('crud')
1061+
1062+
local upsert_opts = ...
1063+
1064+
local _, err = crud.upsert_object('customers',
1065+
{id = 2, name = 'Alex', age = 30}, {{'+', 'age', 1}},
1066+
upsert_opts)
1067+
1068+
return upsert_opts, err
1069+
]], {upsert_opts})
1070+
1071+
t.assert_equals(err, nil)
1072+
t.assert_equals(new_upsert_opts, upsert_opts)
1073+
1074+
-- get
1075+
local get_opts = {timeout = 1, bucket_id = 401, fields = {'name', 'age'}}
1076+
local new_get_opts, err = g.cluster.main_server:eval([[
1077+
local crud = require('crud')
1078+
1079+
local get_opts = ...
1080+
1081+
local _, err = crud.get('customers', 2, get_opts)
1082+
1083+
return get_opts, err
1084+
]], {get_opts})
1085+
1086+
t.assert_equals(err, nil)
1087+
t.assert_equals(new_get_opts, get_opts)
1088+
1089+
-- update
1090+
local update_opts = {timeout = 1, bucket_id = 401, fields = {'name', 'age'}}
1091+
local new_update_opts, err = g.cluster.main_server:eval([[
1092+
local crud = require('crud')
1093+
1094+
local update_opts = ...
1095+
1096+
local _, err = crud.update('customers', 2, {{'+', 'age', 10}}, update_opts)
1097+
1098+
return update_opts, err
1099+
]], {update_opts})
1100+
1101+
t.assert_equals(err, nil)
1102+
t.assert_equals(new_update_opts, update_opts)
1103+
1104+
-- replace
1105+
local replace_opts = {timeout = 1, bucket_id = 655, fields = {'name', 'age'}}
1106+
local new_replace_opts, err = g.cluster.main_server:eval([[
1107+
local crud = require('crud')
1108+
1109+
local replace_opts = ...
1110+
1111+
local _, err = crud.replace('customers', {22, box.NULL, 'Elizabeth', 25}, replace_opts)
1112+
1113+
return replace_opts, err
1114+
]], {replace_opts})
1115+
1116+
t.assert_equals(err, nil)
1117+
t.assert_equals(new_replace_opts, replace_opts)
1118+
1119+
-- replace_object
1120+
local replace_opts = {timeout = 1, bucket_id = 477, fields = {'name', 'age'}}
1121+
local new_replace_opts, err = g.cluster.main_server:eval([[
1122+
local crud = require('crud')
1123+
1124+
local replace_opts = ...
1125+
1126+
local _, err = crud.replace_object('customers', {id = 1, name = 'Fedor', age = 60}, replace_opts)
1127+
1128+
return replace_opts, err
1129+
]], {replace_opts})
1130+
1131+
t.assert_equals(err, nil)
1132+
t.assert_equals(new_replace_opts, replace_opts)
1133+
1134+
-- delete
1135+
local delete_opts = {timeout = 1, bucket_id = 401, fields = {'name', 'age'}}
1136+
local new_delete_opts, err = g.cluster.main_server:eval([[
1137+
local crud = require('crud')
1138+
1139+
local delete_opts = ...
1140+
1141+
local _, err = crud.delete('customers', 2, delete_opts)
1142+
1143+
return delete_opts, err
1144+
]], {delete_opts})
1145+
1146+
t.assert_equals(err, nil)
1147+
t.assert_equals(new_delete_opts, delete_opts)
1148+
end

0 commit comments

Comments
 (0)