From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: imeevma@tarantool.org Subject: [PATCH v1 2/2] netbox: define formats for tuple from netbox Date: Mon, 10 Jun 2019 13:02:24 +0300 Message-Id: <82d7940c4420df06b6e905304f27797d4d348329.1560160282.git.imeevma@gmail.com> In-Reply-To: References: To: vdavydov.dev@gmail.com Cc: tarantool-patches@freelists.org List-ID: This patch creates tuple_formats for the tuples obtained through the netbox. Closes #2978 --- src/box/lua/net_box.c | 87 ++++++++++++++++++++++++++++++++++++++++++++--- src/box/lua/net_box.lua | 69 ++++++++++++++++++++++++++++--------- test/box/net.box.result | 77 +++++++++++++++++++++++++++++++++++++++++ test/box/net.box.test.lua | 20 +++++++++++ 4 files changed, 231 insertions(+), 22 deletions(-) diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c index 7484a86..fab4a8b 100644 --- a/src/box/lua/net_box.c +++ b/src/box/lua/net_box.c @@ -40,6 +40,7 @@ #include "box/xrow.h" #include "box/tuple.h" #include "box/execute.h" +#include "box/schema.h" #include "lua/msgpack.h" #include "third_party/base64.h" @@ -590,12 +591,12 @@ netbox_encode_execute(lua_State *L) * @param data MessagePack. */ static void -netbox_decode_data(struct lua_State *L, const char **data) +netbox_decode_data(struct lua_State *L, const char **data, uint32_t format_id) { + (void)format_id; uint32_t count = mp_decode_array(data); lua_createtable(L, count, 0); - struct tuple_format *format = - box_tuple_format_default(); + struct tuple_format *format = tuple_format_by_id(format_id); for (uint32_t j = 0; j < count; ++j) { const char *begin = *data; mp_next(data); @@ -608,6 +609,74 @@ netbox_decode_data(struct lua_State *L, const char **data) } } +static int +netbox_format_new(struct lua_State *L) +{ + if (lua_gettop(L) != 1 || lua_istable(L, 1) != 1) + return luaL_error(L, "Bad params!"); + + uint32_t count = lua_objlen(L, 1); + if (count == 0) { + lua_pushinteger(L, box_tuple_format_default()->id); + return 1; + } + size_t size = count * sizeof(struct field_def); + struct region *region = &fiber()->gc; + size_t region_svp = region_used(region); + struct field_def *fields = region_alloc(region, size); + if (fields == NULL) { + diag_set(OutOfMemory, size, "region_alloc", "fields"); + return luaT_error(L); + } + memset(fields, 0, size); + for (uint32_t i = 0; i < count; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, 1); + + lua_pushstring(L, "type"); + lua_gettable(L, -2); + size_t len; + const char *type_name = lua_tolstring(L, -1, &len); + lua_pop(L, 1); + fields[i].type = field_type_by_name(type_name, len); + + lua_pushstring(L, "name"); + lua_gettable(L, -2); + const char *name = lua_tolstring(L, -1, &len); + fields[i].name = region_alloc(region, len + 1); + memcpy(fields[i].name, name, len); + fields[i].name[len] = '\0'; + lua_pop(L, 1); + lua_pop(L, 1); + } + struct tuple_dictionary *dict = tuple_dictionary_new(fields, count); + if (dict == NULL) { + region_truncate(region, region_svp); + return luaT_error(L); + } + + struct tuple_format *format = + tuple_format_new(&box_tuple_format_default()->vtab, NULL, NULL, + 0, fields, count, 0, dict, false, false); + assert(format != NULL); + tuple_format_ref(format); + lua_pushinteger(L, format->id); + region_truncate(region, region_svp); + return 1; +} + +static int +netbox_format_delete(struct lua_State *L) +{ + int32_t format_id = luaL_checkinteger(L, 1); + if (format_id == box_tuple_format_default()->id) + return 0; + struct tuple_format *format = tuple_format_by_id(format_id); + assert(format != NULL); + tuple_format_unref(format); + return 0; +} + /** * Decode Tarantool response body consisting of single * IPROTO_DATA key into array of tuples. @@ -619,6 +688,11 @@ netbox_decode_select(struct lua_State *L) { uint32_t ctypeid; const char *data = *(const char **)luaL_checkcdata(L, 1, &ctypeid); + uint32_t format_id; + if (lua_gettop(L) == 2) + format_id = luaL_checkinteger(L, 2); + else + format_id = box_tuple_format_default()->id; assert(mp_typeof(*data) == MP_MAP); uint32_t map_size = mp_decode_map(&data); /* Until 2.0 body has no keys except DATA. */ @@ -627,7 +701,7 @@ netbox_decode_select(struct lua_State *L) uint32_t key = mp_decode_uint(&data); assert(key == IPROTO_DATA); (void) key; - netbox_decode_data(L, &data); + netbox_decode_data(L, &data, format_id); *(const char **)luaL_pushcdata(L, ctypeid) = data; return 2; } @@ -716,7 +790,8 @@ netbox_decode_execute(struct lua_State *L) uint32_t key = mp_decode_uint(&data); switch(key) { case IPROTO_DATA: - netbox_decode_data(L, &data); + netbox_decode_data(L, &data, + box_tuple_format_default()->id); rows_index = i - map_size; break; case IPROTO_METADATA: @@ -766,6 +841,8 @@ luaopen_net_box(struct lua_State *L) { "communicate", netbox_communicate }, { "decode_select", netbox_decode_select }, { "decode_execute", netbox_decode_execute }, + { "_format_new", netbox_format_new }, + { "_format_delete", netbox_format_delete }, { NULL, NULL} }; /* luaL_register_module polutes _G */ diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua index 8d42fb4..26ff7ff 100644 --- a/src/box/lua/net_box.lua +++ b/src/box/lua/net_box.lua @@ -63,12 +63,22 @@ local function decode_data(raw_data) local response, raw_end = decode(raw_data) return response[IPROTO_DATA_KEY], raw_end end -local function decode_tuple(raw_data) - local response, raw_end = internal.decode_select(raw_data) +local function decode_tuple(raw_data, raw_data_end, opts) + local response, raw_end + if opts ~= nil and opts.format_id ~= nil then + response, raw_end = internal.decode_select(raw_data, opts.format_id) + else + response, raw_end = internal.decode_select(raw_data) + end return response[1], raw_end end -local function decode_get(raw_data) - local body, raw_end = internal.decode_select(raw_data) +local function decode_get(raw_data, raw_data_end, opts) + local body, raw_end + if opts ~= nil and opts.format_id ~= nil then + body, raw_end = internal.decode_select(raw_data, opts.format_id) + else + body, raw_end = internal.decode_select(raw_data) + end if body[2] then return nil, raw_end, box.error.MORE_THAN_ONE_TUPLE end @@ -82,6 +92,15 @@ local function decode_push(raw_data) local response, raw_end = decode(raw_data) return response[IPROTO_DATA_KEY][1], raw_end end +local function decode_select(raw_data, raw_data_end, opts) + if opts ~= nil and opts.format_id ~= nil then + return internal.decode_select(raw_data, opts.format_id) + end + return internal.decode_select(raw_data) +end +local function decode_execute(raw_data, raw_data_end) + return internal.decode_execute(raw_data) +end local function encode_call(send_buf, id, method_args) return internal.encode_call(send_buf, id, method_args.func_name, @@ -157,7 +176,7 @@ local method_encoder = { local method_decoder = { ping = decode_nil, - call_16 = internal.decode_select, + call_16 = decode_select, call_17 = decode_data, eval = decode_data, insert = decode_tuple, @@ -165,8 +184,8 @@ local method_decoder = { delete = decode_tuple, update = decode_tuple, upsert = decode_nil, - select = internal.decode_select, - execute = internal.decode_execute, + select = decode_select, + execute = decode_execute, get = decode_get, min = decode_get, max = decode_get, @@ -630,14 +649,15 @@ local function create_transport(host, port, user, password, callback, -- Decode xrow.body[DATA] to Lua objects if status == IPROTO_OK_KEY then request.response, real_end, request.errno = - method_decoder[request.method](body_rpos, body_end) + method_decoder[request.method](body_rpos, body_end, + request.method_args) assert(real_end == body_end, "invalid body length") requests[id] = nil request.id = nil else local msg msg, real_end, request.errno = - method_decoder.push(body_rpos, body_end) + method_decoder.push(body_rpos, body_end, request.method_args) assert(real_end == body_end, "invalid body length") request.on_push(request.on_push_ctx, msg) end @@ -1085,6 +1105,14 @@ end function remote_methods:close() check_remote_arg(self, 'close') + if (self.space ~= nil and type(self.space) == 'table') then + for _,space in pairs(self.space) do + if space.format_id ~= nil then + internal._format_delete(space._format_id) + space.format_id = nil + end + end + end self._transport.stop() end @@ -1274,6 +1302,7 @@ function remote_methods:_install_schema(schema_version, spaces, indices, s.index = {} s.temporary = false s._format = format + s._format_id = internal._format_new(format) s.connection = self if #space > 5 then local opts = space[6] @@ -1391,13 +1420,15 @@ space_metatable = function(remote) function methods:insert(tuple, opts) check_space_arg(self, 'insert') - local method_args = {space_id=self.id, tuple=tuple} + local method_args = {space_id=self.id, tuple=tuple, + format_id=self._format_id} return remote:_request('insert', opts, method_args) end function methods:replace(tuple, opts) check_space_arg(self, 'replace') - local method_args = {space_id=self.id, tuple=tuple} + local method_args = {space_id=self.id, tuple=tuple, + format_id=self._format_id} return remote:_request('replace', opts, method_args) end @@ -1453,7 +1484,7 @@ index_metatable = function(remote) local limit = tonumber(opts and opts.limit) or 0xFFFFFFFF local method_args = {space_id=self.space.id, index_id=self.id, iterator=iterator, offset=offset, limit=limit, - key=key} + key=key, format_id=self.space._format_id} return (remote:_request('select', opts, method_args)) end @@ -1463,7 +1494,8 @@ index_metatable = function(remote) error("index:get() doesn't support `buffer` argument") end local method_args = {space_id=self.space.id, index_id=self.id, - iterator=box.index.EQ, offset=0, limit=2, key=key} + iterator=box.index.EQ, offset=0, limit=2, key=key, + format_id=self.space._format_id} return nothing_or_data(remote:_request('get', opts, method_args)) end @@ -1473,7 +1505,8 @@ index_metatable = function(remote) error("index:min() doesn't support `buffer` argument") end local method_args = {space_id=self.space.id, index_id=self.id, - iterator=box.index.GE, offset=0, limit=1, key=key} + iterator=box.index.GE, offset=0, limit=1, key=key, + format_id=self.space._format_id} return nothing_or_data(remote:_request('min', opts, method_args)) end @@ -1483,7 +1516,8 @@ index_metatable = function(remote) error("index:max() doesn't support `buffer` argument") end local method_args = {space_id=self.space.id, index_id=self.id, - iterator=box.index.LE, offset=0, limit=1, key=key} + iterator=box.index.LE, offset=0, limit=1, key=key, + format_id=self.space._format_id} return nothing_or_data(remote:_request('max', opts, method_args)) end @@ -1500,14 +1534,15 @@ index_metatable = function(remote) function methods:delete(key, opts) check_index_arg(self, 'delete') - local method_args = {space_id=self.space.id, index_id=self.id, key=key} + local method_args = {space_id=self.space.id, index_id=self.id, key=key, + format_id=self.space._format_id} return nothing_or_data(remote:_request('delete', opts, method_args)) end function methods:update(key, oplist, opts) check_index_arg(self, 'update') local method_args = {space_id=self.space.id, index_id=self.id, key=key, - oplist=oplist} + oplist=oplist, format_id=self.space._format_id} return nothing_or_data(remote:_request('update', opts, method_args)) end diff --git a/test/box/net.box.result b/test/box/net.box.result index 451c31d..da40a3d 100644 --- a/test/box/net.box.result +++ b/test/box/net.box.result @@ -3572,6 +3572,83 @@ box.schema.func.drop('change_schema') --- ... -- +-- gh-2978: field names for tuples received from netbox. +-- +_ = box.schema.create_space("named", {format = {{name = "id"}, {name="abc"}}}) +--- +... +_ = box.space.named:create_index('id', {parts = {{1, 'unsigned'}}}) +--- +... +box.space.named:insert({1, 1}) +--- +- [1, 1] +... +box.schema.user.grant('guest', 'read, write, execute', 'universe') +--- +... +cn = net.connect(box.cfg.listen) +--- +... +s = cn.space.named +--- +... +s:get{1}.id +--- +- 1 +... +s:get{1}:tomap() +--- +- 1: 1 + 2: 1 + abc: 1 + id: 1 +... +s:insert{2,3}:tomap() +--- +- 1: 2 + 2: 3 + abc: 3 + id: 2 +... +s:replace{2,14}:tomap() +--- +- 1: 2 + 2: 14 + abc: 14 + id: 2 +... +s:update(1, {{'+', 2, 10}}):tomap() +--- +- 1: 1 + 2: 11 + abc: 11 + id: 1 +... +s:select()[1]:tomap() +--- +- 1: 1 + 2: 11 + abc: 11 + id: 1 +... +s:delete({2}):tomap() +--- +- 1: 2 + 2: 14 + abc: 14 + id: 2 +... +cn:close() +--- +... +box.space.named:drop() +--- +... +box.schema.user.revoke('guest', 'read, write, execute', 'universe') +--- +... +-- -- gh-3400: long-poll input discard must not touch event loop of -- a closed connection. -- diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua index 6651b58..bba4eb5 100644 --- a/test/box/net.box.test.lua +++ b/test/box/net.box.test.lua @@ -1433,6 +1433,26 @@ box.space.test3:drop() box.schema.func.drop('change_schema') -- +-- gh-2978: field names for tuples received from netbox. +-- +_ = box.schema.create_space("named", {format = {{name = "id"}, {name="abc"}}}) +_ = box.space.named:create_index('id', {parts = {{1, 'unsigned'}}}) +box.space.named:insert({1, 1}) +box.schema.user.grant('guest', 'read, write, execute', 'universe') +cn = net.connect(box.cfg.listen) +s = cn.space.named +s:get{1}.id +s:get{1}:tomap() +s:insert{2,3}:tomap() +s:replace{2,14}:tomap() +s:update(1, {{'+', 2, 10}}):tomap() +s:select()[1]:tomap() +s:delete({2}):tomap() +cn:close() +box.space.named:drop() +box.schema.user.revoke('guest', 'read, write, execute', 'universe') + +-- -- gh-3400: long-poll input discard must not touch event loop of -- a closed connection. -- -- 2.7.4