From 641cfa61a3250033455f21fa663d27613e94b64c Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Thu, 27 Feb 2014 21:07:40 +0400
Subject: [PATCH] Reimplement select() using FFI

---
 src/box/lua/call.cc    | 70 ++++++++++++++++++++++++++++++++----------
 src/box/lua/call.h     | 24 +++++++++++++++
 src/box/lua/schema.lua | 56 +++++++++++++++++++++++++++------
 src/box/lua/tuple.lua  |  4 +--
 src/ffisyms.cc         |  6 +++-
 test/big/lua.result    |  2 +-
 test/box/misc.result   |  1 -
 7 files changed, 132 insertions(+), 31 deletions(-)

diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index f28d63e4c6..8207de2be6 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -263,26 +263,63 @@ lbox_request_create(struct lua_State *L, enum iproto_request_type type,
 	return request;
 }
 
-static int
-lbox_select(lua_State *L)
+static void
+port_ffi_add_tuple(struct port *port, struct tuple *tuple)
 {
-	if (lua_gettop(L) != 6 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
-	    !lua_isnumber(L, 4) || !lua_isnumber(L, 5) || !lua_isnumber(L, 6)) {
-		return luaL_error(L, "Usage index:select(key, "
-				  "iterator, offset, limit)");
+	struct port_ffi *port_ffi = (struct port_ffi *) port;
+	if (port_ffi->size >= port_ffi->capacity) {
+		uint32_t capacity = (port_ffi->capacity > 0) ?
+				2 * port_ffi->capacity : 1024;
+		struct tuple **ret = (struct tuple **)
+			realloc(port_ffi->ret, sizeof(*ret) * capacity);
+		assert(ret != NULL);
+		port_ffi->ret = ret;
+		port_ffi->capacity = capacity;
 	}
+	port_ffi->ret[port_ffi->size++] = tuple;
+}
 
-	RegionGuard region_guard(&fiber()->gc);
-	struct request *request = lbox_request_create(L, IPROTO_SELECT,
-						      3, -1);
-	request->index_id = lua_tointeger(L, 2);
-	request->iterator = lua_tointeger(L, 4);
-	request->offset = lua_tointeger(L, 5);
-	request->limit = lua_tointeger(L, 6);
+struct port_vtab port_ffi_vtab = {
+	port_ffi_add_tuple,
+	null_port_eof,
+};
 
-	lua_newtable(L);
-	box_process(port_lua_process_create(L), request);
-	return 1;
+void
+port_ffi_create(struct port_ffi *port)
+{
+	memset(port, 0, sizeof(*port));
+	port->vtab = &port_ffi_vtab;
+}
+
+void
+port_ffi_destroy(struct port_ffi *port)
+{
+	free(port->ret);
+	port->capacity = port->size = 0;
+}
+
+int
+boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id,
+	      int iterator, uint32_t offset, uint32_t limit,
+	      const char *key, const char *key_end)
+{
+	struct request request;
+	request_create(&request, IPROTO_SELECT);
+	request.space_id = space_id;
+	request.index_id = index_id;
+	request.limit = limit;
+	request.offset = offset;
+	request.iterator = iterator;
+	request.key = key;
+	request.key_end = key_end;
+
+	try {
+		box_process(port, &request);
+		return 0;
+	} catch (Exception *e) {
+		/* will be hanled by box.raise() in Lua */
+		return -1;
+	}
 }
 
 static int
@@ -859,7 +896,6 @@ lbox_unpack(struct lua_State *L)
 
 static const struct luaL_reg boxlib[] = {
 	{"process", lbox_process},
-	{"_select", lbox_select},
 	{"_insert", lbox_insert},
 	{"_replace", lbox_replace},
 	{"_update", lbox_update},
diff --git a/src/box/lua/call.h b/src/box/lua/call.h
index 204c6cd712..c5ad2c4bab 100644
--- a/src/box/lua/call.h
+++ b/src/box/lua/call.h
@@ -29,6 +29,8 @@
  * SUCH DAMAGE.
  */
 
+#include <stdint.h>
+
 struct txn;
 struct request;
 struct port;
@@ -40,4 +42,26 @@ struct port;
 void
 box_lua_call(struct request *request, struct txn *txn, struct port *port);
 
+extern "C" {
+struct port_ffi
+{
+	struct port_vtab *vtab;
+	uint32_t size;
+	uint32_t capacity;
+	struct tuple **ret;
+};
+
+void
+port_ffi_create(struct port_ffi *port);
+
+void
+port_ffi_destroy(struct port_ffi *port);
+
+int
+boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id,
+	      int iterator, uint32_t offset, uint32_t limit,
+	      const char *key, const char *key_end);
+
+} /* extern "C" */
+
 #endif /* INCLUDES_TARANTOOL_MOD_BOX_LUA_CALL_H */
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 0eb94162a4..e3cff88a57 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -12,6 +12,25 @@ ffi.cdef[[
     struct iterator *
     boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
                   const char *key);
+
+    struct port;
+    struct port_ffi
+    {
+        struct port_vtab *vtab;
+        uint32_t size;
+        uint32_t capacity;
+        struct tuple **ret;
+    };
+
+    void
+    port_ffi_create(struct port_ffi *port);
+    void
+    port_ffi_destroy(struct port_ffi *port);
+
+    int
+    boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id,
+              int iterator, uint32_t offset, uint32_t limit,
+              const char *key, const char *key_end);
 ]]
 local builtin = ffi.C
 local msgpackffi = require('msgpackffi')
@@ -203,6 +222,12 @@ local iterator_cdata_gc = function(iterator_cdata)
     return iterator_cdata.free(iterator_cdata)
 end
 
+-- global struct port instance to use by select()/get()
+local port = ffi.new('struct port_ffi')
+builtin.port_ffi_create(port)
+ffi.gc(port, builtin.port_ffi_destroy)
+local port_t = ffi.typeof('struct port *')
+
 function box.schema.space.bless(space)
     local index_mt = {}
     -- __len and __index
@@ -357,12 +382,16 @@ function box.schema.space.bless(space)
     end
 
     index_mt.get = function(index, key)
-        key = keify(key)
-        local result = box._select(index.n, index.id, key, box.index.EQ, 0, 2)
-        if #result == 0 then
+        local key, key_end = msgpackffi.encode_tuple(key)
+        port.size = 0;
+        if builtin.boxffi_select(ffi.cast(port_t, port), index.n,
+           index.id, box.index.EQ, 0, 2, key, key_end) ~=0 then
+            return box.raise()
+        end
+        if port.size == 0 then
             return
-        elseif #result == 1 then
-            return result[1]
+        elseif port.size == 1 then
+            return box.tuple.bless(port.ret[0])
         else
             box.raise(box.error.ER_MORE_THAN_ONE_TUPLE,
                 "More than one tuple found by get()")
@@ -374,8 +403,8 @@ function box.schema.space.bless(space)
         local limit = 4294967295
         local iterator = box.index.EQ
 
-        key = keify(key)
-        if #key == 0 then
+        local key, key_end = msgpackffi.encode_tuple(key)
+        if key_end == key + 1 then -- empty array
             iterator = box.index.ALL
         end
 
@@ -394,7 +423,17 @@ function box.schema.space.bless(space)
             end
         end
 
-        return box._select(index.n, index.id, key, iterator, offset, limit)
+        port.size = 0;
+        if builtin.boxffi_select(ffi.cast(port_t, port), index.n,
+            index.id, iterator, offset, limit, key, key_end) ~=0 then
+            return box.raise()
+        end
+
+        local ret = {}
+        for i=0,port.size - 1,1 do
+            table.insert(ret, box.tuple.bless(port.ret[i]))
+        end
+        return ret
     end
     index_mt.update = function(index, key, ops)
         return box._update(index.n, index.id, keify(key), ops);
@@ -501,4 +540,3 @@ function box.schema.space.bless(space)
         end
     end
 end
-
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index 501e2a7735..b9b16bd545 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -132,8 +132,9 @@ end
 
 local tuple_bless = function(tuple)
     -- update in-place, do not spent time calling tuple_ref
+    local tuple2 = ffi.gc(ffi.cast(const_struct_tuple_ref_t, tuple), tuple_gc)
     tuple._refs = tuple._refs + 1
-    return ffi.gc(ffi.cast(const_struct_tuple_ref_t, tuple), tuple_gc)
+    return tuple2
 end
 
 local tuple_field = function(tuple, field_n)
@@ -146,7 +147,6 @@ local tuple_field = function(tuple, field_n)
 end
 
 ffi.metatype('struct tuple', {
-    __gc = tuple_gc;
     __len = function(tuple)
         return builtin.tuple_arity(tuple)
     end;
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index 8ac28fb7fb..c7e2c190a0 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -2,6 +2,7 @@
 #include <lib/msgpuck/msgpuck.h>
 #include <box/tuple.h>
 #include <box/lua/index.h>
+#include <box/lua/call.h>
 
 /*
  * A special hack to cc/ld to keep symbols in an optimized binary.
@@ -18,5 +19,8 @@ void *ffi_symbols[] = {
 	(void *) tuple_seek,
 	(void *) tuple_next,
 	(void *) tuple_ref,
-	(void *) boxffi_index_iterator
+	(void *) boxffi_index_iterator,
+	(void *) port_ffi_create,
+	(void *) port_ffi_destroy,
+	(void *) boxffi_select
 };
diff --git a/test/big/lua.result b/test/big/lua.result
index f2dbe9483b..45e66a3caf 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -466,7 +466,7 @@ t = {}
 ...
 index:iterator('sid_t', { iterator = 'wrong_iterator_type' })
 ---
-- error: '[string "-- schema.lua (internal file)..."]:248: Wrong iterator type: wrong_iterator_type'
+- error: '[string "-- schema.lua (internal file)..."]:273: Wrong iterator type: wrong_iterator_type'
 ...
 index = nil
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index 699fb8e706..3f0752c557 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -19,7 +19,6 @@ t
 - - _delete
   - _insert
   - _replace
-  - _select
   - _update
   - call_loadproc
   - cfg
-- 
GitLab