[PATCH v1 2/2] netbox: define formats for tuple from netbox
imeevma at tarantool.org
imeevma at tarantool.org
Mon Jun 10 13:02:24 MSK 2019
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
More information about the Tarantool-patches
mailing list