From 81123dcbcce5c9cf6e400c50c590c69bece77e90 Mon Sep 17 00:00:00 2001 From: Roman Tsisyk <roman@tsisyk.com> Date: Mon, 8 Jun 2015 16:56:00 +0300 Subject: [PATCH] Add Lua/C version of index:select{} for Sophia A part of #863 --- src/box/lua/call.cc | 49 ++++++++++++++---- src/box/lua/schema.lua | 31 +++++++++-- test/box/select.result | 99 ++++++++++++++++++++--------------- test/box/select.test.lua | 108 ++++++++++++++++++++++----------------- 4 files changed, 185 insertions(+), 102 deletions(-) diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 846a7fa3d4..46b7e85bc8 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -83,6 +83,7 @@ struct port_lua { struct port_vtab *vtab; struct lua_State *L; + size_t size; /* for port_lua_add_tuple */ }; static inline struct port_lua * @@ -122,30 +123,26 @@ port_lua_table_add_tuple(struct port *port, struct tuple *tuple) { lua_State *L = port_lua(port)->L; try { - int idx = luaL_getn(L, -1); /* TODO: can be optimized */ lbox_pushtuple(L, tuple); - lua_rawseti(L, -2, idx + 1); - + lua_rawseti(L, -2, ++port_lua(port)->size); } catch (...) { tnt_raise(ClientError, ER_PROC_LUA, lua_tostring(L, -1)); } } /** Add all tuples to a Lua table. */ -static struct port * -port_lua_table_create(struct lua_State *L) +void +port_lua_table_create(struct port_lua *port, struct lua_State *L) { static struct port_vtab port_lua_vtab = { port_lua_table_add_tuple, null_port_eof, }; - struct port_lua *port = (struct port_lua *) - region_alloc(&fiber()->gc, sizeof(struct port_lua)); port->vtab = &port_lua_vtab; port->L = L; + port->size = 0; /* The destination table to append tuples to. */ lua_newtable(L); - return (struct port *) port; } /* }}} */ @@ -179,12 +176,12 @@ lbox_process(lua_State *L) return luaL_error(L, "box.process(CALL, ...) is not allowed"); } /* Capture all output into a Lua table. */ - struct port *port_lua = port_lua_table_create(L); + struct port_lua port_lua; struct request request; request_create(&request, op); request_decode(&request, req, sz); - box_process(&request, port_lua); - + port_lua_table_create(&port_lua, L); + box_process(&request, (struct port *) &port_lua); return 1; } @@ -291,6 +288,27 @@ boxffi_select(struct port_ffi *port, uint32_t space_id, uint32_t index_id, } } +static int +lbox_select(lua_State *L) +{ + if (lua_gettop(L) != 6 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || + !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5)) { + return luaL_error(L, "Usage index:select(space_id, index_id," + "iterator, offset, limit, key)"); + } + + struct request request; + struct port_lua port; + lbox_request_create(&request, L, IPROTO_SELECT, 6, -1); + request.index_id = lua_tointeger(L, 2); + request.iterator = lua_tointeger(L, 3); + request.offset = lua_tointeger(L, 4); + request.limit = lua_tointeger(L, 5); + port_lua_table_create(&port, L); + box_process(&request, (struct port *) &port); + return 1; +} + static int lbox_insert(lua_State *L) { @@ -731,6 +749,7 @@ static const struct luaL_reg boxlib[] = { static const struct luaL_reg boxlib_internal[] = { {"process", lbox_process}, {"call_loadproc", lbox_call_loadproc}, + {"select", lbox_select}, {"insert", lbox_insert}, {"replace", lbox_replace}, {"update", lbox_update}, @@ -747,6 +766,14 @@ box_lua_init(struct lua_State *L) luaL_register(L, "box.internal", boxlib_internal); lua_pop(L, 1); +#if 0 + /* Get CTypeID for `struct port *' */ + int rc = luaL_cdef(L, "struct port;"); + assert(rc == 0); + (void) rc; + CTID_STRUCT_PORT_PTR = luaL_ctypeid(L, "struct port *"); + assert(CTID_CONST_STRUCT_TUPLE_REF != 0); +#endif box_lua_error_init(L); box_lua_tuple_init(L); box_lua_index_init(L); diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 5461756fb9..f1e1c364e1 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -683,13 +683,10 @@ function box.schema.space.bless(space) end end - index_mt.select = function(index, key, opts) + local function check_select_opts(opts, key_is_nil) local offset = 0 local limit = 4294967295 - - local key, key_end = msgpackffi.encode_tuple(key) - local iterator = check_iterator_type(opts, key + 1 >= key_end) - + local iterator = check_iterator_type(opts, key_is_nil) if opts ~= nil then if opts.offset ~= nil then offset = opts.offset @@ -698,6 +695,12 @@ function box.schema.space.bless(space) limit = opts.limit end end + return iterator, offset, limit + end + + index_mt.select_ffi = function(index, key, opts) + local key, key_end = msgpackffi.encode_tuple(key) + local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end) if builtin.boxffi_select(port, index.space_id, index.id, iterator, offset, limit, key, key_end) ~=0 then @@ -711,6 +714,14 @@ function box.schema.space.bless(space) end return ret end + + index_mt.select_luac = function(index, key, opts) + local key = keify(key) + local iterator, offset, limit = check_select_opts(opts, #key == 0) + return internal.select(index.space_id, index.id, iterator, + offset, limit, key) + end + index_mt.update = function(index, key, ops) return internal.update(index.space_id, index.id, keify(key), ops); end @@ -729,6 +740,16 @@ function box.schema.space.bless(space) end return box.schema.index.alter(index.space_id, index.id, options) end + + -- true if reading operations may yield + local read_yields = space.engine == 'sophia' + if read_yields then + -- use Lua/C implmenetation + index_mt.select = index_mt.select_luac + else + -- use FFI implementation + index_mt.select = index_mt.select_ffi + end -- local space_mt = {} space_mt.len = function(space) diff --git a/test/box/select.result b/test/box/select.result index cc4522a46f..5266bfcfee 100644 --- a/test/box/select.result +++ b/test/box/select.result @@ -1,3 +1,6 @@ +msgpack = require('msgpack') +--- +... s = box.schema.space.create('select', { temporary = true }) --- ... @@ -66,7 +69,23 @@ s.index[1]:get({1, 2}) -------------------------------------------------------------------------------- -- select tests -------------------------------------------------------------------------------- -s.index[0]:select() +--# setopt delimiter ';' +function test(idx, ...) + local t1 = idx:select_ffi(...) + local t2 = idx:select_luac(...) + if msgpack.encode(t1) ~= msgpack.encode(t2) then + return 'different result from select_ffi and select_luac', t1, t2 + end + return t1 +end; +--- +... +--# setopt delimiter '' +s.index[0].select == s.index[0].select_ffi or s.index[0].select == s.index[0].select_luac +--- +- true +... +test(s.index[0]) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -89,7 +108,7 @@ s.index[0]:select() - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}) +test(s.index[0], {}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -112,7 +131,7 @@ s.index[0]:select({}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil) +test(s.index[0], nil) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -135,7 +154,7 @@ s.index[0]:select(nil) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}, {iterator = 'ALL'}) +test(s.index[0], {}, {iterator = 'ALL'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -158,7 +177,7 @@ s.index[0]:select({}, {iterator = 'ALL'}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = box.index.ALL }) +test(s.index[0], nil, {iterator = box.index.ALL }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -181,7 +200,7 @@ s.index[0]:select(nil, {iterator = box.index.ALL }) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}, {iterator = box.index.ALL, limit = 10}) +test(s.index[0], {}, {iterator = box.index.ALL, limit = 10}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -194,15 +213,15 @@ s.index[0]:select({}, {iterator = box.index.ALL, limit = 10}) - [9, 1, 2, 3] - [10, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = box.index.ALL, limit = 0}) +test(s.index[0], nil, {iterator = box.index.ALL, limit = 0}) --- - [] ... -s.index[0]:select({}, {iterator = 'ALL', limit = 1, offset = 15}) +test(s.index[0], {}, {iterator = 'ALL', limit = 1, offset = 15}) --- - - [16, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = 'ALL', limit = 20, offset = 15}) +test(s.index[0], nil, {iterator = 'ALL', limit = 20, offset = 15}) --- - - [16, 1, 2, 3] - [17, 1, 2, 3] @@ -210,7 +229,7 @@ s.index[0]:select(nil, {iterator = 'ALL', limit = 20, offset = 15}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = box.index.EQ}) +test(s.index[0], nil, {iterator = box.index.EQ}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -233,7 +252,7 @@ s.index[0]:select(nil, {iterator = box.index.EQ}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}, {iterator = 'EQ'}) +test(s.index[0], {}, {iterator = 'EQ'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -256,7 +275,7 @@ s.index[0]:select({}, {iterator = 'EQ'}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = 'REQ'}) +test(s.index[0], nil, {iterator = 'REQ'}) --- - - [20, 1, 2, 3] - [19, 1, 2, 3] @@ -279,7 +298,7 @@ s.index[0]:select(nil, {iterator = 'REQ'}) - [2, 1, 2, 3] - [1, 1, 2, 3] ... -s.index[0]:select({}, {iterator = box.index.REQ}) +test(s.index[0], {}, {iterator = box.index.REQ}) --- - - [20, 1, 2, 3] - [19, 1, 2, 3] @@ -302,45 +321,45 @@ s.index[0]:select({}, {iterator = box.index.REQ}) - [2, 1, 2, 3] - [1, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = 'EQ', limit = 2, offset = 1}) +test(s.index[0], nil, {iterator = 'EQ', limit = 2, offset = 1}) --- - - [2, 1, 2, 3] - [3, 1, 2, 3] ... -s.index[0]:select({}, {iterator = box.index.REQ, limit = 2, offset = 1}) +test(s.index[0], {}, {iterator = box.index.REQ, limit = 2, offset = 1}) --- - - [19, 1, 2, 3] - [18, 1, 2, 3] ... -s.index[0]:select(1) +test(s.index[0], 1) --- - - [1, 1, 2, 3] ... -s.index[0]:select({1}) +test(s.index[0], {1}) --- - - [1, 1, 2, 3] ... -s.index[0]:select({1, 2}) +test(s.index[0], {1, 2}) --- - error: Invalid key part count (expected [0..1], got 2) ... -s.index[0]:select(0) +test(s.index[0], 0) --- - [] ... -s.index[0]:select({0}) +test(s.index[0], {0}) --- - [] ... -s.index[0]:select("0") +test(s.index[0], "0") --- - error: 'Supplied key type of part 0 does not match index part type: expected NUM' ... -s.index[0]:select({"0"}) +test(s.index[0], {"0"}) --- - error: 'Supplied key type of part 0 does not match index part type: expected NUM' ... -s.index[1]:select(1) +test(s.index[1], 1) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -363,7 +382,7 @@ s.index[1]:select(1) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[1]:select({1}) +test(s.index[1], {1}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -386,12 +405,12 @@ s.index[1]:select({1}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[1]:select({1}, {limit = 2}) +test(s.index[1], {1}, {limit = 2}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] ... -s.index[1]:select(1, {iterator = 'EQ'}) +test(s.index[1], 1, {iterator = 'EQ'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -414,29 +433,29 @@ s.index[1]:select(1, {iterator = 'EQ'}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[1]:select({1}, {iterator = box.index.EQ, offset = 16, limit = 2}) +test(s.index[1], {1}, {iterator = box.index.EQ, offset = 16, limit = 2}) --- - - [17, 1, 2, 3] - [18, 1, 2, 3] ... -s.index[1]:select({1}, {iterator = box.index.REQ, offset = 16, limit = 2 }) +test(s.index[1], {1}, {iterator = box.index.REQ, offset = 16, limit = 2 }) --- - - [4, 1, 2, 3] - [3, 1, 2, 3] ... -s.index[1]:select({1, 2}, {iterator = 'EQ'}) +test(s.index[1], {1, 2}, {iterator = 'EQ'}) --- - - [2, 1, 2, 3] ... -s.index[1]:select({1, 2}, {iterator = box.index.REQ}) +test(s.index[1], {1, 2}, {iterator = box.index.REQ}) --- - - [2, 1, 2, 3] ... -s.index[1]:select({1, 2}) +test(s.index[1], {1, 2}) --- - - [2, 1, 2, 3] ... -s.index[0]:select(nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) +test(s.index[0], nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -459,7 +478,7 @@ s.index[0]:select(nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) +test(s.index[0], {}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -482,19 +501,19 @@ s.index[0]:select({}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(1) +test(s.index[0], 1) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = box.index.EQ }) +test(s.index[0], 1, { iterator = box.index.EQ }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'EQ' }) +test(s.index[0], 1, { iterator = 'EQ' }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'GE' }) +test(s.index[0], 1, { iterator = 'GE' }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -517,16 +536,16 @@ s.index[0]:select(1, { iterator = 'GE' }) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'GE', limit = 2 }) +test(s.index[0], 1, { iterator = 'GE', limit = 2 }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'LE', limit = 2 }) +test(s.index[0], 1, { iterator = 'LE', limit = 2 }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'GE', offset = 10, limit = 2 }) +test(s.index[0], 1, { iterator = 'GE', offset = 10, limit = 2 }) --- - - [11, 1, 2, 3] - [12, 1, 2, 3] diff --git a/test/box/select.test.lua b/test/box/select.test.lua index 2ac00390ff..cfde112f12 100644 --- a/test/box/select.test.lua +++ b/test/box/select.test.lua @@ -1,3 +1,5 @@ +msgpack = require('msgpack') + s = box.schema.space.create('select', { temporary = true }) index1 = s:create_index('primary', { type = 'tree' }) index2 = s:create_index('second', { type = 'tree', unique = true, parts = {2, 'num', 1, 'num'}}) @@ -26,52 +28,66 @@ s.index[1]:get({1, 2}) -- select tests -------------------------------------------------------------------------------- -s.index[0]:select() -s.index[0]:select({}) -s.index[0]:select(nil) -s.index[0]:select({}, {iterator = 'ALL'}) -s.index[0]:select(nil, {iterator = box.index.ALL }) -s.index[0]:select({}, {iterator = box.index.ALL, limit = 10}) -s.index[0]:select(nil, {iterator = box.index.ALL, limit = 0}) -s.index[0]:select({}, {iterator = 'ALL', limit = 1, offset = 15}) -s.index[0]:select(nil, {iterator = 'ALL', limit = 20, offset = 15}) - -s.index[0]:select(nil, {iterator = box.index.EQ}) -s.index[0]:select({}, {iterator = 'EQ'}) -s.index[0]:select(nil, {iterator = 'REQ'}) -s.index[0]:select({}, {iterator = box.index.REQ}) - -s.index[0]:select(nil, {iterator = 'EQ', limit = 2, offset = 1}) -s.index[0]:select({}, {iterator = box.index.REQ, limit = 2, offset = 1}) - -s.index[0]:select(1) -s.index[0]:select({1}) -s.index[0]:select({1, 2}) -s.index[0]:select(0) -s.index[0]:select({0}) -s.index[0]:select("0") -s.index[0]:select({"0"}) - -s.index[1]:select(1) -s.index[1]:select({1}) -s.index[1]:select({1}, {limit = 2}) -s.index[1]:select(1, {iterator = 'EQ'}) -s.index[1]:select({1}, {iterator = box.index.EQ, offset = 16, limit = 2}) -s.index[1]:select({1}, {iterator = box.index.REQ, offset = 16, limit = 2 }) -s.index[1]:select({1, 2}, {iterator = 'EQ'}) -s.index[1]:select({1, 2}, {iterator = box.index.REQ}) -s.index[1]:select({1, 2}) - -s.index[0]:select(nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) -s.index[0]:select({}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) - -s.index[0]:select(1) -s.index[0]:select(1, { iterator = box.index.EQ }) -s.index[0]:select(1, { iterator = 'EQ' }) -s.index[0]:select(1, { iterator = 'GE' }) -s.index[0]:select(1, { iterator = 'GE', limit = 2 }) -s.index[0]:select(1, { iterator = 'LE', limit = 2 }) -s.index[0]:select(1, { iterator = 'GE', offset = 10, limit = 2 }) +--# setopt delimiter ';' +function test(idx, ...) + local t1 = idx:select_ffi(...) + local t2 = idx:select_luac(...) + if msgpack.encode(t1) ~= msgpack.encode(t2) then + return 'different result from select_ffi and select_luac', t1, t2 + end + return t1 +end; +--# setopt delimiter '' + +s.index[0].select == s.index[0].select_ffi or s.index[0].select == s.index[0].select_luac + +test(s.index[0]) +test(s.index[0], {}) +test(s.index[0], nil) +test(s.index[0], {}, {iterator = 'ALL'}) + +test(s.index[0], nil, {iterator = box.index.ALL }) +test(s.index[0], {}, {iterator = box.index.ALL, limit = 10}) +test(s.index[0], nil, {iterator = box.index.ALL, limit = 0}) +test(s.index[0], {}, {iterator = 'ALL', limit = 1, offset = 15}) +test(s.index[0], nil, {iterator = 'ALL', limit = 20, offset = 15}) + +test(s.index[0], nil, {iterator = box.index.EQ}) +test(s.index[0], {}, {iterator = 'EQ'}) +test(s.index[0], nil, {iterator = 'REQ'}) +test(s.index[0], {}, {iterator = box.index.REQ}) + +test(s.index[0], nil, {iterator = 'EQ', limit = 2, offset = 1}) +test(s.index[0], {}, {iterator = box.index.REQ, limit = 2, offset = 1}) + +test(s.index[0], 1) +test(s.index[0], {1}) +test(s.index[0], {1, 2}) +test(s.index[0], 0) +test(s.index[0], {0}) +test(s.index[0], "0") +test(s.index[0], {"0"}) + +test(s.index[1], 1) +test(s.index[1], {1}) +test(s.index[1], {1}, {limit = 2}) +test(s.index[1], 1, {iterator = 'EQ'}) +test(s.index[1], {1}, {iterator = box.index.EQ, offset = 16, limit = 2}) +test(s.index[1], {1}, {iterator = box.index.REQ, offset = 16, limit = 2 }) +test(s.index[1], {1, 2}, {iterator = 'EQ'}) +test(s.index[1], {1, 2}, {iterator = box.index.REQ}) +test(s.index[1], {1, 2}) + +test(s.index[0], nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) +test(s.index[0], {}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) + +test(s.index[0], 1) +test(s.index[0], 1, { iterator = box.index.EQ }) +test(s.index[0], 1, { iterator = 'EQ' }) +test(s.index[0], 1, { iterator = 'GE' }) +test(s.index[0], 1, { iterator = 'GE', limit = 2 }) +test(s.index[0], 1, { iterator = 'LE', limit = 2 }) +test(s.index[0], 1, { iterator = 'GE', offset = 10, limit = 2 }) s:select(2) -- GitLab