[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