Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple
@ 2018-03-28 17:51 Kirill Shcherbatov
  2018-03-28 18:28 ` Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-03-28 17:51 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov

Closes #3282
---
 src/box/lua/schema.lua          | 14 +++++++
 src/box/lua/space.cc            | 86 +++++++++++++++++++++++++++++++++++++++++
 test/box/space_frommap.result   | 76 ++++++++++++++++++++++++++++++++++++
 test/box/space_frommap.test.lua | 20 ++++++++++
 4 files changed, 196 insertions(+)
 create mode 100644 test/box/space_frommap.result
 create mode 100644 test/box/space_frommap.test.lua

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 30c6bc6..afe81e8 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1419,6 +1419,20 @@ function box.schema.space.bless(space)
         end
         builtin.space_run_triggers(s, yesno)
     end
+    space_mt.frommap = function(space, map, options)
+        check_space_arg(space, 'frommap')
+        check_space_exists(space)
+        if map == nil then
+            -- allow C module process error itself
+            box.internal.space._table_frommap(space)
+        end
+        local ret = box.internal.space._table_frommap(space, map)
+        if options and options['tuple'] == true then
+            ret = box.tuple.new(ret)
+        end
+        return ret
+    end
+    space_mt._ptr = box.internal.space._ptr
     space_mt.__index = space_mt
 
     setmetatable(space, space_mt)
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 29a9aca..918f8f0 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -32,6 +32,7 @@
 #include "box/lua/tuple.h"
 #include "lua/utils.h"
 #include "lua/trigger.h"
+#include "box/schema.h"
 
 extern "C" {
 	#include <lua.h>
@@ -174,6 +175,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushstring(L, space->def->engine_name);
 	lua_settable(L, i);
 
+	/* space.schema_version */
+	lua_pushstring(L, "schema_version");
+	luaL_pushuint64(L, box_schema_version());
+	lua_settable(L, i);
+
+	/* space._space */
+	lua_pushstring(L, "_space");
+	lua_pushlightuserdata(L, space);
+	lua_settable(L, i);
+
 	lua_pushstring(L, "enabled");
 	lua_pushboolean(L, space_index(space, 0) != 0);
 	lua_settable(L, i);
@@ -386,6 +397,73 @@ static struct trigger on_alter_space_in_lua = {
 	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
 };
 
+static int
+lbox_space_ptr_cached(struct lua_State *L)
+{
+	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
+		luaL_error(L, "Usage: space:_ptr_cached()");
+
+	// take care about stack to call this function in frommap directly
+	lua_getfield(L, 1, "schema_version");
+	uint64_t schema_version = luaL_touint64(L, -1);
+	lua_pop(L, 1);
+	uint64_t global_shema_version = box_schema_version();
+
+	void *space = nullptr;
+	if (schema_version == global_shema_version) {
+		lua_getfield(L, 1, "_space");
+		space = (void *)lua_topointer(L, -1);
+		lua_pop(L, 1);
+	} else {
+		lua_getfield(L, 1, "id");
+		uint32_t id = luaL_touint64(L, -1);
+		lua_pop(L, 1);
+		space = space_by_id(id);
+
+		luaL_pushuint64(L, global_shema_version);
+		lua_setfield(L, 1, "schema_version");
+
+		lua_pushlightuserdata(L, space);
+		lua_setfield(L, 1, "_space");
+	}
+
+	lua_pushlightuserdata(L, space);
+	return 1;
+}
+
+static int
+lbox_space_table_frommap(struct lua_State *L)
+{
+	int res = 0;
+	struct tuple_dictionary *dict = nullptr;
+	struct space *space = nullptr;
+	int argc = lua_gettop(L);
+	if (argc != 2)
+		goto error;
+
+	res = lbox_space_ptr_cached(L);
+	space = (struct space *)lua_topointer(L, -1);
+	lua_pop(L, res);
+
+	dict = space->format->dict;
+	lua_createtable(L, space->def->field_count, 0);
+
+	lua_pushnil(L);
+	while (lua_next(L, 2) != 0) {
+		uint32_t fieldno;
+		size_t key_len;
+		const char *key = lua_tolstring(L, -2, &key_len);
+		uint32_t key_hash = lua_hashstring(L, -2);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
+			luaL_error(L, "Unknown field '%s'", key);
+		lua_rawseti(L, -3, fieldno+1);
+	}
+	return 1;
+error:
+	luaL_error(L, "Usage: space:frommap(map, opts)");
+	return 1;
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -464,4 +542,12 @@ box_lua_space_init(struct lua_State *L)
 	lua_pushnumber(L, VCLOCK_MAX);
 	lua_setfield(L, -2, "REPLICA_MAX");
 	lua_pop(L, 2); /* box, schema */
+
+	static const struct luaL_Reg space_internal_lib[] = {
+		{"_table_frommap", lbox_space_table_frommap},
+		{"_ptr", lbox_space_ptr_cached},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.internal.space", space_internal_lib);
+	lua_pop(L, 1);
 }
diff --git a/test/box/space_frommap.result b/test/box/space_frommap.result
new file mode 100644
index 0000000..811aa60
--- /dev/null
+++ b/test/box/space_frommap.result
@@ -0,0 +1,76 @@
+-- box.tuple test
+env = require('test_run')
+---
+...
+test_run = env.new()
+---
+...
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
+---
+- true
+...
+format = {}
+---
+...
+format[1] = {name = 'aaa', type = 'unsigned'}
+---
+...
+format[2] = {name = 'bbb', type = 'unsigned'}
+---
+...
+format[3] = {name = 'ccc', type = 'unsigned'}
+---
+...
+format[4] = {name = 'ddd', type = 'unsigned'}
+---
+...
+subscriber = box.schema.space.create('subscriber', {format = format})
+---
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
+---
+- - 2
+  - 3
+  - null
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+---
+- error: 'builtin/box/schema.lua..."]:<line>: Unknown field ''eee'''
+...
+subscriber:frommap()
+---
+- error: 'builtin/box/schema.lua..."]:<line>: Usage: space:frommap(map, opts)'
+...
+subscriber:frommap({})
+---
+- []
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
+---
+- [2, 4, 3, 1]
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:drop()
+---
+...
diff --git a/test/box/space_frommap.test.lua b/test/box/space_frommap.test.lua
new file mode 100644
index 0000000..4760b3a
--- /dev/null
+++ b/test/box/space_frommap.test.lua
@@ -0,0 +1,20 @@
+-- box.tuple test
+env = require('test_run')
+test_run = env.new()
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
+
+format = {}
+format[1] = {name = 'aaa', type = 'unsigned'}
+format[2] = {name = 'bbb', type = 'unsigned'}
+format[3] = {name = 'ccc', type = 'unsigned'}
+format[4] = {name = 'ddd', type = 'unsigned'}
+subscriber = box.schema.space.create('subscriber', {format = format})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+subscriber:frommap()
+subscriber:frommap({})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+subscriber:drop()
-- 
2.7.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-03-28 17:51 [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple Kirill Shcherbatov
@ 2018-03-28 18:28 ` Kirill Shcherbatov
  2018-03-29  7:36   ` [tarantool-patches] " Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-03-28 18:28 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov

Closes #3282
---
 src/box/lua/schema.lua        |   2 +
 src/box/lua/space.cc          | 100 ++++++++++++++++++++++++++++++++++++++++++
 src/box/lua/tuple.c           |   2 +-
 src/box/lua/tuple.h           |   3 ++
 test/box/space_frommap.result |  76 ++++++++++++++++++++++++++++++++
 5 files changed, 182 insertions(+), 1 deletion(-)
 create mode 100644 test/box/space_frommap.result

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 30c6bc6..740b885 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1419,6 +1419,8 @@ function box.schema.space.bless(space)
         end
         builtin.space_run_triggers(s, yesno)
     end
+    space_mt.frommap = box.internal.space.frommap
+    space_mt._ptr = box.internal.space._ptr
     space_mt.__index = space_mt
 
     setmetatable(space, space_mt)
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 29a9aca..a8cfb14 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -32,6 +32,7 @@
 #include "box/lua/tuple.h"
 #include "lua/utils.h"
 #include "lua/trigger.h"
+#include "box/schema.h"
 
 extern "C" {
 	#include <lua.h>
@@ -174,6 +175,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushstring(L, space->def->engine_name);
 	lua_settable(L, i);
 
+	/* space.schema_version */
+	lua_pushstring(L, "schema_version");
+	luaL_pushuint64(L, box_schema_version());
+	lua_settable(L, i);
+
+	/* space._space */
+	lua_pushstring(L, "_space");
+	lua_pushlightuserdata(L, space);
+	lua_settable(L, i);
+
 	lua_pushstring(L, "enabled");
 	lua_pushboolean(L, space_index(space, 0) != 0);
 	lua_settable(L, i);
@@ -386,6 +397,87 @@ static struct trigger on_alter_space_in_lua = {
 	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
 };
 
+static int
+lbox_space_ptr_cached(struct lua_State *L)
+{
+	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
+		luaL_error(L, "Usage: space:_ptr_cached()");
+
+	// take care about stack to call this function in frommap directly
+	lua_getfield(L, 1, "schema_version");
+	uint64_t schema_version = luaL_touint64(L, -1);
+	lua_pop(L, 1);
+	uint64_t global_shema_version = box_schema_version();
+
+	void *space = nullptr;
+	if (schema_version == global_shema_version) {
+		lua_getfield(L, 1, "_space");
+		space = (void *)lua_topointer(L, -1);
+		lua_pop(L, 1);
+	} else {
+		lua_getfield(L, 1, "id");
+		uint32_t id = luaL_touint64(L, -1);
+		lua_pop(L, 1);
+		space = space_by_id(id);
+
+		luaL_pushuint64(L, global_shema_version);
+		lua_setfield(L, 1, "schema_version");
+
+		lua_pushlightuserdata(L, space);
+		lua_setfield(L, 1, "_space");
+	}
+
+	lua_pushlightuserdata(L, space);
+	return 1;
+}
+
+static int
+lbox_space_frommap(struct lua_State *L)
+{
+	int res = 0;
+	struct tuple_dictionary *dict = nullptr;
+	struct space *space = nullptr;
+	int argc = lua_gettop(L);
+	bool tuple = false;
+	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
+		goto error;
+	if (argc == 3) {
+		if (!lua_istable(L, 3))
+				goto error;
+		lua_getfield(L, 3, "tuple");
+		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
+				goto error;
+		tuple = lua_toboolean(L, -1);
+	}
+
+	res = lbox_space_ptr_cached(L);
+	space = (struct space *)lua_topointer(L, -1);
+	lua_pop(L, res);
+
+	dict = space->format->dict;
+	lua_createtable(L, space->def->field_count, 0);
+
+	lua_pushnil(L);
+	while (lua_next(L, 2) != 0) {
+		uint32_t fieldno;
+		size_t key_len;
+		const char *key = lua_tolstring(L, -2, &key_len);
+		uint32_t key_hash = lua_hashstring(L, -2);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
+			luaL_error(L, "Unknown field '%s'", key);
+		lua_rawseti(L, -3, fieldno+1);
+	}
+	if (!tuple)
+		return 1;
+
+	lua_replace(L, 1);
+	lua_settop(L, 1);
+	return lbox_tuple_new(L);
+error:
+	luaL_error(L, "Usage: space:frommap(map, opts)");
+	return 1;
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -464,4 +556,12 @@ box_lua_space_init(struct lua_State *L)
 	lua_pushnumber(L, VCLOCK_MAX);
 	lua_setfield(L, -2, "REPLICA_MAX");
 	lua_pop(L, 2); /* box, schema */
+
+	static const struct luaL_Reg space_internal_lib[] = {
+		{"frommap", lbox_space_frommap},
+		{"_ptr", lbox_space_ptr_cached},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.internal.space", space_internal_lib);
+	lua_pop(L, 1);
 }
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 7ca4299..92a7185 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
 	return *(struct tuple **) data;
 }
 
-static int
+int
 lbox_tuple_new(lua_State *L)
 {
 	int argc = lua_gettop(L);
diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
index 0965644..5d7062e 100644
--- a/src/box/lua/tuple.h
+++ b/src/box/lua/tuple.h
@@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
 
 /** \endcond public */
 
+int
+lbox_tuple_new(struct lua_State *L);
+
 static inline int
 luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
 {
diff --git a/test/box/space_frommap.result b/test/box/space_frommap.result
new file mode 100644
index 0000000..884d78c
--- /dev/null
+++ b/test/box/space_frommap.result
@@ -0,0 +1,76 @@
+-- box.tuple test
+env = require('test_run')
+---
+...
+test_run = env.new()
+---
+...
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
+---
+- true
+...
+format = {}
+---
+...
+format[1] = {name = 'aaa', type = 'unsigned'}
+---
+...
+format[2] = {name = 'bbb', type = 'unsigned'}
+---
+...
+format[3] = {name = 'ccc', type = 'unsigned'}
+---
+...
+format[4] = {name = 'ddd', type = 'unsigned'}
+---
+...
+subscriber = box.schema.space.create('subscriber', {format = format})
+---
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
+---
+- - 2
+  - 3
+  - null
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+---
+- error: Unknown field 'eee'
+...
+subscriber:frommap()
+---
+- error: 'Usage: space:frommap(map, opts)'
+...
+subscriber:frommap({})
+---
+- []
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
+---
+- [2, 4, 3, 1]
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+subscriber:drop()
+---
+...
-- 
2.7.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-03-28 18:28 ` Kirill Shcherbatov
@ 2018-03-29  7:36   ` Kirill Shcherbatov
  2018-03-29 10:14     ` v.shpilevoy
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-03-29  7:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

1. Deleted redundant lua_pop in  lbox_space_frommap
2. Included forgotten new frommap test case

diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index a8cfb14..42eaf79 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -434,7 +434,6 @@ lbox_space_ptr_cached(struct lua_State *L)
  static int
  lbox_space_frommap(struct lua_State *L)
  {
-	int res = 0;
  	struct tuple_dictionary *dict = nullptr;
  	struct space *space = nullptr;
  	int argc = lua_gettop(L);
@@ -450,9 +449,8 @@ lbox_space_frommap(struct lua_State *L)
  		tuple = lua_toboolean(L, -1);
  	}

-	res = lbox_space_ptr_cached(L);
+	lbox_space_ptr_cached(L);
  	space = (struct space *)lua_topointer(L, -1);
-	lua_pop(L, res);

  	dict = space->format->dict;
  	lua_createtable(L, space->def->field_count, 0);
diff --git a/test/box/space_frommap.test.lua 
b/test/box/space_frommap.test.lua
new file mode 100644
index 0000000..4760b3a
--- /dev/null
+++ b/test/box/space_frommap.test.lua
@@ -0,0 +1,20 @@
+-- box.tuple test
+env = require('test_run')
+test_run = env.new()
+test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to 
'.lua...\"]:<line>: '")
+
+format = {}
+format[1] = {name = 'aaa', type = 'unsigned'}
+format[2] = {name = 'bbb', type = 'unsigned'}
+format[3] = {name = 'ccc', type = 'unsigned'}
+format[4] = {name = 'ddd', type = 'unsigned'}
+subscriber = box.schema.space.create('subscriber', {format = format})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+subscriber:frommap()
+subscriber:frommap({})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+subscriber:drop()


On 28.03.2018 21:28, Kirill Shcherbatov wrote:
> Closes #3282
> ---
>   src/box/lua/schema.lua        |   2 +
>   src/box/lua/space.cc          | 100 ++++++++++++++++++++++++++++++++++++++++++
>   src/box/lua/tuple.c           |   2 +-
>   src/box/lua/tuple.h           |   3 ++
>   test/box/space_frommap.result |  76 ++++++++++++++++++++++++++++++++
>   5 files changed, 182 insertions(+), 1 deletion(-)
>   create mode 100644 test/box/space_frommap.result
> 
> diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
> index 30c6bc6..740b885 100644
> --- a/src/box/lua/schema.lua
> +++ b/src/box/lua/schema.lua
> @@ -1419,6 +1419,8 @@ function box.schema.space.bless(space)
>           end
>           builtin.space_run_triggers(s, yesno)
>       end
> +    space_mt.frommap = box.internal.space.frommap
> +    space_mt._ptr = box.internal.space._ptr
>       space_mt.__index = space_mt
>   
>       setmetatable(space, space_mt)
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 29a9aca..a8cfb14 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -32,6 +32,7 @@
>   #include "box/lua/tuple.h"
>   #include "lua/utils.h"
>   #include "lua/trigger.h"
> +#include "box/schema.h"
>   
>   extern "C" {
>   	#include <lua.h>
> @@ -174,6 +175,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
>   	lua_pushstring(L, space->def->engine_name);
>   	lua_settable(L, i);
>   
> +	/* space.schema_version */
> +	lua_pushstring(L, "schema_version");
> +	luaL_pushuint64(L, box_schema_version());
> +	lua_settable(L, i);
> +
> +	/* space._space */
> +	lua_pushstring(L, "_space");
> +	lua_pushlightuserdata(L, space);
> +	lua_settable(L, i);
> +
>   	lua_pushstring(L, "enabled");
>   	lua_pushboolean(L, space_index(space, 0) != 0);
>   	lua_settable(L, i);
> @@ -386,6 +397,87 @@ static struct trigger on_alter_space_in_lua = {
>   	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
>   };
>   
> +static int
> +lbox_space_ptr_cached(struct lua_State *L)
> +{
> +	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
> +		luaL_error(L, "Usage: space:_ptr_cached()");
> +
> +	// take care about stack to call this function in frommap directly
> +	lua_getfield(L, 1, "schema_version");
> +	uint64_t schema_version = luaL_touint64(L, -1);
> +	lua_pop(L, 1);
> +	uint64_t global_shema_version = box_schema_version();
> +
> +	void *space = nullptr;
> +	if (schema_version == global_shema_version) {
> +		lua_getfield(L, 1, "_space");
> +		space = (void *)lua_topointer(L, -1);
> +		lua_pop(L, 1);
> +	} else {
> +		lua_getfield(L, 1, "id");
> +		uint32_t id = luaL_touint64(L, -1);
> +		lua_pop(L, 1);
> +		space = space_by_id(id);
> +
> +		luaL_pushuint64(L, global_shema_version);
> +		lua_setfield(L, 1, "schema_version");
> +
> +		lua_pushlightuserdata(L, space);
> +		lua_setfield(L, 1, "_space");
> +	}
> +
> +	lua_pushlightuserdata(L, space);
> +	return 1;
> +}
> +
> +static int
> +lbox_space_frommap(struct lua_State *L)
> +{
> +	int res = 0;
> +	struct tuple_dictionary *dict = nullptr;
> +	struct space *space = nullptr;
> +	int argc = lua_gettop(L);
> +	bool tuple = false;
> +	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
> +		goto error;
> +	if (argc == 3) {
> +		if (!lua_istable(L, 3))
> +				goto error;
> +		lua_getfield(L, 3, "tuple");
> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
> +				goto error;
> +		tuple = lua_toboolean(L, -1);
> +	}
> +
> +	res = lbox_space_ptr_cached(L);
> +	space = (struct space *)lua_topointer(L, -1);
> +	lua_pop(L, res);
> +
> +	dict = space->format->dict;
> +	lua_createtable(L, space->def->field_count, 0);
> +
> +	lua_pushnil(L);
> +	while (lua_next(L, 2) != 0) {
> +		uint32_t fieldno;
> +		size_t key_len;
> +		const char *key = lua_tolstring(L, -2, &key_len);
> +		uint32_t key_hash = lua_hashstring(L, -2);
> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
> +			luaL_error(L, "Unknown field '%s'", key);
> +		lua_rawseti(L, -3, fieldno+1);
> +	}
> +	if (!tuple)
> +		return 1;
> +
> +	lua_replace(L, 1);
> +	lua_settop(L, 1);
> +	return lbox_tuple_new(L);
> +error:
> +	luaL_error(L, "Usage: space:frommap(map, opts)");
> +	return 1;
> +}
> +
>   void
>   box_lua_space_init(struct lua_State *L)
>   {
> @@ -464,4 +556,12 @@ box_lua_space_init(struct lua_State *L)
>   	lua_pushnumber(L, VCLOCK_MAX);
>   	lua_setfield(L, -2, "REPLICA_MAX");
>   	lua_pop(L, 2); /* box, schema */
> +
> +	static const struct luaL_Reg space_internal_lib[] = {
> +		{"frommap", lbox_space_frommap},
> +		{"_ptr", lbox_space_ptr_cached},
> +		{NULL, NULL}
> +	};
> +	luaL_register(L, "box.internal.space", space_internal_lib);
> +	lua_pop(L, 1);
>   }
> diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
> index 7ca4299..92a7185 100644
> --- a/src/box/lua/tuple.c
> +++ b/src/box/lua/tuple.c
> @@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
>   	return *(struct tuple **) data;
>   }
>   
> -static int
> +int
>   lbox_tuple_new(lua_State *L)
>   {
>   	int argc = lua_gettop(L);
> diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
> index 0965644..5d7062e 100644
> --- a/src/box/lua/tuple.h
> +++ b/src/box/lua/tuple.h
> @@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
>   
>   /** \endcond public */
>   
> +int
> +lbox_tuple_new(struct lua_State *L);
> +
>   static inline int
>   luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
>   {
> diff --git a/test/box/space_frommap.result b/test/box/space_frommap.result
> new file mode 100644
> index 0000000..884d78c
> --- /dev/null
> +++ b/test/box/space_frommap.result
> @@ -0,0 +1,76 @@
> +-- box.tuple test
> +env = require('test_run')
> +---
> +...
> +test_run = env.new()
> +---
> +...
> +test_run:cmd("push filter ".."'\\.lua.*:[0-9]+: ' to '.lua...\"]:<line>: '")
> +---
> +- true
> +...
> +format = {}
> +---
> +...
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +---
> +...
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +---
> +...
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +---
> +...
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +---
> +...
> +subscriber = box.schema.space.create('subscriber', {format = format})
> +---
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
> +---
> +- - 2
> +  - 3
> +  - null
> +  - 1
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +---
> +- error: Unknown field 'eee'
> +...
> +subscriber:frommap()
> +---
> +- error: 'Usage: space:frommap(map, opts)'
> +...
> +subscriber:frommap({})
> +---
> +- []
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
> +---
> +- [2, 4, 3, 1]
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +subscriber:drop()
> +---
> +...
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-03-29  7:36   ` [tarantool-patches] " Kirill Shcherbatov
@ 2018-03-29 10:14     ` v.shpilevoy
  2018-04-02 10:26       ` Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: v.shpilevoy @ 2018-03-29 10:14 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches

[-- Attachment #1: Type: text/plain, Size: 2986 bytes --]

See below 10 comments.

> diff --git a/src/box/lua/space.cc <http://space.cc/> b/src/box/lua/space.cc <http://space.cc/>
> index 29a9aca..a8cfb14 100644
> --- a/src/box/lua/space.cc <http://space.cc/>
> +++ b/src/box/lua/space.cc <http://space.cc/>
> @@ -32,6 +32,7 @@
> #include "box/lua/tuple.h"
> #include "lua/utils.h"
> #include "lua/trigger.h"
> +#include "box/schema.h"
> 
> extern "C" {
> 	#include <lua.h>
> @@ -174,6 +175,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
> 	lua_pushstring(L, space->def->engine_name);
> 	lua_settable(L, i);
> 
> +	/* space.schema_version */
> +	lua_pushstring(L, "schema_version");
> +	luaL_pushuint64(L, box_schema_version());
> +	lua_settable(L, i);

1. Lets name it '_schema_version' - it is internal field.

> +
> +	/* space._space */
> +	lua_pushstring(L, "_space");
> +	lua_pushlightuserdata(L, space);
> +	lua_settable(L, i);

2. Please, find a better name for the pointer. For example, 'ptr' - then it
will be accessed as space.ptr, that looks slightly better than space._space.

> +static int
> +lbox_space_ptr_cached(struct lua_State *L)
> +{

3. Add a comment, what this function does, why, and what arguments on a stack
it expects, what and home many values returns.

> +	// take care about stack to call this function in frommap directly
> +	lua_getfield(L, 1, "schema_version");

4. A comment seems to be not linked with the code below. And please, do not
use '//' comments. In our code we use '/* ... */' for comments inside a
function. Note, that comment max length is 66 symbols, including indentation
before a comment.

> +
> +	void *space = nullptr;
> +	if (schema_version == global_shema_version) {
> +

5. Do not use nullptr. Everywhere in Tarantool code only NULL is used.

> +	bool tuple = false;

6. Lets return tuple by default, and name the option 'table' instead of
tuple. If a user wants a table, then he must pass the option {table = true}.

> +	if (argc == 3) {
> +		if (!lua_istable(L, 3))
> +				goto error;
> +		lua_getfield(L, 3, "tuple");
> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
> +				goto error;
> +		tuple = lua_toboolean(L, -1);
> +	}
> +

7. Double tabs prior to 'goto error'.

> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
> +			luaL_error(L, "Unknown field '%s'", key);


8. According to our Lua error returning convention, you can raise an error either
on incorrect usage (bad argument type, for example), or on OOM(Out Of Memory)
errors. On other errors you must return two values: nil and error object or
description. In this example you must return nil and "Unknown field '%s'" string.

> +	lua_replace(L, 1);
> +	lua_settop(L, 1);
> +	return lbox_tuple_new(L);

9. How about a comment what is happening here?

10. Add a test on nil and box.NULL fields. And test, that a result of
frommap() actually can be inserted or replaced into a space.



[-- Attachment #2: Type: text/html, Size: 15052 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-03-29 10:14     ` v.shpilevoy
@ 2018-04-02 10:26       ` Kirill Shcherbatov
  2018-04-02 11:19         ` v.shpilevoy
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-02 10:26 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

Comments accounted.

On 29.03.2018 13:14, v.shpilevoy@tarantool.org wrote:
> See below 10 comments.
> 
>> diff --git a/src/box/lua/space.cc 
>> <http://space.cc> b/src/box/lua/space.cc <http://space.cc>
>> index 29a9aca..a8cfb14 100644
>> --- a/src/box/lua/space.cc <http://space.cc>
>> +++ b/src/box/lua/space.cc <http://space.cc>
>> @@ -32,6 +32,7 @@
>> #include "box/lua/tuple.h"
>> #include "lua/utils.h"
>> #include "lua/trigger.h"
>> +#include "box/schema.h"
>>
>> extern "C" {
>> #include <lua.h>
>> @@ -174,6 +175,16 @@ lbox_fillspace(struct lua_State *L, struct space 
>> *space, int i)
>> lua_pushstring(L, space->def->engine_name);
>> lua_settable(L, i);
>>
>> +/* space.schema_version */
>> +lua_pushstring(L, "schema_version");
>> +luaL_pushuint64(L, box_schema_version());
>> +lua_settable(L, i);
> 
> 1. Lets name it '_schema_version' - it is internal field.
> 
>> +
>> +/* space._space */
>> +lua_pushstring(L, "_space");
>> +lua_pushlightuserdata(L, space);
>> +lua_settable(L, i);
> 
> 2. Please, find a better name for the pointer. For example, 'ptr' - then it
> will be accessed as space.ptr, that looks slightly better than space._space.
> 
>> +static int
>> +lbox_space_ptr_cached(struct lua_State *L)
>> +{
> 
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 740b885..c2645f7 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1420,7 +1420,7 @@ function box.schema.space.bless(space)
          builtin.space_run_triggers(s, yesno)
      end
      space_mt.frommap = box.internal.space.frommap
-    space_mt._ptr = box.internal.space._ptr
+    space_mt._cptr = box.internal.space._cptr
      space_mt.__index = space_mt

      setmetatable(space, space_mt)
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 42eaf79..f8583a2 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -176,12 +176,12 @@ lbox_fillspace(struct lua_State *L, struct space 
*space, int i)
  	lua_settable(L, i);

  	/* space.schema_version */
-	lua_pushstring(L, "schema_version");
+	lua_pushstring(L, "_schema_version");
  	luaL_pushuint64(L, box_schema_version());
  	lua_settable(L, i);

-	/* space._space */
-	lua_pushstring(L, "_space");
+	/* space._cptr */
+	lua_pushstring(L, "_cptr");
  	lua_pushlightuserdata(L, space);
  	lua_settable(L, i);

> 3. Add a comment, what this function does, why, and what arguments on a 
> stack
> it expects, what and home many values returns.
> 
>> +// take care about stack to call this function in frommap directly
>> +lua_getfield(L, 1, "schema_version");
> 
> 4. A comment seems to be not linked with the code below. And please, do not
> use '//' comments. In our code we use '/* ... */' for comments inside a
> function. Note, that comment max length is 66 symbols, including indentation
> before a comment.
> 
>> +
>> +void *space = nullptr;
>> +if (schema_version == global_shema_version) {
>> +
> 
> 5. Do not use nullptr. Everywhere in Tarantool code only NULL is used.
> 
>> +bool tuple = false;
> 
> 6. Lets return tuple by default, and name the option 'table' instead of
> tuple. If a user wants a table, then he must pass the option {table = true}.
> 
>> +if (argc == 3) {
>> +if (!lua_istable(L, 3))
>> +goto error;
>> +lua_getfield(L, 3, "tuple");
>> +if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
>> +goto error;
>> +tuple = lua_toboolean(L, -1);
>> +}
>> +
> 
> 7. Double tabs prior to 'goto error'.
> 
>> +if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
>> +luaL_error(L, "Unknown field '%s'", key);
> 
> 8. According to our Lua error returning convention, you can raise an 
> error either
> on incorrect usage (bad argument type, for example), or on OOM(Out Of 
> Memory)
> errors. On other errors you must return two values: nil and error object or
> description. In this example you must return nil and "Unknown field 
> '%s'" string.
> 
>> +lua_replace(L, 1);
>> +lua_settop(L, 1);
>> +return lbox_tuple_new(L);



@@ -397,56 +397,66 @@ static struct trigger on_alter_space_in_lua = {
  	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
  };

+/**
+ * Get a C pointer for space structure.
+ * Caches the result. Cleans the user(negative) stack itself.
+ * @param Lua space object at top of the Lua stack
+ * @return C structure pointer
+ */
  static int
  lbox_space_ptr_cached(struct lua_State *L)
  {
  	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
-		luaL_error(L, "Usage: space:_ptr_cached()");
+		luaL_error(L, "Usage: space:_cptr()");

-	// take care about stack to call this function in frommap directly
-	lua_getfield(L, 1, "schema_version");
+	lua_getfield(L, 1, "_schema_version");
  	uint64_t schema_version = luaL_touint64(L, -1);
  	lua_pop(L, 1);
  	uint64_t global_shema_version = box_schema_version();

-	void *space = nullptr;
+	void *space = NULL;
  	if (schema_version == global_shema_version) {
-		lua_getfield(L, 1, "_space");
+		lua_getfield(L, 1, "_cptr");
  		space = (void *)lua_topointer(L, -1);
-		lua_pop(L, 1);
  	} else {
  		lua_getfield(L, 1, "id");
  		uint32_t id = luaL_touint64(L, -1);
-		lua_pop(L, 1);
  		space = space_by_id(id);

+		/** update the cache */
  		luaL_pushuint64(L, global_shema_version);
-		lua_setfield(L, 1, "schema_version");
-
+		lua_setfield(L, 1, "_schema_version");
  		lua_pushlightuserdata(L, space);
-		lua_setfield(L, 1, "_space");
+		lua_setfield(L, 1, "_cptr");
  	}
-
+	lua_pop(L, 1);
  	lua_pushlightuserdata(L, space);
  	return 1;
  }

+/**
+ * Make a tuple or a table Lua object by map
+ * @param Lua space object
+ * @param Lua map table object
+ * @param Lua opts table object (optional)
+ * @return C structure pointer
+ */
  static int
  lbox_space_frommap(struct lua_State *L)
  {
-	struct tuple_dictionary *dict = nullptr;
-	struct space *space = nullptr;
+	struct tuple_dictionary *dict = NULL;
+	struct space *space = NULL;
  	int argc = lua_gettop(L);
-	bool tuple = false;
+	bool table = false;
  	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
  		goto error;
  	if (argc == 3) {
  		if (!lua_istable(L, 3))
-				goto error;
-		lua_getfield(L, 3, "tuple");
+			goto error;
+		lua_getfield(L, 3, "table");
  		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
-				goto error;
-		tuple = lua_toboolean(L, -1);
+			goto error;
+		table = lua_toboolean(L, -1);
  	}

  	lbox_space_ptr_cached(L);
@@ -461,11 +471,14 @@ lbox_space_frommap(struct lua_State *L)
  		size_t key_len;
  		const char *key = lua_tolstring(L, -2, &key_len);
  		uint32_t key_hash = lua_hashstring(L, -2);
-		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno))
-			luaL_error(L, "Unknown field '%s'", key);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno)) {
+			lua_pushnil(L);
+			lua_pushstring(L, "Unknown field");
+			return 2;
+		}
  		lua_rawseti(L, -3, fieldno+1);
  	}
-	if (!tuple)
+	if (table)
  		return 1;

  	lua_replace(L, 1);
@@ -557,7 +570,7 @@ box_lua_space_init(struct lua_State *L)

  	static const struct luaL_Reg space_internal_lib[] = {
  		{"frommap", lbox_space_frommap},
-		{"_ptr", lbox_space_ptr_cached},
+		{"_cptr", lbox_space_ptr_cached},
  		{NULL, NULL}
  	};
  	luaL_register(L, "box.internal.space", space_internal_lib);

> 
> 9. How about a comment what is happening here?
> 
> 10. Add a test on nil and box.NULL fields. And test, that a result of
> frommap() actually can be inserted or replaced into a space.
> 
> 
diff --git a/test/box/space_frommap.result b/test/box/space_frommap.result
index 884d78c..7a4b3db 100644
--- a/test/box/space_frommap.result
+++ b/test/box/space_frommap.result
@@ -29,21 +29,16 @@ subscriber = box.schema.space.create('subscriber', 
{format = format})
  ...
  subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
  ---
-- - 2
-  - 4
-  - 3
-  - 1
+- [2, 4, 3, 1]
  ...
  subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
  ---
-- - 2
-  - 3
-  - null
-  - 1
+- [2, 3, null, 1]
  ...
  subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
  ---
-- error: Unknown field 'eee'
+- null
+- Unknown field
  ...
  subscriber:frommap()
  ---
@@ -53,23 +48,34 @@ subscriber:frommap({})
  ---
  - []
  ...
-subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
----
-- [2, 4, 3, 1]
-...
-subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
  ---
  - - 2
    - 4
    - 3
    - 1
  ...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+---
+- [2, 4, 3, 1]
+...
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+---
+- [2, null, 3, 1]
+...
  subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
  ---
-- - 2
-  - 4
-  - 3
-  - 1
+- [2, 4, 3, 1]
+...
+_ = subscriber:create_index('primary', {parts={'aaa'}})
+---
+...
+tuple = subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+...
+subscriber:replace(tuple)
+---
+- [2, 4, 3, 1]
  ...
  subscriber:drop()
  ---
diff --git a/test/box/space_frommap.test.lua 
b/test/box/space_frommap.test.lua
index 4760b3a..ff7ef98 100644
--- a/test/box/space_frommap.test.lua
+++ b/test/box/space_frommap.test.lua
@@ -14,7 +14,11 @@ subscriber:frommap({ddd = 1, aaa = 2, bbb = 3})
  subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
  subscriber:frommap()
  subscriber:frommap({})
-subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = true})
-subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {tuple = false})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
  subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+_ = subscriber:create_index('primary', {parts={'aaa'}})
+tuple = subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+subscriber:replace(tuple)
  subscriber:drop()

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-02 10:26       ` Kirill Shcherbatov
@ 2018-04-02 11:19         ` v.shpilevoy
  2018-04-02 19:42           ` Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: v.shpilevoy @ 2018-04-02 11:19 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Kirill Shcherbatov

Hello. Thanks for your contributing! A several (5) minor fixes are left:

1. Please, describe the patch in a commit message body. It would be good, if you
write why the feature is needed, who needs this, and very short description of
the feature itself: API usage example, options description. See Vladimir Davydov
patches as the perfect example.

> 	/* space.schema_version */
> 	lua_pushstring(L, "_schema_version");
> 	luaL_pushuint64(L, box_schema_version());
> 	lua_settable(L, i);

2. Please, update the comment too: /* space._schema_version */ (added '_').

> /**
>  * Get a C pointer for space structure.
>  * Caches the result. Cleans the user(negative) stack itself.
>  * @param Lua space object at top of the Lua stack
>  * @return C structure pointer
>  */
> static int
> lbox_space_ptr_cached(struct lua_State *L)

3. Please, use @retval instead of @return, and put '.' at the end of a sentence.

4. Seems like the following test crashes Tarantool. Please, fix it.

box.cfg{}
format = {{'field1', 'unsigned'}}
s = box.schema.create_space('test', {format = format})
pk = s:create_index('pk')
s:drop()
s:frommap({field1 = 100})

Process 67761 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xe8)
    frame #0: 0x0000000100100b68 tarantool`lbox_space_frommap(L=0x0000000103200378) at space.cc:465
   462 		lbox_space_ptr_cached(L);
   463 		space = (struct space *)lua_topointer(L, -1);
   464 	
-> 465 		dict = space->format->dict;
   466 		lua_createtable(L, space->def->field_count, 0);
   467 	
   468 		lua_pushnil(L);
Target 0: (tarantool) stopped.



> 		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno)) {
> 			lua_pushnil(L);
> 			lua_pushstring(L, "Unknown field");
> 			return 2;
> 		}
> 		lua_rawseti(L, -3, fieldno+1);


5. Please, return field name into error message. In the previous patch the error message
was ok. Just do not throw it. And put spaces before and after arithmetic operations.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-02 11:19         ` v.shpilevoy
@ 2018-04-02 19:42           ` Kirill Shcherbatov
  2018-04-03  9:47             ` Vladislav Shpilevoy
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-02 19:42 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

I've also updated commit message content.

diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index f8583a2..2ff9a11 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -38,6 +38,7 @@ extern "C" {
  	#include <lua.h>
  	#include <lauxlib.h>
  	#include <lualib.h>
+	#include <trivia/util.h>
  } /* extern "C" */

  #include "box/space.h"
@@ -175,7 +176,7 @@ lbox_fillspace(struct lua_State *L, struct space 
*space, int i)
  	lua_pushstring(L, space->def->engine_name);
  	lua_settable(L, i);

-	/* space.schema_version */
+	/* space._schema_version */
  	lua_pushstring(L, "_schema_version");
  	luaL_pushuint64(L, box_schema_version());
  	lua_settable(L, i);
@@ -400,8 +401,8 @@ static struct trigger on_alter_space_in_lua = {
  /**
   * Get a C pointer for space structure.
   * Caches the result. Cleans the user(negative) stack itself.
- * @param Lua space object at top of the Lua stack
- * @return C structure pointer
+ * @param Lua space object at top of the Lua stack.
+ * @retval C structure pointer.
   */
  static int
  lbox_space_ptr_cached(struct lua_State *L)
@@ -422,6 +423,8 @@ lbox_space_ptr_cached(struct lua_State *L)
  		lua_getfield(L, 1, "id");
  		uint32_t id = luaL_touint64(L, -1);
  		space = space_by_id(id);
+		if (!space)
+			luaL_error(L, "Specified space is not exists");

  		/** update the cache */
  		luaL_pushuint64(L, global_shema_version);
@@ -435,11 +438,11 @@ lbox_space_ptr_cached(struct lua_State *L)
  }

  /**
- * Make a tuple or a table Lua object by map
- * @param Lua space object
- * @param Lua map table object
- * @param Lua opts table object (optional)
- * @return C structure pointer
+ * Make a tuple or a table Lua object by map.
+ * @param Lua space object.
+ * @param Lua map table object.
+ * @param Lua opts table object (optional).
+ * @retval C structure pointer.
   */
  static int
  lbox_space_frommap(struct lua_State *L)
@@ -461,7 +464,6 @@ lbox_space_frommap(struct lua_State *L)

  	lbox_space_ptr_cached(L);
  	space = (struct space *)lua_topointer(L, -1);
-
  	dict = space->format->dict;
  	lua_createtable(L, space->def->field_count, 0);

@@ -472,8 +474,9 @@ lbox_space_frommap(struct lua_State *L)
  		const char *key = lua_tolstring(L, -2, &key_len);
  		uint32_t key_hash = lua_hashstring(L, -2);
  		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno)) {
+			const char *err_msg = tt_sprintf("Unknown field '%s'", key);
  			lua_pushnil(L);
-			lua_pushstring(L, "Unknown field");
+			lua_pushstring(L, err_msg);
  			return 2;
  		}
  		lua_rawseti(L, -3, fieldno+1);


On 02.04.2018 14:19, v.shpilevoy@tarantool.org wrote:
> Hello. Thanks for your contributing! A several (5) minor fixes are left:
> 
> 1. Please, describe the patch in a commit message body. It would be good, if you
> write why the feature is needed, who needs this, and very short description of
> the feature itself: API usage example, options description. See Vladimir Davydov
> patches as the perfect example.
> 
>> 	/* space.schema_version */
>> 	lua_pushstring(L, "_schema_version");
>> 	luaL_pushuint64(L, box_schema_version());
>> 	lua_settable(L, i);
> 
> 2. Please, update the comment too: /* space._schema_version */ (added '_').
> 
>> /**
>>   * Get a C pointer for space structure.
>>   * Caches the result. Cleans the user(negative) stack itself.
>>   * @param Lua space object at top of the Lua stack
>>   * @return C structure pointer
>>   */
>> static int
>> lbox_space_ptr_cached(struct lua_State *L)
> 
> 3. Please, use @retval instead of @return, and put '.' at the end of a sentence.
> 
> 4. Seems like the following test crashes Tarantool. Please, fix it.
> 
> box.cfg{}
> format = {{'field1', 'unsigned'}}
> s = box.schema.create_space('test', {format = format})
> pk = s:create_index('pk')
> s:drop()
> s:frommap({field1 = 100})
> 
> Process 67761 stopped
> * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xe8)
>      frame #0: 0x0000000100100b68 tarantool`lbox_space_frommap(L=0x0000000103200378) at space.cc:465
>     462 		lbox_space_ptr_cached(L);
>     463 		space = (struct space *)lua_topointer(L, -1);
>     464 	
> -> 465 		dict = space->format->dict;
>     466 		lua_createtable(L, space->def->field_count, 0);
>     467 	
>     468 		lua_pushnil(L);
> Target 0: (tarantool) stopped.
> 
> 
> 
>> 		if (tuple_fieldno_by_name(dict, key, key_len, key_hash, &fieldno)) {
>> 			lua_pushnil(L);
>> 			lua_pushstring(L, "Unknown field");
>> 			return 2;
>> 		}
>> 		lua_rawseti(L, -3, fieldno+1);
> 
> 
> 5. Please, return field name into error message. In the previous patch the error message
> was ok. Just do not throw it. And put spaces before and after arithmetic operations.
> 
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-02 19:42           ` Kirill Shcherbatov
@ 2018-04-03  9:47             ` Vladislav Shpilevoy
  2018-04-04 14:14               ` Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-03  9:47 UTC (permalink / raw)
  To: tarantool-patches, Kirill Shcherbatov

Hello. Please, consider 4 comments below.
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 29a9aca..2ff9a11 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -32,11 +32,13 @@
>   #include "box/lua/tuple.h"
>   #include "lua/utils.h"
>   #include "lua/trigger.h"
> +#include "box/schema.h"
>   
>   extern "C" {
>   	#include <lua.h>
>   	#include <lauxlib.h>
>   	#include <lualib.h>
> +	#include <trivia/util.h>
>   } /* extern "C" */
1. Schema.h is included below. Trivia/util.h is not needed here.

> +		space = space_by_id(id);
> +		if (!space)
> +			luaL_error(L, "Specified space is not exists");
> +
2. Please, for pointers use explicit != NULL.

3. As I noted earlier, a function must not raise an error. It must 
return nil + error message/object.
And please, check your comments grammar: you can not say "is not 
exists". Perhaps,
you meant "does not exist".

4. I do not see my crash test in your space_frommap.test.lua. When I 
provide you a test, or you
found an own, you must add it to the patch.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-03  9:47             ` Vladislav Shpilevoy
@ 2018-04-04 14:14               ` Kirill Shcherbatov
  2018-04-04 16:35                 ` [tarantool-patches] " Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-04 14:14 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Vladislav Shpilevoy


On 03.04.2018 12:47, Vladislav Shpilevoy wrote:
> Hello. Please, consider 4 comments below.
>> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
>> index 29a9aca..2ff9a11 100644
>> --- a/src/box/lua/space.cc
>> +++ b/src/box/lua/space.cc
>> @@ -32,11 +32,13 @@
>>   #include "box/lua/tuple.h"
>>   #include "lua/utils.h"
>>   #include "lua/trigger.h"
>> +#include "box/schema.h"
>>   extern "C" {
>>       #include <lua.h>
>>       #include <lauxlib.h>
>>       #include <lualib.h>
>> +    #include <trivia/util.h>
>>   } /* extern "C" */
> 1. Schema.h is included below. Trivia/util.h is not needed here.
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 2ff9a11..9004c0e 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -38,7 +38,6 @@ extern "C" {
  	#include <lua.h>
  	#include <lauxlib.h>
  	#include <lualib.h>
-	#include <trivia/util.h>
  } /* extern "C" */

  #include "box/space.h"
> 
>> +        space = space_by_id(id);
>> +        if (!space)
>> +            luaL_error(L, "Specified space is not exists");
>> +
> 2. Please, for pointers use explicit != NULL.
> 
> 3. As I noted earlier, a function must not raise an error. It must 
> return nil + error message/object.
> And please, check your comments grammar: you can not say "is not 
> exists". Perhaps,
> you meant "does not exist".
> 
@@ -423,8 +422,14 @@ lbox_space_ptr_cached(struct lua_State *L)
  		lua_getfield(L, 1, "id");
  		uint32_t id = luaL_touint64(L, -1);
  		space = space_by_id(id);
-		if (!space)
-			luaL_error(L, "Specified space is not exists");
+		if (space == NULL) {
+			const char *err_msg;
+			err_msg = tt_sprintf("Space with id '%d' doesn't exists",
+			                     id);
+			lua_pushnil(L);
+			lua_pushstring(L, err_msg);
+			return 2;
+		}

  		/** update the cache */
  		luaL_pushuint64(L, global_shema_version);
@@ -462,8 +467,14 @@ lbox_space_frommap(struct lua_State *L)
  		table = lua_toboolean(L, -1);
  	}

-	lbox_space_ptr_cached(L);
-	space = (struct space *)lua_topointer(L, -1);
+	if (lbox_space_ptr_cached(L) == 1) {
+		space = (struct space *) lua_topointer(L, -1);
+	} else {
+		const char *err_msg = lua_tostring(L, -1);
+		lua_pushnil(L);
+		lua_pushstring(L, err_msg);
+		return 2;
+	}
  	dict = space->format->dict;
  	lua_createtable(L, space->def->field_count, 0);
> 4. I do not see my crash test in your space_frommap.test.lua. When I 
> provide you a test, or you
> found an own, you must add it to the patch.
> 
There is the same test case here:

diff --git a/test/box/space_frommap.test.lua 
b/test/box/space_frommap.test.lua
index ff7ef98..647d761 100644
--- a/test/box/space_frommap.test.lua
+++ b/test/box/space_frommap.test.lua
@@ -21,4 +21,7 @@ subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 
4}, {dummy = true})
  _ = subscriber:create_index('primary', {parts={'aaa'}})
  tuple = subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
  subscriber:replace(tuple)
+_ = box.internal.space._cptr(subscriber)
  subscriber:drop()
+_ = subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+_ = box.internal.space._cptr(subscriber)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-04 14:14               ` Kirill Shcherbatov
@ 2018-04-04 16:35                 ` Kirill Shcherbatov
  2018-04-04 16:43                   ` Vladislav Shpilevoy
                                     ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-04 16:35 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

This feature is designed to build tuple or table object by map.
Tuple output is default. If you wish to make a table, specify table = true option.
subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})

Closes #3282
---
 src/box/lua/schema.lua  |   1 +
 src/box/lua/space.cc    | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/box/lua/tuple.c     |   2 +-
 src/box/lua/tuple.h     |   3 ++
 test/box/tuple.result   |  95 +++++++++++++++++++++++++++++++++++++
 test/box/tuple.test.lua |  29 ++++++++++++
 6 files changed, 252 insertions(+), 1 deletion(-)

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 30c6bc6..323c2d6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1419,6 +1419,7 @@ function box.schema.space.bless(space)
         end
         builtin.space_run_triggers(s, yesno)
     end
+    space_mt.frommap = box.internal.space.frommap
     space_mt.__index = space_mt
 
     setmetatable(space, space_mt)
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 29a9aca..2723baf 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -174,6 +174,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushstring(L, space->def->engine_name);
 	lua_settable(L, i);
 
+	/* space._schema_version */
+	lua_pushstring(L, "_schema_version");
+	luaL_pushuint64(L, box_schema_version());
+	lua_settable(L, i);
+
+	/* space._cptr */
+	lua_pushstring(L, "_cptr");
+	lua_pushlightuserdata(L, space);
+	lua_settable(L, i);
+
 	lua_pushstring(L, "enabled");
 	lua_pushboolean(L, space_index(space, 0) != 0);
 	lua_settable(L, i);
@@ -386,6 +396,111 @@ static struct trigger on_alter_space_in_lua = {
 	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
 };
 
+/**
+ * Get a C pointer for space structure.
+ * Caches the result. Cleans the user(negative) stack itself.
+ * @param Lua space object at top of the Lua stack.
+ * @retval not nil C structure pointer.
+ * @retval nil, err Can not find underlying space, error
+ *         description is returned.
+ */
+static int
+lbox_space_ptr_cached(struct lua_State *L)
+{
+	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
+		luaL_error(L, "Usage: space:_cptr()");
+
+	lua_getfield(L, 1, "_schema_version");
+	uint64_t schema_version = luaL_touint64(L, -1);
+	lua_pop(L, 1);
+	uint64_t global_shema_version = box_schema_version();
+
+	void *space = NULL;
+	if (schema_version == global_shema_version) {
+		lua_getfield(L, 1, "_cptr");
+		space = (void *)lua_topointer(L, -1);
+	} else {
+		lua_getfield(L, 1, "id");
+		uint32_t id = luaL_touint64(L, -1);
+		space = space_by_id(id);
+		if (space == NULL) {
+			lua_pushnil(L);
+			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
+						     "doesn't exist", id));
+			return 2;
+		}
+
+		/** update the cache */
+		luaL_pushuint64(L, global_shema_version);
+		lua_setfield(L, 1, "_schema_version");
+		lua_pushlightuserdata(L, space);
+		lua_setfield(L, 1, "_cptr");
+	}
+	lua_pop(L, 1);
+	lua_pushlightuserdata(L, space);
+	return 1;
+}
+
+/**
+ * Make a tuple or a table Lua object by map.
+ * @param Lua space object.
+ * @param Lua map table object.
+ * @param Lua opts table object (optional).
+ * @retval not nil A tuple or a table conforming to a space
+ *         format.
+ * @retval nil, err Can not built a tuple. A reason is returned in
+ *         the second value.
+ */
+static int
+lbox_space_frommap(struct lua_State *L)
+{
+	struct tuple_dictionary *dict = NULL;
+	struct space *space = NULL;
+	int rc, argc = lua_gettop(L);
+	bool table = false;
+	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
+		goto usage_error;
+	if (argc == 3) {
+		if (!lua_istable(L, 3))
+			goto usage_error;
+		lua_getfield(L, 3, "table");
+		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
+			goto usage_error;
+		table = lua_toboolean(L, -1);
+	}
+
+	rc = lbox_space_ptr_cached(L);
+	if (rc != 1)
+		return rc;
+	space = (struct space *) lua_topointer(L, -1);
+	dict = space->format->dict;
+	lua_createtable(L, space->def->field_count, 0);
+
+	lua_pushnil(L);
+	while (lua_next(L, 2) != 0) {
+		uint32_t fieldno;
+		size_t key_len;
+		const char *key = lua_tolstring(L, -2, &key_len);
+		uint32_t key_hash = lua_hashstring(L, -2);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
+					  &fieldno)) {
+			lua_pushnil(L);
+			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
+						     key));
+			return 2;
+		}
+		lua_rawseti(L, -3, fieldno+1);
+	}
+	if (table)
+		return 1;
+
+	lua_replace(L, 1);
+	lua_settop(L, 1);
+	return lbox_tuple_new(L);
+usage_error:
+	return luaL_error(L, "Usage: space:frommap(map, opts)");
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -464,4 +579,12 @@ box_lua_space_init(struct lua_State *L)
 	lua_pushnumber(L, VCLOCK_MAX);
 	lua_setfield(L, -2, "REPLICA_MAX");
 	lua_pop(L, 2); /* box, schema */
+
+	static const struct luaL_Reg space_internal_lib[] = {
+		{"frommap", lbox_space_frommap},
+		{"_cptr", lbox_space_ptr_cached},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.internal.space", space_internal_lib);
+	lua_pop(L, 1);
 }
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 7ca4299..92a7185 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
 	return *(struct tuple **) data;
 }
 
-static int
+int
 lbox_tuple_new(lua_State *L)
 {
 	int argc = lua_gettop(L);
diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
index 0965644..5d7062e 100644
--- a/src/box/lua/tuple.h
+++ b/src/box/lua/tuple.h
@@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
 
 /** \endcond public */
 
+int
+lbox_tuple_new(struct lua_State *L);
+
 static inline int
 luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
 {
diff --git a/test/box/tuple.result b/test/box/tuple.result
index ec5ed90..2dd8f0d 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -1060,6 +1060,101 @@ box.tuple.bsize == t.bsize
 ---
 - true
 ...
+--
+-- gh-3282: space:frommap().
+--
+format = {}
+---
+...
+format[1] = {name = 'aaa', type = 'unsigned'}
+---
+...
+format[2] = {name = 'bbb', type = 'unsigned'}
+---
+...
+format[3] = {name = 'ccc', type = 'unsigned'}
+---
+...
+format[4] = {name = 'ddd', type = 'unsigned'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+---
+- [2, 3, null, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+---
+- null
+- Unknown field 'eee'
+...
+s:frommap()
+---
+- error: 'Usage: space:frommap(map, opts)'
+...
+s:frommap({})
+---
+- []
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+---
+- [2, null, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+---
+- [2, 4, 3, 1]
+...
+_ = s:create_index('primary', {parts = {'aaa'}})
+---
+...
+tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+...
+s:replace(tuple)
+---
+- [2, 4, 3, 1]
+...
+_, err = box.internal.space._cptr(s)
+---
+...
+assert(not err)
+---
+- true
+...
+s:drop()
+---
+...
+_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+...
+assert(err ~= nil)
+---
+- true
+...
+_, err = box.internal.space._cptr(s)
+---
+...
+assert(err ~= nil)
+---
+- true
+...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
index 0b3392d..03e6194 100644
--- a/test/box/tuple.test.lua
+++ b/test/box/tuple.test.lua
@@ -349,4 +349,33 @@ box.tuple.update == t.update
 box.tuple.upsert == t.upsert
 box.tuple.bsize == t.bsize
 
+--
+-- gh-3282: space:frommap().
+--
+format = {}
+format[1] = {name = 'aaa', type = 'unsigned'}
+format[2] = {name = 'bbb', type = 'unsigned'}
+format[3] = {name = 'ccc', type = 'unsigned'}
+format[4] = {name = 'ddd', type = 'unsigned'}
+s = box.schema.create_space('test', {format = format})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+s:frommap()
+s:frommap({})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+_ = s:create_index('primary', {parts = {'aaa'}})
+tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+s:replace(tuple)
+_, err = box.internal.space._cptr(s)
+assert(not err)
+s:drop()
+_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+assert(err ~= nil)
+_, err = box.internal.space._cptr(s)
+assert(err ~= nil)
+
 test_run:cmd("clear filter")
-- 
2.7.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-04 16:35                 ` [tarantool-patches] " Kirill Shcherbatov
@ 2018-04-04 16:43                   ` Vladislav Shpilevoy
  2018-04-06 13:47                   ` Vladimir Davydov
  2018-04-06 15:44                   ` [tarantool-patches] " Konstantin Osipov
  2 siblings, 0 replies; 18+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-04 16:43 UTC (permalink / raw)
  To: tarantool-patches, vdavydov.dev; +Cc: Kirill Shcherbatov

LGTM. Vova, please, take a look.

04.04.2018 19:35, Kirill Shcherbatov пишет:
> This feature is designed to build tuple or table object by map.
> Tuple output is default. If you wish to make a table, specify table = true option.
> subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
>
> Closes #3282
> ---
>   src/box/lua/schema.lua  |   1 +
>   src/box/lua/space.cc    | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
>   src/box/lua/tuple.c     |   2 +-
>   src/box/lua/tuple.h     |   3 ++
>   test/box/tuple.result   |  95 +++++++++++++++++++++++++++++++++++++
>   test/box/tuple.test.lua |  29 ++++++++++++
>   6 files changed, 252 insertions(+), 1 deletion(-)
>
> diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
> index 30c6bc6..323c2d6 100644
> --- a/src/box/lua/schema.lua
> +++ b/src/box/lua/schema.lua
> @@ -1419,6 +1419,7 @@ function box.schema.space.bless(space)
>           end
>           builtin.space_run_triggers(s, yesno)
>       end
> +    space_mt.frommap = box.internal.space.frommap
>       space_mt.__index = space_mt
>   
>       setmetatable(space, space_mt)
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 29a9aca..2723baf 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -174,6 +174,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
>   	lua_pushstring(L, space->def->engine_name);
>   	lua_settable(L, i);
>   
> +	/* space._schema_version */
> +	lua_pushstring(L, "_schema_version");
> +	luaL_pushuint64(L, box_schema_version());
> +	lua_settable(L, i);
> +
> +	/* space._cptr */
> +	lua_pushstring(L, "_cptr");
> +	lua_pushlightuserdata(L, space);
> +	lua_settable(L, i);
> +
>   	lua_pushstring(L, "enabled");
>   	lua_pushboolean(L, space_index(space, 0) != 0);
>   	lua_settable(L, i);
> @@ -386,6 +396,111 @@ static struct trigger on_alter_space_in_lua = {
>   	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
>   };
>   
> +/**
> + * Get a C pointer for space structure.
> + * Caches the result. Cleans the user(negative) stack itself.
> + * @param Lua space object at top of the Lua stack.
> + * @retval not nil C structure pointer.
> + * @retval nil, err Can not find underlying space, error
> + *         description is returned.
> + */
> +static int
> +lbox_space_ptr_cached(struct lua_State *L)
> +{
> +	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
> +		luaL_error(L, "Usage: space:_cptr()");
> +
> +	lua_getfield(L, 1, "_schema_version");
> +	uint64_t schema_version = luaL_touint64(L, -1);
> +	lua_pop(L, 1);
> +	uint64_t global_shema_version = box_schema_version();
> +
> +	void *space = NULL;
> +	if (schema_version == global_shema_version) {
> +		lua_getfield(L, 1, "_cptr");
> +		space = (void *)lua_topointer(L, -1);
> +	} else {
> +		lua_getfield(L, 1, "id");
> +		uint32_t id = luaL_touint64(L, -1);
> +		space = space_by_id(id);
> +		if (space == NULL) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
> +						     "doesn't exist", id));
> +			return 2;
> +		}
> +
> +		/** update the cache */
> +		luaL_pushuint64(L, global_shema_version);
> +		lua_setfield(L, 1, "_schema_version");
> +		lua_pushlightuserdata(L, space);
> +		lua_setfield(L, 1, "_cptr");
> +	}
> +	lua_pop(L, 1);
> +	lua_pushlightuserdata(L, space);
> +	return 1;
> +}
> +
> +/**
> + * Make a tuple or a table Lua object by map.
> + * @param Lua space object.
> + * @param Lua map table object.
> + * @param Lua opts table object (optional).
> + * @retval not nil A tuple or a table conforming to a space
> + *         format.
> + * @retval nil, err Can not built a tuple. A reason is returned in
> + *         the second value.
> + */
> +static int
> +lbox_space_frommap(struct lua_State *L)
> +{
> +	struct tuple_dictionary *dict = NULL;
> +	struct space *space = NULL;
> +	int rc, argc = lua_gettop(L);
> +	bool table = false;
> +	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
> +		goto usage_error;
> +	if (argc == 3) {
> +		if (!lua_istable(L, 3))
> +			goto usage_error;
> +		lua_getfield(L, 3, "table");
> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
> +			goto usage_error;
> +		table = lua_toboolean(L, -1);
> +	}
> +
> +	rc = lbox_space_ptr_cached(L);
> +	if (rc != 1)
> +		return rc;
> +	space = (struct space *) lua_topointer(L, -1);
> +	dict = space->format->dict;
> +	lua_createtable(L, space->def->field_count, 0);
> +
> +	lua_pushnil(L);
> +	while (lua_next(L, 2) != 0) {
> +		uint32_t fieldno;
> +		size_t key_len;
> +		const char *key = lua_tolstring(L, -2, &key_len);
> +		uint32_t key_hash = lua_hashstring(L, -2);
> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
> +					  &fieldno)) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
> +						     key));
> +			return 2;
> +		}
> +		lua_rawseti(L, -3, fieldno+1);
> +	}
> +	if (table)
> +		return 1;
> +
> +	lua_replace(L, 1);
> +	lua_settop(L, 1);
> +	return lbox_tuple_new(L);
> +usage_error:
> +	return luaL_error(L, "Usage: space:frommap(map, opts)");
> +}
> +
>   void
>   box_lua_space_init(struct lua_State *L)
>   {
> @@ -464,4 +579,12 @@ box_lua_space_init(struct lua_State *L)
>   	lua_pushnumber(L, VCLOCK_MAX);
>   	lua_setfield(L, -2, "REPLICA_MAX");
>   	lua_pop(L, 2); /* box, schema */
> +
> +	static const struct luaL_Reg space_internal_lib[] = {
> +		{"frommap", lbox_space_frommap},
> +		{"_cptr", lbox_space_ptr_cached},
> +		{NULL, NULL}
> +	};
> +	luaL_register(L, "box.internal.space", space_internal_lib);
> +	lua_pop(L, 1);
>   }
> diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
> index 7ca4299..92a7185 100644
> --- a/src/box/lua/tuple.c
> +++ b/src/box/lua/tuple.c
> @@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
>   	return *(struct tuple **) data;
>   }
>   
> -static int
> +int
>   lbox_tuple_new(lua_State *L)
>   {
>   	int argc = lua_gettop(L);
> diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
> index 0965644..5d7062e 100644
> --- a/src/box/lua/tuple.h
> +++ b/src/box/lua/tuple.h
> @@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
>   
>   /** \endcond public */
>   
> +int
> +lbox_tuple_new(struct lua_State *L);
> +
>   static inline int
>   luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
>   {
> diff --git a/test/box/tuple.result b/test/box/tuple.result
> index ec5ed90..2dd8f0d 100644
> --- a/test/box/tuple.result
> +++ b/test/box/tuple.result
> @@ -1060,6 +1060,101 @@ box.tuple.bsize == t.bsize
>   ---
>   - true
>   ...
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +---
> +...
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +---
> +...
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +---
> +...
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +---
> +...
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +---
> +...
> +s = box.schema.create_space('test', {format = format})
> +---
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +---
> +- [2, 3, null, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +---
> +- null
> +- Unknown field 'eee'
> +...
> +s:frommap()
> +---
> +- error: 'Usage: space:frommap(map, opts)'
> +...
> +s:frommap({})
> +---
> +- []
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +---
> +- [2, null, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +---
> +- [2, 4, 3, 1]
> +...
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +---
> +...
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +s:replace(tuple)
> +---
> +- [2, 4, 3, 1]
> +...
> +_, err = box.internal.space._cptr(s)
> +---
> +...
> +assert(not err)
> +---
> +- true
> +...
> +s:drop()
> +---
> +...
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +assert(err ~= nil)
> +---
> +- true
> +...
> +_, err = box.internal.space._cptr(s)
> +---
> +...
> +assert(err ~= nil)
> +---
> +- true
> +...
>   test_run:cmd("clear filter")
>   ---
>   - true
> diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
> index 0b3392d..03e6194 100644
> --- a/test/box/tuple.test.lua
> +++ b/test/box/tuple.test.lua
> @@ -349,4 +349,33 @@ box.tuple.update == t.update
>   box.tuple.upsert == t.upsert
>   box.tuple.bsize == t.bsize
>   
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +s = box.schema.create_space('test', {format = format})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +s:frommap()
> +s:frommap({})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:replace(tuple)
> +_, err = box.internal.space._cptr(s)
> +assert(not err)
> +s:drop()
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +assert(err ~= nil)
> +_, err = box.internal.space._cptr(s)
> +assert(err ~= nil)
> +
>   test_run:cmd("clear filter")

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-04 16:35                 ` [tarantool-patches] " Kirill Shcherbatov
  2018-04-04 16:43                   ` Vladislav Shpilevoy
@ 2018-04-06 13:47                   ` Vladimir Davydov
  2018-04-06 15:44                   ` [tarantool-patches] " Konstantin Osipov
  2 siblings, 0 replies; 18+ messages in thread
From: Vladimir Davydov @ 2018-04-06 13:47 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, v.shpilevoy

On Wed, Apr 04, 2018 at 07:35:54PM +0300, Kirill Shcherbatov wrote:
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 29a9aca..2723baf 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -386,6 +396,111 @@ static struct trigger on_alter_space_in_lua = {
>  	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
>  };
>  
> +/**
> + * Get a C pointer for space structure.
> + * Caches the result. Cleans the user(negative) stack itself.
> + * @param Lua space object at top of the Lua stack.
> + * @retval not nil C structure pointer.
> + * @retval nil, err Can not find underlying space, error
> + *         description is returned.
> + */
> +static int
> +lbox_space_ptr_cached(struct lua_State *L)
> +{

Is it really necessary? Can't you always pass space_id and use
space_cache_find, which already caches last space ptr?

> +	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
> +		luaL_error(L, "Usage: space:_cptr()");
> +
> +	lua_getfield(L, 1, "_schema_version");
> +	uint64_t schema_version = luaL_touint64(L, -1);
> +	lua_pop(L, 1);
> +	uint64_t global_shema_version = box_schema_version();
> +
> +	void *space = NULL;
> +	if (schema_version == global_shema_version) {
> +		lua_getfield(L, 1, "_cptr");
> +		space = (void *)lua_topointer(L, -1);
> +	} else {
> +		lua_getfield(L, 1, "id");
> +		uint32_t id = luaL_touint64(L, -1);
> +		space = space_by_id(id);
> +		if (space == NULL) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
> +						     "doesn't exist", id));
> +			return 2;
> +		}
> +
> +		/** update the cache */
> +		luaL_pushuint64(L, global_shema_version);
> +		lua_setfield(L, 1, "_schema_version");
> +		lua_pushlightuserdata(L, space);
> +		lua_setfield(L, 1, "_cptr");
> +	}
> +	lua_pop(L, 1);
> +	lua_pushlightuserdata(L, space);
> +	return 1;
> +}

> @@ -464,4 +579,12 @@ box_lua_space_init(struct lua_State *L)
>  	lua_pushnumber(L, VCLOCK_MAX);
>  	lua_setfield(L, -2, "REPLICA_MAX");
>  	lua_pop(L, 2); /* box, schema */
> +
> +	static const struct luaL_Reg space_internal_lib[] = {
> +		{"frommap", lbox_space_frommap},
> +		{"_cptr", lbox_space_ptr_cached},
> +		{NULL, NULL}
> +	};
> +	luaL_register(L, "box.internal.space", space_internal_lib);
> +	lua_pop(L, 1);

AFAIU this function might get pretty hot. Shouldn't we use FFI for
calling it?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-04 16:35                 ` [tarantool-patches] " Kirill Shcherbatov
  2018-04-04 16:43                   ` Vladislav Shpilevoy
  2018-04-06 13:47                   ` Vladimir Davydov
@ 2018-04-06 15:44                   ` Konstantin Osipov
  2018-04-09  8:30                     ` Kirill Shcherbatov
  2 siblings, 1 reply; 18+ messages in thread
From: Konstantin Osipov @ 2018-04-06 15:44 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy, Kirill Shcherbatov

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [18/04/05 10:24]:

> This feature is designed to build tuple or table object by map.
> Tuple output is default. If you wish to make a table, specify table = true option.
> subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})

What are the performance results from adding the cache? 

How much do we win?

It's OK to export _schema_version to Lua as extern int via FFI, 
and avoid a function call.

> +static int
> +lbox_space_ptr_cached(struct lua_State *L)

The fast path of this function should be in plain Lua.

You have to re-fetch the space via lookup in a slowpath only, 
when schema version has changed.


What about using the cptr in all other functions? 


> +{
> +	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
> +		luaL_error(L, "Usage: space:_cptr()");
> +
> +	lua_getfield(L, 1, "_schema_version");
> +	uint64_t schema_version = luaL_touint64(L, -1);
> +	lua_pop(L, 1);
> +	uint64_t global_shema_version = box_schema_version();
> +
> +	void *space = NULL;
> +	if (schema_version == global_shema_version) {
> +		lua_getfield(L, 1, "_cptr");
> +		space = (void *)lua_topointer(L, -1);
> +	} else {
> +		lua_getfield(L, 1, "id");
> +		uint32_t id = luaL_touint64(L, -1);
> +		space = space_by_id(id);
> +		if (space == NULL) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
> +						     "doesn't exist", id));
> +			return 2;
> +		}
> +
> +		/** update the cache */
> +		luaL_pushuint64(L, global_shema_version);
> +		lua_setfield(L, 1, "_schema_version");
> +		lua_pushlightuserdata(L, space);
> +		lua_setfield(L, 1, "_cptr");
> +	}
> +	lua_pop(L, 1);
> +	lua_pushlightuserdata(L, space);
> +	return 1;
> +}
> +
> +/**
> + * Make a tuple or a table Lua object by map.
> + * @param Lua space object.
> + * @param Lua map table object.
> + * @param Lua opts table object (optional).
> + * @retval not nil A tuple or a table conforming to a space
> + *         format.
> + * @retval nil, err Can not built a tuple. A reason is returned in
> + *         the second value.
> + */
> +static int
> +lbox_space_frommap(struct lua_State *L)
> +{
> +	struct tuple_dictionary *dict = NULL;
> +	struct space *space = NULL;
> +	int rc, argc = lua_gettop(L);
> +	bool table = false;
> +	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
> +		goto usage_error;
> +	if (argc == 3) {
> +		if (!lua_istable(L, 3))
> +			goto usage_error;
> +		lua_getfield(L, 3, "table");
> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
> +			goto usage_error;
> +		table = lua_toboolean(L, -1);
> +	}
> +
> +	rc = lbox_space_ptr_cached(L);
> +	if (rc != 1)
> +		return rc;
> +	space = (struct space *) lua_topointer(L, -1);
> +	dict = space->format->dict;
> +	lua_createtable(L, space->def->field_count, 0);
> +
> +	lua_pushnil(L);
> +	while (lua_next(L, 2) != 0) {
> +		uint32_t fieldno;
> +		size_t key_len;
> +		const char *key = lua_tolstring(L, -2, &key_len);
> +		uint32_t key_hash = lua_hashstring(L, -2);
> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
> +					  &fieldno)) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
> +						     key));
> +			return 2;
> +		}
> +		lua_rawseti(L, -3, fieldno+1);
> +	}
> +	if (table)
> +		return 1;
> +
> +	lua_replace(L, 1);
> +	lua_settop(L, 1);
> +	return lbox_tuple_new(L);
> +usage_error:
> +	return luaL_error(L, "Usage: space:frommap(map, opts)");
> +}
> +
>  void
>  box_lua_space_init(struct lua_State *L)
>  {
> @@ -464,4 +579,12 @@ box_lua_space_init(struct lua_State *L)
>  	lua_pushnumber(L, VCLOCK_MAX);
>  	lua_setfield(L, -2, "REPLICA_MAX");
>  	lua_pop(L, 2); /* box, schema */
> +
> +	static const struct luaL_Reg space_internal_lib[] = {
> +		{"frommap", lbox_space_frommap},
> +		{"_cptr", lbox_space_ptr_cached},
> +		{NULL, NULL}
> +	};
> +	luaL_register(L, "box.internal.space", space_internal_lib);
> +	lua_pop(L, 1);
>  }
> diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
> index 7ca4299..92a7185 100644
> --- a/src/box/lua/tuple.c
> +++ b/src/box/lua/tuple.c
> @@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
>  	return *(struct tuple **) data;
>  }
>  
> -static int
> +int
>  lbox_tuple_new(lua_State *L)
>  {
>  	int argc = lua_gettop(L);
> diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
> index 0965644..5d7062e 100644
> --- a/src/box/lua/tuple.h
> +++ b/src/box/lua/tuple.h
> @@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
>  
>  /** \endcond public */
>  
> +int
> +lbox_tuple_new(struct lua_State *L);
> +
>  static inline int
>  luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
>  {
> diff --git a/test/box/tuple.result b/test/box/tuple.result
> index ec5ed90..2dd8f0d 100644
> --- a/test/box/tuple.result
> +++ b/test/box/tuple.result
> @@ -1060,6 +1060,101 @@ box.tuple.bsize == t.bsize
>  ---
>  - true
>  ...
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +---
> +...
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +---
> +...
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +---
> +...
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +---
> +...
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +---
> +...
> +s = box.schema.create_space('test', {format = format})
> +---
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +---
> +- [2, 3, null, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +---
> +- null
> +- Unknown field 'eee'
> +...
> +s:frommap()
> +---
> +- error: 'Usage: space:frommap(map, opts)'
> +...
> +s:frommap({})
> +---
> +- []
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +---
> +- [2, null, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +---
> +- [2, 4, 3, 1]
> +...
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +---
> +...
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +s:replace(tuple)
> +---
> +- [2, 4, 3, 1]
> +...
> +_, err = box.internal.space._cptr(s)
> +---
> +...
> +assert(not err)
> +---
> +- true
> +...
> +s:drop()
> +---
> +...
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +assert(err ~= nil)
> +---
> +- true
> +...
> +_, err = box.internal.space._cptr(s)
> +---
> +...
> +assert(err ~= nil)
> +---
> +- true
> +...
>  test_run:cmd("clear filter")
>  ---
>  - true
> diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
> index 0b3392d..03e6194 100644
> --- a/test/box/tuple.test.lua
> +++ b/test/box/tuple.test.lua
> @@ -349,4 +349,33 @@ box.tuple.update == t.update
>  box.tuple.upsert == t.upsert
>  box.tuple.bsize == t.bsize
>  
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +s = box.schema.create_space('test', {format = format})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +s:frommap()
> +s:frommap({})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:replace(tuple)
> +_, err = box.internal.space._cptr(s)
> +assert(not err)
> +s:drop()
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +assert(err ~= nil)
> +_, err = box.internal.space._cptr(s)
> +assert(err ~= nil)
> +
>  test_run:cmd("clear filter")
> -- 
> 2.7.4
> 
> 

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-06 15:44                   ` [tarantool-patches] " Konstantin Osipov
@ 2018-04-09  8:30                     ` Kirill Shcherbatov
  2018-04-09 10:44                       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-09  8:30 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

I've accounted Kostya's comments. 
Going to measure performance.


From ef47d3536b845a84b52be6c84a66aef12fbe8561 Mon Sep 17 00:00:00 2001
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
Date: Mon, 9 Apr 2018 11:24:38 +0300
Subject: [PATCH] Moved obtaining cptr to Lua code.

---
 src/box/lua/schema.lua | 33 +++++++++++++++++++++++++---
 src/box/lua/space.cc   | 59 ++++++--------------------------------------------
 2 files changed, 37 insertions(+), 55 deletions(-)

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 323c2d6..a0f2d25 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1072,6 +1072,19 @@ end
 
 internal.check_iterator_type = check_iterator_type -- export for net.box
 
+-- Get a C space structure pointer by space id using cache.
+-- Update cache if necessary.
+-- The _schema_version field is a cache invalidation rule.
+function space_cptr_cached(space)
+    local schema_version = builtin.box_schema_version()
+    if space._schema_version == schema_version then
+        return space._cptr
+    end
+    space._cptr = builtin.space_by_id(space.id)
+    space._schema_version = schema_version
+    return space._cptr
+end
+
 function box.schema.space.bless(space)
     local index_mt = {}
     -- __len and __index
@@ -1320,7 +1333,7 @@ function box.schema.space.bless(space)
     end
     space_mt.bsize = function(space)
         check_space_arg(space, 'bsize')
-        local s = builtin.space_by_id(space.id)
+        local s = space_cptr_cached(space)
         if s == nil then
             box.error(box.error.NO_SUCH_SPACE, space.name)
         end
@@ -1413,13 +1426,20 @@ function box.schema.space.bless(space)
     end
     space_mt.run_triggers = function(space, yesno)
         check_space_arg(space, 'run_triggers')
-        local s = builtin.space_by_id(space.id)
+        local s = space_cptr_cached(space)
         if s == nil then
             box.error(box.error.NO_SUCH_SPACE, space.name)
         end
         builtin.space_run_triggers(s, yesno)
     end
-    space_mt.frommap = box.internal.space.frommap
+    space_mt.frommap = function(space, map, opts)
+        -- update the cache
+        local cptr = space_cptr_cached(space)
+        if cptr == nil then
+            return box.NULL, "Space " .. space.name .. " does not exist"
+        end
+        return box.internal.space.frommap(space, map, opts)
+    end
     space_mt.__index = space_mt
 
     setmetatable(space, space_mt)
@@ -2234,5 +2254,12 @@ box.internal.schema = {}
 box.internal.schema.init = function()
     box_sequence_init()
 end
+box.internal.space._cptr = function(space)
+    local cptr = space_cptr_cached(space)
+    if cptr == nil then
+        return box.NULL, "Space " .. space.name .. " does not exist"
+    end
+    return cptr
+end
 
 box.NULL = msgpack.NULL
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 2723baf..9383096 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -397,51 +397,6 @@ static struct trigger on_alter_space_in_lua = {
 };
 
 /**
- * Get a C pointer for space structure.
- * Caches the result. Cleans the user(negative) stack itself.
- * @param Lua space object at top of the Lua stack.
- * @retval not nil C structure pointer.
- * @retval nil, err Can not find underlying space, error
- *         description is returned.
- */
-static int
-lbox_space_ptr_cached(struct lua_State *L)
-{
-	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
-		luaL_error(L, "Usage: space:_cptr()");
-
-	lua_getfield(L, 1, "_schema_version");
-	uint64_t schema_version = luaL_touint64(L, -1);
-	lua_pop(L, 1);
-	uint64_t global_shema_version = box_schema_version();
-
-	void *space = NULL;
-	if (schema_version == global_shema_version) {
-		lua_getfield(L, 1, "_cptr");
-		space = (void *)lua_topointer(L, -1);
-	} else {
-		lua_getfield(L, 1, "id");
-		uint32_t id = luaL_touint64(L, -1);
-		space = space_by_id(id);
-		if (space == NULL) {
-			lua_pushnil(L);
-			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
-						     "doesn't exist", id));
-			return 2;
-		}
-
-		/** update the cache */
-		luaL_pushuint64(L, global_shema_version);
-		lua_setfield(L, 1, "_schema_version");
-		lua_pushlightuserdata(L, space);
-		lua_setfield(L, 1, "_cptr");
-	}
-	lua_pop(L, 1);
-	lua_pushlightuserdata(L, space);
-	return 1;
-}
-
-/**
  * Make a tuple or a table Lua object by map.
  * @param Lua space object.
  * @param Lua map table object.
@@ -456,11 +411,11 @@ lbox_space_frommap(struct lua_State *L)
 {
 	struct tuple_dictionary *dict = NULL;
 	struct space *space = NULL;
-	int rc, argc = lua_gettop(L);
+	int argc = lua_gettop(L);
 	bool table = false;
 	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
 		goto usage_error;
-	if (argc == 3) {
+	if (argc == 3 && !lua_isnil(L, 3)) {
 		if (!lua_istable(L, 3))
 			goto usage_error;
 		lua_getfield(L, 3, "table");
@@ -469,10 +424,11 @@ lbox_space_frommap(struct lua_State *L)
 		table = lua_toboolean(L, -1);
 	}
 
-	rc = lbox_space_ptr_cached(L);
-	if (rc != 1)
-		return rc;
-	space = (struct space *) lua_topointer(L, -1);
+	lua_getfield(L, 1, "_cptr");
+	space = (struct space *)lua_topointer(L, -1);
+	/* validation is in the Lua code */
+	assert(space);
+
 	dict = space->format->dict;
 	lua_createtable(L, space->def->field_count, 0);
 
@@ -582,7 +538,6 @@ box_lua_space_init(struct lua_State *L)
 
 	static const struct luaL_Reg space_internal_lib[] = {
 		{"frommap", lbox_space_frommap},
-		{"_cptr", lbox_space_ptr_cached},
 		{NULL, NULL}
 	};
 	luaL_register(L, "box.internal.space", space_internal_lib);
-- 
2.7.4

On 06.04.2018 18:44, Konstantin Osipov wrote:
> * Kirill Shcherbatov <kshcherbatov@tarantool.org> [18/04/05 10:24]:
> 
>> This feature is designed to build tuple or table object by map.
>> Tuple output is default. If you wish to make a table, specify table = true option.
>> subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> 
> What are the performance results from adding the cache? 
> 
> How much do we win?
> 
> It's OK to export _schema_version to Lua as extern int via FFI, 
> and avoid a function call.
> 
>> +static int
>> +lbox_space_ptr_cached(struct lua_State *L)
> 
> The fast path of this function should be in plain Lua.
> 
> You have to re-fetch the space via lookup in a slowpath only, 
> when schema version has changed.
> 
> 
> What about using the cptr in all other functions? 
> 
> 
>> +{
>> +	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
>> +		luaL_error(L, "Usage: space:_cptr()");
>> +
>> +	lua_getfield(L, 1, "_schema_version");
>> +	uint64_t schema_version = luaL_touint64(L, -1);
>> +	lua_pop(L, 1);
>> +	uint64_t global_shema_version = box_schema_version();
>> +
>> +	void *space = NULL;
>> +	if (schema_version == global_shema_version) {
>> +		lua_getfield(L, 1, "_cptr");
>> +		space = (void *)lua_topointer(L, -1);
>> +	} else {
>> +		lua_getfield(L, 1, "id");
>> +		uint32_t id = luaL_touint64(L, -1);
>> +		space = space_by_id(id);
>> +		if (space == NULL) {
>> +			lua_pushnil(L);
>> +			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
>> +						     "doesn't exist", id));
>> +			return 2;
>> +		}
>> +
>> +		/** update the cache */
>> +		luaL_pushuint64(L, global_shema_version);
>> +		lua_setfield(L, 1, "_schema_version");
>> +		lua_pushlightuserdata(L, space);
>> +		lua_setfield(L, 1, "_cptr");
>> +	}
>> +	lua_pop(L, 1);
>> +	lua_pushlightuserdata(L, space);
>> +	return 1;
>> +}
>> +
>> +/**
>> + * Make a tuple or a table Lua object by map.
>> + * @param Lua space object.
>> + * @param Lua map table object.
>> + * @param Lua opts table object (optional).
>> + * @retval not nil A tuple or a table conforming to a space
>> + *         format.
>> + * @retval nil, err Can not built a tuple. A reason is returned in
>> + *         the second value.
>> + */
>> +static int
>> +lbox_space_frommap(struct lua_State *L)
>> +{
>> +	struct tuple_dictionary *dict = NULL;
>> +	struct space *space = NULL;
>> +	int rc, argc = lua_gettop(L);
>> +	bool table = false;
>> +	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
>> +		goto usage_error;
>> +	if (argc == 3) {
>> +		if (!lua_istable(L, 3))
>> +			goto usage_error;
>> +		lua_getfield(L, 3, "table");
>> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
>> +			goto usage_error;
>> +		table = lua_toboolean(L, -1);
>> +	}
>> +
>> +	rc = lbox_space_ptr_cached(L);
>> +	if (rc != 1)
>> +		return rc;
>> +	space = (struct space *) lua_topointer(L, -1);
>> +	dict = space->format->dict;
>> +	lua_createtable(L, space->def->field_count, 0);
>> +
>> +	lua_pushnil(L);
>> +	while (lua_next(L, 2) != 0) {
>> +		uint32_t fieldno;
>> +		size_t key_len;
>> +		const char *key = lua_tolstring(L, -2, &key_len);
>> +		uint32_t key_hash = lua_hashstring(L, -2);
>> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
>> +					  &fieldno)) {
>> +			lua_pushnil(L);
>> +			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
>> +						     key));
>> +			return 2;
>> +		}
>> +		lua_rawseti(L, -3, fieldno+1);
>> +	}
>> +	if (table)
>> +		return 1;
>> +
>> +	lua_replace(L, 1);
>> +	lua_settop(L, 1);
>> +	return lbox_tuple_new(L);
>> +usage_error:
>> +	return luaL_error(L, "Usage: space:frommap(map, opts)");
>> +}
>> +
>>  void
>>  box_lua_space_init(struct lua_State *L)
>>  {
>> @@ -464,4 +579,12 @@ box_lua_space_init(struct lua_State *L)
>>  	lua_pushnumber(L, VCLOCK_MAX);
>>  	lua_setfield(L, -2, "REPLICA_MAX");
>>  	lua_pop(L, 2); /* box, schema */
>> +
>> +	static const struct luaL_Reg space_internal_lib[] = {
>> +		{"frommap", lbox_space_frommap},
>> +		{"_cptr", lbox_space_ptr_cached},
>> +		{NULL, NULL}
>> +	};
>> +	luaL_register(L, "box.internal.space", space_internal_lib);
>> +	lua_pop(L, 1);
>>  }
>> diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
>> index 7ca4299..92a7185 100644
>> --- a/src/box/lua/tuple.c
>> +++ b/src/box/lua/tuple.c
>> @@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
>>  	return *(struct tuple **) data;
>>  }
>>  
>> -static int
>> +int
>>  lbox_tuple_new(lua_State *L)
>>  {
>>  	int argc = lua_gettop(L);
>> diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
>> index 0965644..5d7062e 100644
>> --- a/src/box/lua/tuple.h
>> +++ b/src/box/lua/tuple.h
>> @@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
>>  
>>  /** \endcond public */
>>  
>> +int
>> +lbox_tuple_new(struct lua_State *L);
>> +
>>  static inline int
>>  luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
>>  {
>> diff --git a/test/box/tuple.result b/test/box/tuple.result
>> index ec5ed90..2dd8f0d 100644
>> --- a/test/box/tuple.result
>> +++ b/test/box/tuple.result
>> @@ -1060,6 +1060,101 @@ box.tuple.bsize == t.bsize
>>  ---
>>  - true
>>  ...
>> +--
>> +-- gh-3282: space:frommap().
>> +--
>> +format = {}
>> +---
>> +...
>> +format[1] = {name = 'aaa', type = 'unsigned'}
>> +---
>> +...
>> +format[2] = {name = 'bbb', type = 'unsigned'}
>> +---
>> +...
>> +format[3] = {name = 'ccc', type = 'unsigned'}
>> +---
>> +...
>> +format[4] = {name = 'ddd', type = 'unsigned'}
>> +---
>> +...
>> +s = box.schema.create_space('test', {format = format})
>> +---
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +---
>> +- [2, 4, 3, 1]
>> +...
>> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
>> +---
>> +- [2, 3, null, 1]
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
>> +---
>> +- null
>> +- Unknown field 'eee'
>> +...
>> +s:frommap()
>> +---
>> +- error: 'Usage: space:frommap(map, opts)'
>> +...
>> +s:frommap({})
>> +---
>> +- []
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
>> +---
>> +- - 2
>> +  - 4
>> +  - 3
>> +  - 1
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
>> +---
>> +- [2, 4, 3, 1]
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
>> +---
>> +- [2, null, 3, 1]
>> +...
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
>> +---
>> +- [2, 4, 3, 1]
>> +...
>> +_ = s:create_index('primary', {parts = {'aaa'}})
>> +---
>> +...
>> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +---
>> +...
>> +s:replace(tuple)
>> +---
>> +- [2, 4, 3, 1]
>> +...
>> +_, err = box.internal.space._cptr(s)
>> +---
>> +...
>> +assert(not err)
>> +---
>> +- true
>> +...
>> +s:drop()
>> +---
>> +...
>> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +---
>> +...
>> +assert(err ~= nil)
>> +---
>> +- true
>> +...
>> +_, err = box.internal.space._cptr(s)
>> +---
>> +...
>> +assert(err ~= nil)
>> +---
>> +- true
>> +...
>>  test_run:cmd("clear filter")
>>  ---
>>  - true
>> diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
>> index 0b3392d..03e6194 100644
>> --- a/test/box/tuple.test.lua
>> +++ b/test/box/tuple.test.lua
>> @@ -349,4 +349,33 @@ box.tuple.update == t.update
>>  box.tuple.upsert == t.upsert
>>  box.tuple.bsize == t.bsize
>>  
>> +--
>> +-- gh-3282: space:frommap().
>> +--
>> +format = {}
>> +format[1] = {name = 'aaa', type = 'unsigned'}
>> +format[2] = {name = 'bbb', type = 'unsigned'}
>> +format[3] = {name = 'ccc', type = 'unsigned'}
>> +format[4] = {name = 'ddd', type = 'unsigned'}
>> +s = box.schema.create_space('test', {format = format})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
>> +s:frommap()
>> +s:frommap({})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
>> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
>> +_ = s:create_index('primary', {parts = {'aaa'}})
>> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +s:replace(tuple)
>> +_, err = box.internal.space._cptr(s)
>> +assert(not err)
>> +s:drop()
>> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
>> +assert(err ~= nil)
>> +_, err = box.internal.space._cptr(s)
>> +assert(err ~= nil)
>> +
>>  test_run:cmd("clear filter")
>> -- 
>> 2.7.4
>>
>>
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-09  8:30                     ` Kirill Shcherbatov
@ 2018-04-09 10:44                       ` Vladislav Shpilevoy
  2018-04-09 17:23                         ` Kirill Shcherbatov
  0 siblings, 1 reply; 18+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-09 10:44 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches

Hello. See 7 comments below.

On 09/04/2018 11:30, Kirill Shcherbatov wrote:
> I've accounted Kostya's comments.
> Going to measure performance.
> 
> 
>  From ef47d3536b845a84b52be6c84a66aef12fbe8561 Mon Sep 17 00:00:00 2001
> From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
> Date: Mon, 9 Apr 2018 11:24:38 +0300
> Subject: [PATCH] Moved obtaining cptr to Lua code.
> 
> ---
>   src/box/lua/schema.lua | 33 +++++++++++++++++++++++++---
>   src/box/lua/space.cc   | 59 ++++++--------------------------------------------
>   2 files changed, 37 insertions(+), 55 deletions(-)
> 
> diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
> index 323c2d6..a0f2d25 100644
> --- a/src/box/lua/schema.lua
> +++ b/src/box/lua/schema.lua
> @@ -1072,6 +1072,19 @@ end
>   
>   internal.check_iterator_type = check_iterator_type -- export for net.box
>   
> +-- Get a C space structure pointer by space id using cache.
> +-- Update cache if necessary.
> +-- The _schema_version field is a cache invalidation rule.
> +function space_cptr_cached(space)
> +    local schema_version = builtin.box_schema_version()
> +    if space._schema_version == schema_version then
> +        return space._cptr
> +    end
> +    space._cptr = builtin.space_by_id(space.id)
> +    space._schema_version = schema_version
> +    return space._cptr
> +end > +
>   function box.schema.space.bless(space)
>       local index_mt = {}
>       -- __len and __index
> @@ -1320,7 +1333,7 @@ function box.schema.space.bless(space)
>       end
>       space_mt.bsize = function(space)
>           check_space_arg(space, 'bsize')
> -        local s = builtin.space_by_id(space.id)
> +        local s = space_cptr_cached(space)

1. I know, it is very small one-line fix, but lets do it in a separate 
patch.

>           if s == nil then
>               box.error(box.error.NO_SUCH_SPACE, space.name)
>           end
> @@ -1413,13 +1426,20 @@ function box.schema.space.bless(space)
>       end
>       space_mt.run_triggers = function(space, yesno)
>           check_space_arg(space, 'run_triggers')
> -        local s = builtin.space_by_id(space.id)
> +        local s = space_cptr_cached(space)

2. This too.

>           if s == nil then
>               box.error(box.error.NO_SUCH_SPACE, space.name)
>           end
>           builtin.space_run_triggers(s, yesno)
>       end
> -    space_mt.frommap = box.internal.space.frommap
> +    space_mt.frommap = function(space, map, opts)
> +        -- update the cache
> +        local cptr = space_cptr_cached(space)
> +        if cptr == nil then
> +            return box.NULL, "Space " .. space.name .. " does not exist"

3. box.NULL == nil, but `if box.NULL` check is true, so box.NULL behaves 
different from nil. Please, return exactly nil.

> +        end
> +        return box.internal.space.frommap(space, map, opts)
> +    end
>       space_mt.__index = space_mt
>   
>       setmetatable(space, space_mt)
> @@ -2234,5 +2254,12 @@ box.internal.schema = {}
>   box.internal.schema.init = function()
>       box_sequence_init()
>   end
> +box.internal.space._cptr = function(space)
> +    local cptr = space_cptr_cached(space)
> +    if cptr == nil then
> +        return box.NULL, "Space " .. space.name .. " does not exist"
> +    end
> +    return cptr
> +end

4. You do not need this function anymore. It is unused.

>   
>   box.NULL = msgpack.NULL
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 2723baf..9383096 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -397,51 +397,6 @@ static struct trigger on_alter_space_in_lua = {
>   };
>   
>   /**
> - * Get a C pointer for space structure.
> - * Caches the result. Cleans the user(negative) stack itself.
> - * @param Lua space object at top of the Lua stack.
> - * @retval not nil C structure pointer.
> - * @retval nil, err Can not find underlying space, error
> - *         description is returned.
> - */
> -static int
> -lbox_space_ptr_cached(struct lua_State *L)
> -{
> -	if (lua_gettop(L) < 1 || !lua_istable(L, 1))
> -		luaL_error(L, "Usage: space:_cptr()");
> -
> -	lua_getfield(L, 1, "_schema_version");
> -	uint64_t schema_version = luaL_touint64(L, -1);
> -	lua_pop(L, 1);
> -	uint64_t global_shema_version = box_schema_version();
> -
> -	void *space = NULL;
> -	if (schema_version == global_shema_version) {
> -		lua_getfield(L, 1, "_cptr");
> -		space = (void *)lua_topointer(L, -1);
> -	} else {
> -		lua_getfield(L, 1, "id");
> -		uint32_t id = luaL_touint64(L, -1);
> -		space = space_by_id(id);
> -		if (space == NULL) {
> -			lua_pushnil(L);
> -			lua_pushstring(L, tt_sprintf("Space with id '%d' "\
> -						     "doesn't exist", id));
> -			return 2;
> -		}
> -
> -		/** update the cache */
> -		luaL_pushuint64(L, global_shema_version);
> -		lua_setfield(L, 1, "_schema_version");
> -		lua_pushlightuserdata(L, space);
> -		lua_setfield(L, 1, "_cptr");
> -	}
> -	lua_pop(L, 1);
> -	lua_pushlightuserdata(L, space);
> -	return 1;
> -}
> -
> -/**
>    * Make a tuple or a table Lua object by map.
>    * @param Lua space object.
>    * @param Lua map table object.
> @@ -456,11 +411,11 @@ lbox_space_frommap(struct lua_State *L)
>   {
>   	struct tuple_dictionary *dict = NULL;
>   	struct space *space = NULL;
> -	int rc, argc = lua_gettop(L);
> +	int argc = lua_gettop(L);
>   	bool table = false;
>   	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
>   		goto usage_error;
> -	if (argc == 3) {
> +	if (argc == 3 && !lua_isnil(L, 3)) {

5. Why do you need this? Error on argc == 3 and argv[3] != nil is the 
same as error on argc > 2.

>   		if (!lua_istable(L, 3))
>   			goto usage_error;
>   		lua_getfield(L, 3, "table");
> @@ -469,10 +424,11 @@ lbox_space_frommap(struct lua_State *L)
>   		table = lua_toboolean(L, -1);
>   	}
>   
> -	rc = lbox_space_ptr_cached(L);
> -	if (rc != 1)
> -		return rc;
> -	space = (struct space *) lua_topointer(L, -1);
> +	lua_getfield(L, 1, "_cptr");
> +	space = (struct space *)lua_topointer(L, -1);

6. Unnecessary diff.

> +	/* validation is in the Lua code */
> +	assert(space);

7. Please, use explicit != NULL, start a sentence with a capital letter, 
and put a dot at the end.

> +
>   	dict = space->format->dict;
>   	lua_createtable(L, space->def->field_count, 0);
>   
> @@ -582,7 +538,6 @@ box_lua_space_init(struct lua_State *L)
>   
>   	static const struct luaL_Reg space_internal_lib[] = {
>   		{"frommap", lbox_space_frommap},
> -		{"_cptr", lbox_space_ptr_cached},
>   		{NULL, NULL}
>   	};
>   	luaL_register(L, "box.internal.space", space_internal_lib);
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-09 10:44                       ` Vladislav Shpilevoy
@ 2018-04-09 17:23                         ` Kirill Shcherbatov
  2018-04-09 17:45                           ` Vladislav Shpilevoy
  2018-04-10 10:31                           ` Vladimir Davydov
  0 siblings, 2 replies; 18+ messages in thread
From: Kirill Shcherbatov @ 2018-04-09 17:23 UTC (permalink / raw)
  To: tarantool-patches; +Cc: v.shpilevoy

From 0cb37cac97056398c39f723aef81053cc4550485 Mon Sep 17 00:00:00 2001
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
Date: Wed, 4 Apr 2018 19:32:40 +0300
Subject: [PATCH] schema:frommap() to create table or tuple

This feature is designed to build tuple or table object by map.
Tuple output is default. If you wish to make a table, specify table = true option.
subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})

Closes #3282
---
 src/box/lua/schema.lua  |  1 +
 src/box/lua/space.cc    | 76 +++++++++++++++++++++++++++++++++++++++++++++-
 src/box/lua/tuple.c     |  2 +-
 src/box/lua/tuple.h     |  3 ++
 test/box/tuple.result   | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/box/tuple.test.lua | 25 +++++++++++++++
 6 files changed, 186 insertions(+), 2 deletions(-)

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 30c6bc6..323c2d6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1419,6 +1419,7 @@ function box.schema.space.bless(space)
         end
         builtin.space_run_triggers(s, yesno)
     end
+    space_mt.frommap = box.internal.space.frommap
     space_mt.__index = space_mt
 
     setmetatable(space, space_mt)
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 29a9aca..e358934 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -178,7 +178,6 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushboolean(L, space_index(space, 0) != 0);
 	lua_settable(L, i);
 
-
         /* space:on_replace */
         lua_pushstring(L, "on_replace");
         lua_pushcfunction(L, lbox_space_on_replace);
@@ -386,6 +385,74 @@ static struct trigger on_alter_space_in_lua = {
 	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
 };
 
+/**
+ * Make a tuple or a table Lua object by map.
+ * @param Lua space object.
+ * @param Lua map table object.
+ * @param Lua opts table object (optional).
+ * @retval not nil A tuple or a table conforming to a space
+ *         format.
+ * @retval nil, err Can not built a tuple. A reason is returned in
+ *         the second value.
+ */
+static int
+lbox_space_frommap(struct lua_State *L)
+{
+	struct tuple_dictionary *dict = NULL;
+	uint32_t id = 0;
+	struct space *space = NULL;
+	int argc = lua_gettop(L);
+	bool table = false;
+	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
+		goto usage_error;
+	if (argc == 3) {
+		if (!lua_istable(L, 3))
+			goto usage_error;
+		lua_getfield(L, 3, "table");
+		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
+			goto usage_error;
+		table = lua_toboolean(L, -1);
+	}
+
+	lua_getfield(L, 1, "id");
+	id = (int)lua_tointeger(L, -1);
+	space = space_by_id(id);
+	if (!space) {
+		lua_pushnil(L);
+		lua_pushstring(L, tt_sprintf("Space with id '%d' "\
+					     "doesn't exist", id));
+		return 2;
+	}
+	assert(space->format != NULL);
+
+	dict = space->format->dict;
+	lua_createtable(L, space->def->field_count, 0);
+
+	lua_pushnil(L);
+	while (lua_next(L, 2) != 0) {
+		uint32_t fieldno;
+		size_t key_len;
+		const char *key = lua_tolstring(L, -2, &key_len);
+		uint32_t key_hash = lua_hashstring(L, -2);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
+					  &fieldno)) {
+			lua_pushnil(L);
+			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
+						     key));
+			return 2;
+		}
+		lua_rawseti(L, -3, fieldno+1);
+	}
+	if (table)
+		return 1;
+
+	lua_replace(L, 1);
+	lua_settop(L, 1);
+	return lbox_tuple_new(L);
+usage_error:
+	return luaL_error(L, "Usage: space:frommap(map, opts)");
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -464,4 +531,11 @@ box_lua_space_init(struct lua_State *L)
 	lua_pushnumber(L, VCLOCK_MAX);
 	lua_setfield(L, -2, "REPLICA_MAX");
 	lua_pop(L, 2); /* box, schema */
+
+	static const struct luaL_Reg space_internal_lib[] = {
+		{"frommap", lbox_space_frommap},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.internal.space", space_internal_lib);
+	lua_pop(L, 1);
 }
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 7ca4299..92a7185 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
 	return *(struct tuple **) data;
 }
 
-static int
+int
 lbox_tuple_new(lua_State *L)
 {
 	int argc = lua_gettop(L);
diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
index 0965644..5d7062e 100644
--- a/src/box/lua/tuple.h
+++ b/src/box/lua/tuple.h
@@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
 
 /** \endcond public */
 
+int
+lbox_tuple_new(struct lua_State *L);
+
 static inline int
 luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
 {
diff --git a/test/box/tuple.result b/test/box/tuple.result
index ec5ed90..e035cb9 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -1060,6 +1060,87 @@ box.tuple.bsize == t.bsize
 ---
 - true
 ...
+--
+-- gh-3282: space:frommap().
+--
+format = {}
+---
+...
+format[1] = {name = 'aaa', type = 'unsigned'}
+---
+...
+format[2] = {name = 'bbb', type = 'unsigned'}
+---
+...
+format[3] = {name = 'ccc', type = 'unsigned'}
+---
+...
+format[4] = {name = 'ddd', type = 'unsigned'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+---
+- [2, 3, null, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+---
+- null
+- Unknown field 'eee'
+...
+s:frommap()
+---
+- error: 'Usage: space:frommap(map, opts)'
+...
+s:frommap({})
+---
+- []
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+---
+- [2, null, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+---
+- [2, 4, 3, 1]
+...
+_ = s:create_index('primary', {parts = {'aaa'}})
+---
+...
+tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+...
+s:replace(tuple)
+---
+- [2, 4, 3, 1]
+...
+s:drop()
+---
+...
+_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+...
+assert(err ~= nil)
+---
+- true
+...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
index 0b3392d..9df978d 100644
--- a/test/box/tuple.test.lua
+++ b/test/box/tuple.test.lua
@@ -349,4 +349,29 @@ box.tuple.update == t.update
 box.tuple.upsert == t.upsert
 box.tuple.bsize == t.bsize
 
+--
+-- gh-3282: space:frommap().
+--
+format = {}
+format[1] = {name = 'aaa', type = 'unsigned'}
+format[2] = {name = 'bbb', type = 'unsigned'}
+format[3] = {name = 'ccc', type = 'unsigned'}
+format[4] = {name = 'ddd', type = 'unsigned'}
+s = box.schema.create_space('test', {format = format})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+s:frommap()
+s:frommap({})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+_ = s:create_index('primary', {parts = {'aaa'}})
+tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+s:replace(tuple)
+s:drop()
+_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+assert(err ~= nil)
+
 test_run:cmd("clear filter")
-- 
2.7.4

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-09 17:23                         ` Kirill Shcherbatov
@ 2018-04-09 17:45                           ` Vladislav Shpilevoy
  2018-04-10 10:31                           ` Vladimir Davydov
  1 sibling, 0 replies; 18+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-09 17:45 UTC (permalink / raw)
  To: tarantool-patches, Kirill Shcherbatov

LGTM. Vova, please, take a look.

On 09/04/2018 20:23, Kirill Shcherbatov wrote:
>  From 0cb37cac97056398c39f723aef81053cc4550485 Mon Sep 17 00:00:00 2001
> From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
> Date: Wed, 4 Apr 2018 19:32:40 +0300
> Subject: [PATCH] schema:frommap() to create table or tuple
> 
> This feature is designed to build tuple or table object by map.
> Tuple output is default. If you wish to make a table, specify table = true option.
> subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> 
> Closes #3282
> ---
>   src/box/lua/schema.lua  |  1 +
>   src/box/lua/space.cc    | 76 +++++++++++++++++++++++++++++++++++++++++++++-
>   src/box/lua/tuple.c     |  2 +-
>   src/box/lua/tuple.h     |  3 ++
>   test/box/tuple.result   | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
>   test/box/tuple.test.lua | 25 +++++++++++++++
>   6 files changed, 186 insertions(+), 2 deletions(-)
> 
> diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
> index 30c6bc6..323c2d6 100644
> --- a/src/box/lua/schema.lua
> +++ b/src/box/lua/schema.lua
> @@ -1419,6 +1419,7 @@ function box.schema.space.bless(space)
>           end
>           builtin.space_run_triggers(s, yesno)
>       end
> +    space_mt.frommap = box.internal.space.frommap
>       space_mt.__index = space_mt
>   
>       setmetatable(space, space_mt)
> diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
> index 29a9aca..e358934 100644
> --- a/src/box/lua/space.cc
> +++ b/src/box/lua/space.cc
> @@ -178,7 +178,6 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
>   	lua_pushboolean(L, space_index(space, 0) != 0);
>   	lua_settable(L, i);
>   
> -
>           /* space:on_replace */
>           lua_pushstring(L, "on_replace");
>           lua_pushcfunction(L, lbox_space_on_replace);
> @@ -386,6 +385,74 @@ static struct trigger on_alter_space_in_lua = {
>   	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
>   };
>   
> +/**
> + * Make a tuple or a table Lua object by map.
> + * @param Lua space object.
> + * @param Lua map table object.
> + * @param Lua opts table object (optional).
> + * @retval not nil A tuple or a table conforming to a space
> + *         format.
> + * @retval nil, err Can not built a tuple. A reason is returned in
> + *         the second value.
> + */
> +static int
> +lbox_space_frommap(struct lua_State *L)
> +{
> +	struct tuple_dictionary *dict = NULL;
> +	uint32_t id = 0;
> +	struct space *space = NULL;
> +	int argc = lua_gettop(L);
> +	bool table = false;
> +	if (argc < 2 || argc > 3 || !lua_istable(L, 2))
> +		goto usage_error;
> +	if (argc == 3) {
> +		if (!lua_istable(L, 3))
> +			goto usage_error;
> +		lua_getfield(L, 3, "table");
> +		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
> +			goto usage_error;
> +		table = lua_toboolean(L, -1);
> +	}
> +
> +	lua_getfield(L, 1, "id");
> +	id = (int)lua_tointeger(L, -1);
> +	space = space_by_id(id);
> +	if (!space) {
> +		lua_pushnil(L);
> +		lua_pushstring(L, tt_sprintf("Space with id '%d' "\
> +					     "doesn't exist", id));
> +		return 2;
> +	}
> +	assert(space->format != NULL);
> +
> +	dict = space->format->dict;
> +	lua_createtable(L, space->def->field_count, 0);
> +
> +	lua_pushnil(L);
> +	while (lua_next(L, 2) != 0) {
> +		uint32_t fieldno;
> +		size_t key_len;
> +		const char *key = lua_tolstring(L, -2, &key_len);
> +		uint32_t key_hash = lua_hashstring(L, -2);
> +		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
> +					  &fieldno)) {
> +			lua_pushnil(L);
> +			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
> +						     key));
> +			return 2;
> +		}
> +		lua_rawseti(L, -3, fieldno+1);
> +	}
> +	if (table)
> +		return 1;
> +
> +	lua_replace(L, 1);
> +	lua_settop(L, 1);
> +	return lbox_tuple_new(L);
> +usage_error:
> +	return luaL_error(L, "Usage: space:frommap(map, opts)");
> +}
> +
>   void
>   box_lua_space_init(struct lua_State *L)
>   {
> @@ -464,4 +531,11 @@ box_lua_space_init(struct lua_State *L)
>   	lua_pushnumber(L, VCLOCK_MAX);
>   	lua_setfield(L, -2, "REPLICA_MAX");
>   	lua_pop(L, 2); /* box, schema */
> +
> +	static const struct luaL_Reg space_internal_lib[] = {
> +		{"frommap", lbox_space_frommap},
> +		{NULL, NULL}
> +	};
> +	luaL_register(L, "box.internal.space", space_internal_lib);
> +	lua_pop(L, 1);
>   }
> diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
> index 7ca4299..92a7185 100644
> --- a/src/box/lua/tuple.c
> +++ b/src/box/lua/tuple.c
> @@ -90,7 +90,7 @@ luaT_istuple(struct lua_State *L, int narg)
>   	return *(struct tuple **) data;
>   }
>   
> -static int
> +int
>   lbox_tuple_new(lua_State *L)
>   {
>   	int argc = lua_gettop(L);
> diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
> index 0965644..5d7062e 100644
> --- a/src/box/lua/tuple.h
> +++ b/src/box/lua/tuple.h
> @@ -66,6 +66,9 @@ luaT_istuple(struct lua_State *L, int idx);
>   
>   /** \endcond public */
>   
> +int
> +lbox_tuple_new(struct lua_State *L);
> +
>   static inline int
>   luaT_pushtupleornil(struct lua_State *L, struct tuple *tuple)
>   {
> diff --git a/test/box/tuple.result b/test/box/tuple.result
> index ec5ed90..e035cb9 100644
> --- a/test/box/tuple.result
> +++ b/test/box/tuple.result
> @@ -1060,6 +1060,87 @@ box.tuple.bsize == t.bsize
>   ---
>   - true
>   ...
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +---
> +...
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +---
> +...
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +---
> +...
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +---
> +...
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +---
> +...
> +s = box.schema.create_space('test', {format = format})
> +---
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +---
> +- [2, 3, null, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +---
> +- null
> +- Unknown field 'eee'
> +...
> +s:frommap()
> +---
> +- error: 'Usage: space:frommap(map, opts)'
> +...
> +s:frommap({})
> +---
> +- []
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +---
> +- - 2
> +  - 4
> +  - 3
> +  - 1
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +---
> +- [2, 4, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +---
> +- [2, null, 3, 1]
> +...
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +---
> +- [2, 4, 3, 1]
> +...
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +---
> +...
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +s:replace(tuple)
> +---
> +- [2, 4, 3, 1]
> +...
> +s:drop()
> +---
> +...
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +---
> +...
> +assert(err ~= nil)
> +---
> +- true
> +...
>   test_run:cmd("clear filter")
>   ---
>   - true
> diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
> index 0b3392d..9df978d 100644
> --- a/test/box/tuple.test.lua
> +++ b/test/box/tuple.test.lua
> @@ -349,4 +349,29 @@ box.tuple.update == t.update
>   box.tuple.upsert == t.upsert
>   box.tuple.bsize == t.bsize
>   
> +--
> +-- gh-3282: space:frommap().
> +--
> +format = {}
> +format[1] = {name = 'aaa', type = 'unsigned'}
> +format[2] = {name = 'bbb', type = 'unsigned'}
> +format[3] = {name = 'ccc', type = 'unsigned'}
> +format[4] = {name = 'ddd', type = 'unsigned'}
> +s = box.schema.create_space('test', {format = format})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:frommap({ddd = 1, aaa = 2, bbb = 3})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
> +s:frommap()
> +s:frommap({})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
> +s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
> +_ = s:create_index('primary', {parts = {'aaa'}})
> +tuple = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +s:replace(tuple)
> +s:drop()
> +_, err = s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
> +assert(err ~= nil)
> +
>   test_run:cmd("clear filter")
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [tarantool-patches] Re: [PATCH v2 1/1] schema:frommap() to create table or tuple
  2018-04-09 17:23                         ` Kirill Shcherbatov
  2018-04-09 17:45                           ` Vladislav Shpilevoy
@ 2018-04-10 10:31                           ` Vladimir Davydov
  1 sibling, 0 replies; 18+ messages in thread
From: Vladimir Davydov @ 2018-04-10 10:31 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, v.shpilevoy

On Mon, Apr 09, 2018 at 08:23:21PM +0300, Kirill Shcherbatov wrote:
> From 0cb37cac97056398c39f723aef81053cc4550485 Mon Sep 17 00:00:00 2001
> From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
> Date: Wed, 4 Apr 2018 19:32:40 +0300
> Subject: [PATCH] schema:frommap() to create table or tuple
> 
> This feature is designed to build tuple or table object by map.
> Tuple output is default. If you wish to make a table, specify table = true option.
> subscriber:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
> 
> Closes #3282
> ---
>  src/box/lua/schema.lua  |  1 +
>  src/box/lua/space.cc    | 76 +++++++++++++++++++++++++++++++++++++++++++++-
>  src/box/lua/tuple.c     |  2 +-
>  src/box/lua/tuple.h     |  3 ++
>  test/box/tuple.result   | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
>  test/box/tuple.test.lua | 25 +++++++++++++++
>  6 files changed, 186 insertions(+), 2 deletions(-)

ACK

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2018-04-10 10:31 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-28 17:51 [tarantool-patches] [PATCH v2 1/1] schema:frommap() to create table or tuple Kirill Shcherbatov
2018-03-28 18:28 ` Kirill Shcherbatov
2018-03-29  7:36   ` [tarantool-patches] " Kirill Shcherbatov
2018-03-29 10:14     ` v.shpilevoy
2018-04-02 10:26       ` Kirill Shcherbatov
2018-04-02 11:19         ` v.shpilevoy
2018-04-02 19:42           ` Kirill Shcherbatov
2018-04-03  9:47             ` Vladislav Shpilevoy
2018-04-04 14:14               ` Kirill Shcherbatov
2018-04-04 16:35                 ` [tarantool-patches] " Kirill Shcherbatov
2018-04-04 16:43                   ` Vladislav Shpilevoy
2018-04-06 13:47                   ` Vladimir Davydov
2018-04-06 15:44                   ` [tarantool-patches] " Konstantin Osipov
2018-04-09  8:30                     ` Kirill Shcherbatov
2018-04-09 10:44                       ` Vladislav Shpilevoy
2018-04-09 17:23                         ` Kirill Shcherbatov
2018-04-09 17:45                           ` Vladislav Shpilevoy
2018-04-10 10:31                           ` Vladimir Davydov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox