[tarantool-patches] [PATCH v1 1/1] lua: export key_def methods for LUA key_def object

Kirill Shcherbatov kshcherbatov at tarantool.org
Mon Mar 25 15:27:41 MSK 2019


Functions extract_key, compare, compare_with_key, merge defined
for key_def introduced for LUA key_def object.

Close #4025

@TarantoolBot document
Title: Built-in methods for key_def object
Now it is possible to work with index base object - key
definition in LUA. Available methods:
key = key_def:extract_key(tuple)
key_def:compare(tuple_a, tuple_b)
key_def:compare_with_key(tuple, key), where key is tuple or table
new_key_def = key_def:merge(second_key_def)

Example:
-- Removal values for secondary non-unique index
key_def = require('key_def')
s = box.schema.space.create('test')
pk = s:create_index('pk')
idx = s:create_index('test', {unique=false,
                     parts = {{2, 'number', path = 'a'},
                              {2, 'number', path = 'b'}}})
s:insert{1, {a = 1, b = 1}}
s:insert{2, {a = 1, b = 2}}
pk_def = key_def.new(pk.parts)
for _, tuple in pairs(idx:select({1, nil})) do
    local key = pk_def:extract_key(tuple)
    pk:delete(key)
end
---
 src/box/CMakeLists.txt  |   1 +
 src/box/lua/init.c      |   2 +
 src/box/lua/key_def.c   | 134 ++++++++++++++++++++++++++++++++++++++++
 src/box/lua/key_def.h   |   8 +++
 src/box/lua/key_def.lua |  19 ++++++
 src/box/lua/space.cc    |  28 +--------
 test/box/tuple.result   | 133 +++++++++++++++++++++++++++++++++++++++
 test/box/tuple.test.lua |  39 ++++++++++++
 8 files changed, 338 insertions(+), 26 deletions(-)
 create mode 100644 src/box/lua/key_def.lua

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index f25c21045..906ce22e6 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -12,6 +12,7 @@ lua_source(lua_sources lua/net_box.lua)
 lua_source(lua_sources lua/upgrade.lua)
 lua_source(lua_sources lua/console.lua)
 lua_source(lua_sources lua/xlog.lua)
+lua_source(lua_sources lua/key_def.lua)
 set(bin_sources)
 bin_source(bin_sources bootstrap.snap bootstrap.h)
 
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 69f346414..68ef58909 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -63,6 +63,7 @@
 
 extern char session_lua[],
 	tuple_lua[],
+	key_def_lua[],
 	schema_lua[],
 	load_cfg_lua[],
 	xlog_lua[],
@@ -81,6 +82,7 @@ static const char *lua_sources[] = {
 	"box/console", console_lua,
 	"box/load_cfg", load_cfg_lua,
 	"box/xlog", xlog_lua,
+	"box/key_def", key_def_lua,
 	NULL
 };
 
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 813582913..6d2c06afb 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -34,8 +34,10 @@
 #include <lua.h>
 #include <lauxlib.h>
 #include "diag.h"
+#include "tuple.h"
 #include "box/key_def.h"
 #include "box/box.h"
+#include "box/tuple.h"
 #include "box/coll_id_cache.h"
 #include "lua/utils.h"
 #include "box/tuple_format.h" /* TUPLE_INDEX_BASE */
@@ -149,6 +151,28 @@ luaT_key_def_set_part(struct lua_State *L, struct key_part_def *part)
 	return 0;
 }
 
+void
+lbox_fill_key_part(struct lua_State *L, const struct key_part *part)
+{
+	lua_newtable(L);
+	lua_pushstring(L, field_type_strs[part->type]);
+	lua_setfield(L, -2, "type");
+	lua_pushnumber(L, part->fieldno + TUPLE_INDEX_BASE);
+	lua_setfield(L, -2, "fieldno");
+	if (part->path != NULL) {
+		lua_pushlstring(L, part->path, part->path_len);
+		lua_setfield(L, -2, "path");
+	}
+	lua_pushboolean(L, key_part_is_nullable(part));
+	lua_setfield(L, -2, "is_nullable");
+	if (part->coll_id != COLL_NONE) {
+		struct coll_id *coll_id = coll_by_id(part->coll_id);
+		assert(coll_id != NULL);
+		lua_pushstring(L, coll_id->name);
+		lua_setfield(L, -2, "collation");
+	}
+}
+
 struct key_def *
 check_key_def(struct lua_State *L, int idx)
 {
@@ -175,6 +199,103 @@ lbox_key_def_gc(struct lua_State *L)
 	return 0;
 }
 
+static int
+lbox_key_def_extract_key(lua_State *L)
+{
+	struct key_def *key_def;
+	struct tuple *tuple;
+	if (lua_gettop(L) != 2 || (key_def = check_key_def(L, 1)) == NULL ||
+	   (tuple = luaT_istuple(L, 2)) == NULL)
+		return luaL_error(L, "Usage key_def:extract_key(tuple)");
+
+	uint32_t key_size;
+	char *key = tuple_extract_key(tuple, key_def, &key_size);
+	if (key == NULL)
+		return luaT_error(L);
+
+	struct tuple *ret =
+		box_tuple_new(box_tuple_format_default(), key, key + key_size);
+	if (ret == NULL)
+		return luaT_error(L);
+	luaT_pushtuple(L, ret);
+	return 1;
+}
+
+static int
+lbox_key_def_compare(lua_State *L)
+{
+	struct key_def *key_def;
+	struct tuple *tuple_a, *tuple_b;
+	if (lua_gettop(L) != 3 || (key_def = check_key_def(L, 1)) == NULL ||
+	   (tuple_a = luaT_istuple(L, 2)) == NULL ||
+	   (tuple_b = luaT_istuple(L, 3)) == NULL)
+		return luaL_error(L, "Usage key_def:compare(tuple_a, tuple_b)");
+
+	int rc = tuple_compare(tuple_a, tuple_b, key_def);
+	lua_pushinteger(L, rc);
+	return 1;
+}
+
+static int
+lbox_key_def_compare_with_key(lua_State *L)
+{
+	struct key_def *key_def;
+	struct tuple *tuple, *key_tuple;
+	if (lua_gettop(L) != 3 || (key_def = check_key_def(L, 1)) == NULL ||
+	   (tuple = luaT_istuple(L, 2)) == NULL)
+		goto usage_error;
+
+	lua_remove(L, 1);
+	lua_remove(L, 1);
+	if (luaT_tuple_new(L, box_tuple_format_default()) != 1 ||
+	    (key_tuple = luaT_istuple(L, -1)) == NULL)
+		goto usage_error;
+
+	const char *key = tuple_data(key_tuple);
+	assert(mp_typeof(*key) == MP_ARRAY);
+	uint32_t part_count = mp_decode_array(&key);
+
+	int rc = tuple_compare_with_key(tuple, key, part_count, key_def);
+	lua_pushinteger(L, rc);
+	return 1;
+usage_error:
+	return luaL_error(L, "Usage key_def:compare_with_key(tuple, key)");
+}
+
+static int
+lbox_key_def_merge(lua_State *L)
+{
+	struct key_def *key_def_a, *key_def_b;
+	if (lua_gettop(L) != 2 || (key_def_a = check_key_def(L, 1)) == NULL ||
+	   (key_def_b = check_key_def(L, 2)) == NULL)
+		return luaL_error(L, "Usage key_def:merge(second_key_def)");
+
+	struct key_def *new_key_def = key_def_merge(key_def_a, key_def_b);
+	if (new_key_def == NULL)
+		return luaT_error(L);
+
+	*(struct key_def **)luaL_pushcdata(L, key_def_type_id) = new_key_def;
+	lua_pushcfunction(L, lbox_key_def_gc);
+	luaL_setcdatagc(L, -2);
+	return 1;
+}
+
+static int
+lbox_key_def_to_map(struct lua_State *L)
+{
+	struct key_def *key_def;
+	if (lua_gettop(L) != 1 || (key_def = check_key_def(L, 1)) == NULL)
+		return luaL_error(L, "Usage key_def:tomap()");
+
+	lua_createtable(L, key_def->part_count, 0);
+	for (uint32_t i = 0; i < key_def->part_count; ++i) {
+		lua_pushnumber(L, i + 1);
+		lbox_fill_key_part(L, &key_def->parts[i]);
+		lua_settable(L, -3);
+	}
+	return 1;
+}
+
 /**
  * Create a new key_def from a Lua table.
  *
@@ -236,5 +357,18 @@ luaopen_key_def(struct lua_State *L)
 	};
 	luaL_register_module(L, "key_def", meta);
 
+	lua_newtable(L); /* key_def.internal */
+	lua_pushcfunction(L, lbox_key_def_extract_key);
+	lua_setfield(L, -2, "extract_key");
+	lua_pushcfunction(L, lbox_key_def_compare);
+	lua_setfield(L, -2, "compare");
+	lua_pushcfunction(L, lbox_key_def_compare_with_key);
+	lua_setfield(L, -2, "compare_with_key");
+	lua_pushcfunction(L, lbox_key_def_merge);
+	lua_setfield(L, -2, "merge");
+	lua_pushcfunction(L, lbox_key_def_to_map);
+	lua_setfield(L, -2, "tomap");
+	lua_setfield(L, -2, "internal");
+
 	return 1;
 }
diff --git a/src/box/lua/key_def.h b/src/box/lua/key_def.h
index 61894d452..02217d3a3 100644
--- a/src/box/lua/key_def.h
+++ b/src/box/lua/key_def.h
@@ -36,6 +36,14 @@ extern "C" {
 #endif /* defined(__cplusplus) */
 
 struct lua_State;
+struct key_part;
+
+/**
+ * Prepare a new table representing a key_part on top of the Lua
+ * stack.
+ */
+void
+lbox_fill_key_part(struct lua_State *L, const struct key_part *part);
 
 /**
  * Extract a key_def object from a Lua stack.
diff --git a/src/box/lua/key_def.lua b/src/box/lua/key_def.lua
new file mode 100644
index 000000000..5dc8d9357
--- /dev/null
+++ b/src/box/lua/key_def.lua
@@ -0,0 +1,19 @@
+local ffi = require('ffi')
+local key_def = require('key_def')
+local key_def_t = ffi.typeof('struct key_def')
+
+local methods = {
+    ['extract_key'] = key_def.internal.extract_key,
+    ['compare'] = key_def.internal.compare,
+    ['compare_with_key'] = key_def.internal.compare_with_key,
+    ['merge'] = key_def.internal.merge,
+    ['tomap'] = key_def.internal.tomap,
+    ['__serialize'] = key_def.internal.tomap,
+}
+
+ffi.metatype(key_def_t, {
+    __index = function(self, key)
+        return methods[key]
+    end,
+    __tostring = function(key_def) return "<struct key_def&>" end,
+})
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 9dfc97b6a..9be27959c 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -30,6 +30,7 @@
  */
 #include "box/lua/space.h"
 #include "box/lua/tuple.h"
+#include "box/lua/key_def.h"
 #include "box/sql/sqlLimit.h"
 #include "lua/utils.h"
 #include "lua/trigger.h"
@@ -286,32 +287,7 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 
 		for (uint32_t j = 0; j < index_def->key_def->part_count; j++) {
 			lua_pushnumber(L, j + 1);
-			lua_newtable(L);
-			const struct key_part *part =
-				&index_def->key_def->parts[j];
-
-			lua_pushstring(L, field_type_strs[part->type]);
-			lua_setfield(L, -2, "type");
-
-			lua_pushnumber(L, part->fieldno + TUPLE_INDEX_BASE);
-			lua_setfield(L, -2, "fieldno");
-
-			if (part->path != NULL) {
-				lua_pushlstring(L, part->path, part->path_len);
-				lua_setfield(L, -2, "path");
-			}
-
-			lua_pushboolean(L, key_part_is_nullable(part));
-			lua_setfield(L, -2, "is_nullable");
-
-			if (part->coll_id != COLL_NONE) {
-				struct coll_id *coll_id =
-					coll_by_id(part->coll_id);
-				assert(coll_id != NULL);
-				lua_pushstring(L, coll_id->name);
-				lua_setfield(L, -2, "collation");
-			}
-
+			lbox_fill_key_part(L, &index_def->key_def->parts[j]);
 			lua_settable(L, -3); /* index[k].parts[j] */
 		}
 
diff --git a/test/box/tuple.result b/test/box/tuple.result
index 82ad8404d..a0209ca4a 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -1231,3 +1231,136 @@ s2:frommap({a="1", k="11"}):tomap({names_only = true})
 s2:drop()
 ---
 ...
+--
+-- gh-4025: Introduce built-in function to get index key
+--          from tuple.
+--
+key_def = require('key_def')
+---
+...
+s = box.schema.space.create('test', {engine='vinyl'})
+---
+...
+pk = s:create_index('pk')
+---
+...
+sk = s:create_index('sk', {unique=false, parts = {{2, 'number'}, {3, 'number'}}})
+---
+...
+tuple_a = box.tuple.new({1, 1, 22})
+---
+...
+tuple_b = box.tuple.new({2, 1, 11})
+---
+...
+tuple_c = box.tuple.new({3, 1, 22})
+---
+...
+pk_def = key_def.new(pk.parts)
+---
+...
+pk_def:extract_key(tuple_a)
+---
+- [1]
+...
+sk_def = key_def.new(sk.parts)
+---
+...
+sk_def:extract_key(tuple_a)
+---
+- [1, 22]
+...
+s:insert(tuple_a)
+---
+- [1, 1, 22]
+...
+s:insert(tuple_b)
+---
+- [2, 1, 11]
+...
+for _, tuple in pairs(sk:select({1, nil})) do pk:delete(pk_def:extract_key(tuple)) end
+---
+...
+s:select()
+---
+- []
+...
+pk_def:compare(tuple_b, tuple_a)
+---
+- 1
+...
+pk_def:compare(tuple_b, tuple_c)
+---
+- -1
+...
+sk_def:compare(tuple_b, tuple_a)
+---
+- -1
+...
+sk_def:compare(tuple_a, tuple_c)
+---
+- 0
+...
+pk_sk_def = pk_def:merge(sk_def)
+---
+...
+pk_sk_def:extract_key(tuple_a)
+---
+- [1, 1, 22]
+...
+pk_sk_def:compare(tuple_a, tuple_b)
+---
+- -1
+...
+sk_pk_def = sk_def:merge(pk_def)
+---
+...
+sk_pk_def:extract_key(tuple_a)
+---
+- [1, 22, 1]
+...
+sk_pk_def:compare(tuple_a, tuple_b)
+---
+- 1
+...
+key = sk_def:extract_key(tuple_a)
+---
+...
+sk_def:compare_with_key(tuple_a, key)
+---
+- 0
+...
+sk_def:compare_with_key(tuple_a, {1, 20})
+---
+- 1
+...
+sk_def:tomap()
+---
+- - type: number
+    is_nullable: false
+    fieldno: 2
+  - type: number
+    is_nullable: false
+    fieldno: 3
+...
+sk_def
+---
+- - type: number
+    is_nullable: false
+    fieldno: 2
+  - type: number
+    is_nullable: false
+    fieldno: 3
+...
+print(sk_def)
+---
+...
+key_def.new(sk_def:tomap())
+---
+- - type: number
+    is_nullable: false
+    fieldno: 2
+  - type: number
+    is_nullable: false
+    fieldno: 3
+...
diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
index 8030b0884..0a626b36a 100644
--- a/test/box/tuple.test.lua
+++ b/test/box/tuple.test.lua
@@ -416,3 +416,42 @@ s2:frommap({a="1", k="11"})
 s2:frommap({a="1", k="11"}):tomap({names_only = true})
 
 s2:drop()
+
+--
+-- gh-4025: Introduce built-in function to get index key
+--          from tuple.
+--
+key_def = require('key_def')
+s = box.schema.space.create('test', {engine='vinyl'})
+pk = s:create_index('pk')
+sk = s:create_index('sk', {unique=false, parts = {{2, 'number'}, {3, 'number'}}})
+tuple_a = box.tuple.new({1, 1, 22})
+tuple_b = box.tuple.new({2, 1, 11})
+tuple_c = box.tuple.new({3, 1, 22})
+pk_def = key_def.new(pk.parts)
+pk_def:extract_key(tuple_a)
+sk_def = key_def.new(sk.parts)
+sk_def:extract_key(tuple_a)
+s:insert(tuple_a)
+s:insert(tuple_b)
+for _, tuple in pairs(sk:select({1, nil})) do pk:delete(pk_def:extract_key(tuple)) end
+s:select()
+pk_def:compare(tuple_b, tuple_a)
+pk_def:compare(tuple_b, tuple_c)
+sk_def:compare(tuple_b, tuple_a)
+sk_def:compare(tuple_a, tuple_c)
+
+pk_sk_def = pk_def:merge(sk_def)
+pk_sk_def:extract_key(tuple_a)
+pk_sk_def:compare(tuple_a, tuple_b)
+sk_pk_def = sk_def:merge(pk_def)
+sk_pk_def:extract_key(tuple_a)
+sk_pk_def:compare(tuple_a, tuple_b)
+
+key = sk_def:extract_key(tuple_a)
+sk_def:compare_with_key(tuple_a, key)
+sk_def:compare_with_key(tuple_a, {1, 20})
+sk_def:tomap()
+sk_def
+print(sk_def)
+key_def.new(sk_def:tomap())
-- 
2.21.0





More information about the Tarantool-patches mailing list