diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 0cdb34e22f541bc2b14b96c9bebdca7e5027e1a3..77573d50802eaa876a56e51d4ddfce399cae081f 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -448,7 +448,7 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in) * in->rpos. */ if (msg->header.type >= IPROTO_SELECT && - msg->header.type <= IPROTO_EVAL) { + msg->header.type <= IPROTO_UPSERT) { /* Pre-parse request before putting it into the queue */ if (msg->header.bodycnt == 0) { tnt_raise(ClientError, ER_INVALID_MSGPACK, @@ -630,6 +630,7 @@ tx_process_msg(struct cmsg *m) case IPROTO_REPLACE: case IPROTO_UPDATE: case IPROTO_DELETE: + case IPROTO_UPSERT: assert(msg->request.type == msg->header.type); struct iproto_port port; iproto_port_init(&port, out, msg->header.sync); diff --git a/src/lua/net_box.cc b/src/lua/net_box.cc index 9e6fc68e0c9e5702205a096818d73f5c05dc0f46..0a1303106f87ebc68a812c6db1253896f7ef4476 100644 --- a/src/lua/net_box.cc +++ b/src/lua/net_box.cc @@ -358,6 +358,51 @@ netbox_encode_update(lua_State *L) return 0; } +static int +netbox_encode_upsert(lua_State *L) +{ + if (lua_gettop(L) < 7) + return luaL_error(L, "Usage: netbox.encode_update(ibuf, sync, " + "space_id, index_id, key, ops, tuple)"); + + struct mpstream stream; + size_t svp = netbox_prepare_request(L, &stream, IPROTO_UPSERT); + + luamp_encode_map(cfg, &stream, 6); + + /* encode space_id */ + uint32_t space_id = lua_tointeger(L, 3); + luamp_encode_uint(cfg, &stream, IPROTO_SPACE_ID); + luamp_encode_uint(cfg, &stream, space_id); + + /* encode index_id */ + uint32_t index_id = lua_tointeger(L, 4); + luamp_encode_uint(cfg, &stream, IPROTO_INDEX_ID); + luamp_encode_uint(cfg, &stream, index_id); + + /* encode index_id */ + luamp_encode_uint(cfg, &stream, IPROTO_INDEX_BASE); + luamp_encode_uint(cfg, &stream, 1); + + /* encode in reverse order for speedup - see luamp_encode() code */ + /* encode tuple */ + luamp_encode_uint(cfg, &stream, IPROTO_TUPLE); + luamp_encode_tuple(L, cfg, &stream, 7); + lua_pop(L, 1); /* tuple */ + + /* encode ops */ + luamp_encode_uint(cfg, &stream, IPROTO_OPS); + luamp_encode_tuple(L, cfg, &stream, 6); + lua_pop(L, 1); /* ops */ + + /* encode key */ + luamp_encode_uint(cfg, &stream, IPROTO_KEY); + luamp_convert_key(L, cfg, &stream, 5); + + netbox_encode_request(&stream, svp); + return 0; +} + int luaopen_net_box(struct lua_State *L) { @@ -370,6 +415,7 @@ luaopen_net_box(struct lua_State *L) { "encode_replace", netbox_encode_replace }, { "encode_delete", netbox_encode_delete }, { "encode_update", netbox_encode_update }, + { "encode_upsert", netbox_encode_upsert }, { "encode_auth", netbox_encode_auth }, { NULL, NULL} }; diff --git a/src/lua/net_box.lua b/src/lua/net_box.lua index 76cbfa1371ab294b1606c8aadf243b8b86259b26..f6b09505c15232bfe2ef88a35601d69d4177d885 100644 --- a/src/lua/net_box.lua +++ b/src/lua/net_box.lua @@ -21,6 +21,7 @@ local DELETE = 5 local CALL = 6 local AUTH = 7 local EVAL = 8 +local UPSERT = 9 local PING = 64 local ERROR_TYPE = 65536 @@ -38,6 +39,7 @@ local TUPLE = 0x21 local FUNCTION_NAME = 0x22 local USER = 0x23 local EXPR = 0x27 +local OPS = 0x28 local DATA = 0x30 local ERROR = 0x31 local GREETING_SIZE = 128 @@ -92,6 +94,7 @@ local requests = { [REPLACE] = internal.encode_replace; [DELETE] = internal.encode_delete; [UPDATE] = internal.encode_update; + [UPSERT] = internal.encode_upsert; [SELECT] = function(wbuf, sync, spaceno, indexno, key, opts) if opts == nil then opts = {} @@ -158,6 +161,11 @@ local function space_metatable(self) return self:_update(space.id, key, oplist, 0) end, + upsert = function(space, key, oplist, tuple) + check_if_space(space) + return self:_upsert(space.id, key, oplist, tuple, 0) + end, + get = function(space, key) check_if_space(space) local res = self:_select(space.id, 0, key, @@ -240,6 +248,11 @@ local function index_metatable(self) return self:_update(idx.space.id, key, oplist, idx.id) end, + upsert = function(idx, key, oplist, tuple) + check_if_index(idx) + return self:_upsert(idx.space.id, key, oplist, tuple, idx.id) + end, + } } end @@ -966,7 +979,13 @@ local remote_methods = { _update = function(self, spaceno, key, oplist, index_id) local res = self:_request(UPDATE, true, spaceno, index_id, key, oplist) return one_tuple(res.body[DATA]) - end + end, + + _upsert = function(self, spaceno, key, oplist, tuple, index_id) + local res = self:_request(UPSERT, true, spaceno, + index_id, key, oplist, tuple) + return one_tuple(res.body[DATA]) + end, } setmetatable(remote, { __index = remote_methods }) diff --git a/test/box/net.box.result b/test/box/net.box.result index 2f38086ed423cfa437c3a54dfed8a87a4f2c668a..94ed39a94b689f769b744115a55cab4679c7c4e7 100644 --- a/test/box/net.box.result +++ b/test/box/net.box.result @@ -288,7 +288,7 @@ cn.space.net_box_test_space:insert{234, 1,2,3} ... cn.space.net_box_test_space.insert{234, 1,2,3} --- -- error: 'builtin/net.box.lua:130: Use space:method(...) instead space.method(...)' +- error: 'builtin/net.box.lua:133: Use space:method(...) instead space.method(...)' ... cn.space.net_box_test_space:replace{354, 1,2,3} --- @@ -905,6 +905,49 @@ r = c.space.test:select(nil, {limit=5000}) box.space.test:drop() --- ... +-- gh-970 gh-971 UPSERT over network +_ = box.schema.space.create('test') +--- +... +_ = box.space.test:create_index('primary', {type = 'TREE', parts = {1,'NUM'}}) +--- +... +_ = box.space.test:insert{1, 2, "string"} +--- +... +c = net:new(box.cfg.listen) +--- +... +c.space.test:select{} +--- +- - [1, 2, 'string'] +... +c.space.test:upsert({1}, {{'+', 2, 1}}, {10, 20, 'nothing'}) -- common update +--- +... +c.space.test:select{} +--- +- - [1, 3, 'string'] +... +c.space.test:upsert({2}, {{'+', 2, 1}}, {2, 4, 'something'}) -- insert +--- +... +c.space.test:select{} +--- +- - [1, 3, 'string'] + - [2, 4, 'something'] +... +c.space.test:upsert({2}, {{'+', 3, 100500}}, {2, 4, 'nothing'}) -- wrong operation +--- +... +c.space.test:select{} +--- +- - [1, 3, 'string'] + - [2, 4, 'something'] +... +box.space.test:drop() +--- +... box.schema.user.revoke('guest', 'read,write,execute', 'universe') --- ... diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua index 0ea5f5b40e89a2dfc06d39d4fad8133fa47f7a9c..5942779adbd246d12bcaf679b7e9050bfbc8e92b 100644 --- a/test/box/net.box.test.lua +++ b/test/box/net.box.test.lua @@ -379,4 +379,18 @@ c = net:new(box.cfg.listen) r = c.space.test:select(nil, {limit=5000}) box.space.test:drop() +-- gh-970 gh-971 UPSERT over network +_ = box.schema.space.create('test') +_ = box.space.test:create_index('primary', {type = 'TREE', parts = {1,'NUM'}}) +_ = box.space.test:insert{1, 2, "string"} +c = net:new(box.cfg.listen) +c.space.test:select{} +c.space.test:upsert({1}, {{'+', 2, 1}}, {10, 20, 'nothing'}) -- common update +c.space.test:select{} +c.space.test:upsert({2}, {{'+', 2, 1}}, {2, 4, 'something'}) -- insert +c.space.test:select{} +c.space.test:upsert({2}, {{'+', 3, 100500}}, {2, 4, 'nothing'}) -- wrong operation +c.space.test:select{} +box.space.test:drop() + box.schema.user.revoke('guest', 'read,write,execute', 'universe')