diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 3fce6f259af60611cc6d409121668ef1c15714e7..f047bead61a492f18fe86244d077be55c710cfcb 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -84,6 +84,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 * @@ -123,30 +124,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; } /* }}} */ @@ -180,12 +177,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; } @@ -298,6 +295,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) { @@ -763,6 +781,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}, @@ -779,6 +798,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/error.cc b/src/box/lua/error.cc index 9e86f19cc96c39e6a86906b794bc90779d36175d..9aa8c01cf2ba250e9f5d259f934e3e4d60a1a1c8 100644 --- a/src/box/lua/error.cc +++ b/src/box/lua/error.cc @@ -40,6 +40,14 @@ extern "C" { #include "lua/utils.h" #include "box/error.h" +int +lbox_error(lua_State *L) +{ + (void) L; + diag_last_error(&fiber()->diag)->raise(); + return 0; +} + static int lbox_error_raise(lua_State *L) { @@ -53,9 +61,8 @@ lbox_error_raise(lua_State *L) int top = lua_gettop(L); if (top <= 1) { /* re-throw saved exceptions (if any) */ - Exception *e = diag_last_error(&fiber()->diag); - if (e != NULL) - e->raise(); + if (diag_last_error(&fiber()->diag)) + lbox_error(L); return 0; } else if (top >= 2 && lua_type(L, 2) == LUA_TNUMBER) { code = lua_tointeger(L, 2); diff --git a/src/box/lua/error.h b/src/box/lua/error.h index f0dc5895f9834700a901211f522b3ec62afbd12c..9661f4e147194576579296e65836d81f03ce6fc2 100644 --- a/src/box/lua/error.h +++ b/src/box/lua/error.h @@ -34,4 +34,7 @@ struct lua_State; void box_lua_error_init(struct lua_State *L); +int +lbox_error(struct lua_State *L); + #endif /* INCLUDES_TARANTOOL_LUA_ERROR_H */ diff --git a/src/box/lua/index.cc b/src/box/lua/index.cc index 7555537c554a2e2add560b41f6e2d08e7cd6360f..b81fa2a96314d35d86ca3ff5b1bc5f31b0c15e54 100644 --- a/src/box/lua/index.cc +++ b/src/box/lua/index.cc @@ -28,17 +28,22 @@ */ #include "box/lua/index.h" #include "lua/utils.h" +#include "lua/msgpack.h" #include "box/index.h" #include "box/space.h" #include "box/schema.h" #include "box/user_def.h" #include "box/tuple.h" +#include "box/lua/error.h" #include "box/lua/tuple.h" #include "fiber.h" +#include "iobuf.h" /** {{{ box.index Lua library: access to spaces and indexes */ +static int CTID_STRUCT_ITERATOR_REF = 0; + static inline Index * check_index(uint32_t space_id, uint32_t index_id) { @@ -67,21 +72,69 @@ boxffi_index_len(uint32_t space_id, uint32_t index_id) } } +static inline int +lbox_returntuple(lua_State *L, struct tuple *tuple) +{ + if (tuple == (struct tuple *) -1) { + return lbox_error(L); + } else if (tuple == NULL) { + lua_pushnil(L); + return 1; + } else { + lbox_pushtuple_noref(L, tuple); + return 1; + } +} + +static inline struct tuple * +boxffi_returntuple(struct tuple *tuple) +{ + if (tuple == NULL) + return NULL; + tuple_ref(tuple); + return tuple; +} + +static inline char * +lbox_tokey(lua_State *L, int idx) +{ + struct region *gc = &fiber()->gc; + size_t used = region_used(gc); + struct mpstream stream; + mpstream_init(&stream, gc, region_reserve_cb, region_alloc_cb); + luamp_encode_tuple(L, luaL_msgpack_default, &stream, idx); + mpstream_flush(&stream); + size_t key_len = region_used(gc) - used; + return (char *) region_join(gc, key_len); +} + struct tuple * boxffi_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd) { try { Index *index = check_index(space_id, index_id); struct tuple *tuple = index->random(rnd); - if (tuple == NULL) - return NULL; - tuple_ref(tuple); /* must not throw in this case */ - return tuple; + return boxffi_returntuple(tuple); } catch (Exception *) { return (struct tuple *) -1; /* handled by box.error() in Lua */ } } +static int +lbox_index_random(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || + !lua_isnumber(L, 3)) + return luaL_error(L, "Usage index.random(space_id, index_id, rnd)"); + + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + uint32_t rnd = lua_tointeger(L, 3); + + struct tuple *tuple = boxffi_index_random(space_id, index_id, rnd); + return lbox_returntuple(L, tuple); +} + struct tuple * boxffi_index_get(uint32_t space_id, uint32_t index_id, const char *key) { @@ -92,15 +145,27 @@ boxffi_index_get(uint32_t space_id, uint32_t index_id, const char *key) uint32_t part_count = key ? mp_decode_array(&key) : 0; primary_key_validate(index->key_def, key, part_count); struct tuple *tuple = index->findByKey(key, part_count); - if (tuple == NULL) - return NULL; - tuple_ref(tuple); /* must not throw in this case */ - return tuple; + return boxffi_returntuple(tuple); } catch (Exception *) { return (struct tuple *) -1; /* handled by box.error() in Lua */ } } +static int +lbox_index_get(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage index.get(space_id, index_id, key)"); + + RegionGuard region_guard(&fiber()->gc); + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + const char *key = lbox_tokey(L, 3); + + struct tuple *tuple = boxffi_index_get(space_id, index_id, key); + return lbox_returntuple(L, tuple); +} + struct tuple * boxffi_index_min(uint32_t space_id, uint32_t index_id, const char *key) { @@ -115,15 +180,27 @@ boxffi_index_min(uint32_t space_id, uint32_t index_id, const char *key) uint32_t part_count = key ? mp_decode_array(&key) : 0; key_validate(index->key_def, ITER_GE, key, part_count); struct tuple *tuple = index->min(key, part_count); - if (tuple == NULL) - return NULL; - tuple_ref(tuple); /* must not throw in this case */ - return tuple; + return boxffi_returntuple(tuple); } catch (Exception *) { return (struct tuple *) -1; /* handled by box.error() in Lua */ } } +static int +lbox_index_min(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) + return luaL_error(L, "usage index.min(space_id, index_id, key)"); + + RegionGuard region_guard(&fiber()->gc); + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + const char *key = lbox_tokey(L, 3); + + struct tuple *tuple = boxffi_index_min(space_id, index_id, key); + return lbox_returntuple(L, tuple); +} + struct tuple * boxffi_index_max(uint32_t space_id, uint32_t index_id, const char *key) { @@ -138,15 +215,27 @@ boxffi_index_max(uint32_t space_id, uint32_t index_id, const char *key) uint32_t part_count = key ? mp_decode_array(&key) : 0; key_validate(index->key_def, ITER_LE, key, part_count); struct tuple *tuple = index->max(key, part_count); - if (tuple == NULL) - return NULL; - tuple_ref(tuple); /* must not throw in this case */ - return tuple; + return boxffi_returntuple(tuple); } catch (Exception *) { return (struct tuple *) -1; /* handled by box.error() in Lua */ } } +static int +lbox_index_max(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) + return luaL_error(L, "usage index.max(space_id, index_id, key)"); + + RegionGuard region_guard(&fiber()->gc); + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + const char *key = lbox_tokey(L, 3); + + struct tuple *tuple = boxffi_index_max(space_id, index_id, key); + return lbox_returntuple(L, tuple); +} + ssize_t boxffi_index_count(uint32_t space_id, uint32_t index_id, int type, const char *key) { @@ -160,6 +249,28 @@ boxffi_index_count(uint32_t space_id, uint32_t index_id, int type, const char *k return -1; /* handled by box.error() in Lua */ } } +static int +lbox_index_count(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || + !lua_isnumber(L, 3)) { + return luaL_error(L, "usage index.count(space_id, index_id, " + "iterator, key)"); + } + + RegionGuard region_guard(&fiber()->gc); + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + uint32_t iterator = lua_tointeger(L, 3); + const char *key = lbox_tokey(L, 4); + + ssize_t count = boxffi_index_count(space_id, index_id, + iterator, key); + if (count == -1) + return lbox_error(L); + lua_pushinteger(L, count); + return 1; +} static void box_index_init_iterator_types(struct lua_State *L, int idx) @@ -197,11 +308,35 @@ boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type, } catch (Exception *) { if (it) it->free(it); - /* will be hanled by box.error() in Lua */ + /* will be handled by box.error() in Lua */ return NULL; } } +static int +lbox_index_iterator(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || + !lua_isnumber(L, 3)) + return luaL_error(L, "usage index.iterator(space_id, index_id, type, key)"); + + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + uint32_t iterator = lua_tointeger(L, 3); + /* const char *key = lbox_tokey(L, 4); */ + const char *mpkey = lua_tolstring(L, 4, NULL); /* Key encoded by Lua */ + struct iterator *it = boxffi_index_iterator(space_id, index_id, + iterator, mpkey); + if (it == NULL) + return lbox_error(L); + + assert(CTID_STRUCT_ITERATOR_REF != 0); + struct iterator **ptr = (struct iterator **) luaL_pushcdata(L, + CTID_STRUCT_ITERATOR_REF, sizeof(struct iterator *)); + *ptr = it; /* NULL handled by Lua, gc also set by Lua */ + return 1; +} + struct tuple* boxffi_iterator_next(struct iterator *itr) { @@ -228,11 +363,36 @@ boxffi_iterator_next(struct iterator *itr) } } +static int +lbox_iterator_next(lua_State *L) +{ + /* first argument is key buffer */ + if (lua_gettop(L) < 1 || lua_type(L, 1) != LUA_TCDATA) + return luaL_error(L, "usage: next(state)"); + + assert(CTID_STRUCT_ITERATOR_REF != 0); + uint32_t ctypeid; + void *data = luaL_checkcdata(L, 1, &ctypeid); + if (ctypeid != CTID_STRUCT_ITERATOR_REF) + return luaL_error(L, "usage: next(state)"); + + struct iterator *itr = *(struct iterator **) data; + struct tuple *tuple = boxffi_iterator_next(itr); + return lbox_returntuple(L, tuple); +} + /* }}} */ void box_lua_index_init(struct lua_State *L) { + /* Get CTypeIDs */ + int rc = luaL_cdef(L, "struct iterator;"); + assert(rc == 0); + (void) rc; + CTID_STRUCT_ITERATOR_REF = luaL_ctypeid(L, "struct iterator&"); + assert(CTID_STRUCT_ITERATOR_REF != 0); + static const struct luaL_reg indexlib [] = { {NULL, NULL} }; @@ -241,4 +401,18 @@ box_lua_index_init(struct lua_State *L) luaL_register_module(L, "box.index", indexlib); box_index_init_iterator_types(L, -2); lua_pop(L, 1); + + static const struct luaL_reg boxlib_internal[] = { + {"random", lbox_index_random}, + {"get", lbox_index_get}, + {"min", lbox_index_min}, + {"max", lbox_index_max}, + {"count", lbox_index_count}, + {"iterator", lbox_index_iterator}, + {"iterator_next", lbox_iterator_next}, + {NULL, NULL} + }; + + luaL_register(L, "box.internal", boxlib_internal); + lua_pop(L, 1); } diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index d5eb948d49eed7b6e3b970878d9940b78ecaaeb5..a02d49643c65b9eea2f4c041f90a0a76589e5ee1 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1,6 +1,7 @@ -- schema.lua (internal file) -- local ffi = require('ffi') +local msgpack = require('msgpack') local msgpackffi = require('msgpackffi') local fun = require('fun') local session = box.session @@ -524,7 +525,7 @@ local iterator_gen = function(param, state) information. --]] if not ffi.istype(iterator_t, state) then - error('usage gen(param, state)') + error('usage: next(param, state)') end -- next() modifies state in-place local tuple = builtin.boxffi_iterator_next(state) @@ -537,6 +538,15 @@ local iterator_gen = function(param, state) end end +local iterator_gen_luac = function(param, state) + local tuple = internal.iterator_next(state) + if tuple ~= nil then + return state, tuple -- new state, value + else + return nil + end +end + local iterator_cdata_gc = function(iterator) return iterator.free(iterator) end @@ -604,7 +614,7 @@ function box.schema.space.bless(space) return error('Attempt to modify a read-only table') end index_mt.__index = index_mt -- min and max - index_mt.min = function(index, key) + index_mt.min_ffi = function(index, key) local pkey = msgpackffi.encode_tuple(key) local tuple = builtin.boxffi_index_min(index.space_id, index.id, pkey) if tuple == ffi.cast('void *', -1) then @@ -615,7 +625,11 @@ function box.schema.space.bless(space) return end end - index_mt.max = function(index, key) + index_mt.min_luac = function(index, key) + key = keify(key) + return internal.min(index.space_id, index.id, key); + end + index_mt.max_ffi = function(index, key) local pkey = msgpackffi.encode_tuple(key) local tuple = builtin.boxffi_index_max(index.space_id, index.id, pkey) if tuple == ffi.cast('void *', -1) then @@ -626,7 +640,11 @@ function box.schema.space.bless(space) return end end - index_mt.random = function(index, rnd) + index_mt.max_luac = function(index, key) + key = keify(key) + return internal.max(index.space_id, index.id, key); + end + index_mt.random_ffi = function(index, rnd) rnd = rnd or math.random() local tuple = builtin.boxffi_index_random(index.space_id, index.id, rnd) if tuple == ffi.cast('void *', -1) then @@ -637,8 +655,12 @@ function box.schema.space.bless(space) return end end + index_mt.random_luac = function(index, rnd) + rnd = rnd or math.random() + return internal.random(index.space_id, index.id, rnd); + end -- iteration - index_mt.pairs = function(index, key, opts) + index_mt.pairs_ffi = function(index, key, opts) local pkey, pkey_end = msgpackffi.encode_tuple(key) local itype = check_iterator_type(opts, pkey + 1 >= pkey_end); @@ -648,13 +670,20 @@ function box.schema.space.bless(space) if cdata == nil then box.error() end - return fun.wrap(iterator_gen, keybuf, ffi.gc(cdata, iterator_cdata_gc)) end - index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility - index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility + index_mt.pairs_luac = function(index, key, opts) + key = keify(key) + local itype = check_iterator_type(opts, #key == 0); + local keymp = msgpack.encode(key) + local keybuf = ffi.string(keymp, #keymp) + local cdata = internal.iterator(index.space_id, index.id, itype, keymp); + return fun.wrap(iterator_gen_luac, keybuf, + ffi.gc(cdata, iterator_cdata_gc)) + end + -- index subtree size - index_mt.count = function(index, key, opts) + index_mt.count_ffi = function(index, key, opts) local pkey, pkey_end = msgpackffi.encode_tuple(key) local itype = check_iterator_type(opts, pkey + 1 >= pkey_end); local count = builtin.boxffi_index_count(index.space_id, index.id, @@ -664,6 +693,11 @@ function box.schema.space.bless(space) end return tonumber(count) end + index_mt.count_luac = function(index, key, opts) + key = keify(key) + local itype = check_iterator_type(opts, #key == 0); + return internal.count(index.space_id, index.id, itype, key); + end local function check_index(space, index_id) if space.index[index_id] == nil then @@ -671,7 +705,7 @@ function box.schema.space.bless(space) end end - index_mt.get = function(index, key) + index_mt.get_ffi = function(index, key) local key, key_end = msgpackffi.encode_tuple(key) local tuple = builtin.boxffi_index_get(index.space_id, index.id, key) if tuple == ffi.cast('void *', -1) then @@ -682,14 +716,15 @@ function box.schema.space.bless(space) return end end + index_mt.get_luac = function(index, key) + key = keify(key) + return internal.get(index.space_id, index.id, key) + 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 +733,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 +752,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 +778,21 @@ 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' + local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'} + for _, op in ipairs(read_ops) do + if read_yields then + -- use Lua/C implmenetation + index_mt[op] = index_mt[op .. "_luac"] + else + -- use FFI implementation + index_mt[op] = index_mt[op .. "_ffi"] + end + end + index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility + index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility -- local space_mt = {} space_mt.len = function(space) diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc index e9c22b280bee7756f163e7555d94a017f445797f..6fa40dde687c1a58ec942ebcee86bca2d4a648c2 100644 --- a/src/box/lua/space.cc +++ b/src/box/lua/space.cc @@ -54,8 +54,16 @@ lbox_space_on_replace_trigger(struct trigger *trigger, void *event) lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) trigger->data); - lbox_pushtuple(L, stmt->old_tuple); - lbox_pushtuple(L, stmt->new_tuple); + if (stmt->old_tuple) { + lbox_pushtuple(L, stmt->old_tuple); + } else { + lua_pushnil(L); + } + if (stmt->new_tuple) { + lbox_pushtuple(L, stmt->new_tuple); + } else { + lua_pushnil(L); + } /* @todo: maybe the space object has to be here */ lua_pushstring(L, stmt->space->def.name); diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc index ca929b280d169a5d47e52b6eedadfbb45d03802e..323cc18673d264dcc53b8ff870e2aedccdd29bad 100644 --- a/src/box/lua/tuple.cc +++ b/src/box/lua/tuple.cc @@ -301,19 +301,14 @@ lbox_tuple_transform(struct lua_State *L) } void -lbox_pushtuple(struct lua_State *L, struct tuple *tuple) +lbox_pushtuple_noref(struct lua_State *L, struct tuple *tuple) { - if (tuple) { - assert(CTID_CONST_STRUCT_TUPLE_REF != 0); - struct tuple **ptr = (struct tuple **) luaL_pushcdata(L, - CTID_CONST_STRUCT_TUPLE_REF, sizeof(struct tuple *)); - *ptr = tuple; - lua_pushcfunction(L, lbox_tuple_gc); - luaL_setcdatagc(L, -2); - tuple_ref(tuple); - } else { - return lua_pushnil(L); - } + assert(CTID_CONST_STRUCT_TUPLE_REF != 0); + struct tuple **ptr = (struct tuple **) luaL_pushcdata(L, + CTID_CONST_STRUCT_TUPLE_REF, sizeof(struct tuple *)); + *ptr = tuple; + lua_pushcfunction(L, lbox_tuple_gc); + luaL_setcdatagc(L, -2); } static const struct luaL_reg lbox_tuple_meta[] = { diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h index 6d69a831553c1434b72c6a647503b15a848297cd..960278d12085d535d061ac78117d8802713664e7 100644 --- a/src/box/lua/tuple.h +++ b/src/box/lua/tuple.h @@ -28,6 +28,9 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + +#include <box/tuple.h> + struct lua_State; struct txn; struct tuple; @@ -36,7 +39,15 @@ struct tuple; * Push tuple on lua stack */ void -lbox_pushtuple(struct lua_State *L, struct tuple *tuple); +lbox_pushtuple_noref(struct lua_State *L, struct tuple *tuple); + +static inline void +lbox_pushtuple(struct lua_State *L, struct tuple *tuple) +{ + assert(tuple != NULL); + lbox_pushtuple_noref(L, tuple); + tuple_ref(tuple); +} struct tuple *lua_istuple(struct lua_State *L, int narg); diff --git a/src/lua/utils.cc b/src/lua/utils.cc index fa62f3901be48d6fe14501b760ed2dfef73863e0..93cf32ce9845cc9b52c59851d96442c5d9950847 100644 --- a/src/lua/utils.cc +++ b/src/lua/utils.cc @@ -96,6 +96,23 @@ luaL_ctypeid(struct lua_State *L, const char *ctypename) return ctypeid; } +int +luaL_cdef(struct lua_State *L, const char *what) +{ + int idx = lua_gettop(L); + /* This function calls ffi.cdef */ + + /* Get ffi.typeof function */ + luaL_loadstring(L, "return require('ffi').cdef"); + lua_call(L, 0, 1); + /* FFI must exist */ + assert(lua_gettop(L) == idx + 1 && lua_isfunction(L, idx + 1)); + /* Push the argument to ffi.cdef */ + lua_pushstring(L, what); + /* Call ffi.cdef() */ + return lua_pcall(L, 1, 0, 0); +} + int luaL_setcdatagc(struct lua_State *L, int idx) { diff --git a/src/lua/utils.h b/src/lua/utils.h index f1d346695eb1e4f2e3ae449f4cb755ae8bee1bda..3e5cc54eadd966704035a4ab0047826da772faf7 100644 --- a/src/lua/utils.h +++ b/src/lua/utils.h @@ -101,6 +101,17 @@ luaL_setcdatagc(struct lua_State *L, int idx); LUA_API uint32_t luaL_ctypeid(struct lua_State *L, const char *ctypename); +/** +* @brief Declare symbols for FFI +* @param L Lua State +* @param what C definitions +* @sa ffi.cdef(def) +* @retval 0 on success +* @retval LUA_ERRRUN, LUA_ERRMEM, LUA_ERRERR otherwise +*/ +LUA_API int +luaL_cdef(struct lua_State *L, const char *ctypename); + /** \endcond public */ static inline lua_Integer diff --git a/test/box/select.result b/test/box/select.result index cc4522a46fd83340722bd7bcee010c85d4c4c0b4..e621bcfb0ce102b73036a8fb5fc03c8ea9617b7b 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 }) --- ... @@ -8,65 +11,104 @@ index2 = s:create_index('second', { type = 'tree', unique = true, parts = {2, ' --- ... for i = 1, 20 do s:insert({ i, 1, 2, 3 }) end +--- +... +--# setopt delimiter ';' +local function test_op(op, idx, ...) + local t1 = idx[op .. '_ffi'](idx, ...) + local t2 = idx[op .. '_luac'](idx, ...) + if msgpack.encode(t1) ~= msgpack.encode(t2) then + return 'different result from '..op..'_ffi and '..op..'_luac', t1, t2 + end + return t1 +end +test = setmetatable({}, { + __index = function(_, op) return function(...) return test_op(op, ...) end end +}) +--- +... +--# setopt delimiter '' +local function test_op(op, idx, ...) + local t1 = idx[op .. '_ffi'](idx, ...) + local t2 = idx[op .. '_luac'](idx, ...) + if msgpack.encode(t1) ~= msgpack.encode(t2) then + return 'different result from '..op..'_ffi and '..op..'_luac', t1, t2 + end + return t1 +end +test = setmetatable({}, { + __index = function(_, op) return function(...) return test_op(op, ...) end end +}) + --- ... -------------------------------------------------------------------------------- -- get tests -------------------------------------------------------------------------------- -s.index[0]:get() +s.index[0].get == s.index[0].get_ffi or s.index[0].get == s.index[0].get_luac +--- +- true +... +test.get(s.index[0]) --- - error: Invalid key part count in an exact match (expected 1, got 0) ... -s.index[0]:get({}) +test.get(s.index[0], {}) --- - error: Invalid key part count in an exact match (expected 1, got 0) ... -s.index[0]:get(nil) +test.get(s.index[0], nil) --- - error: Invalid key part count in an exact match (expected 1, got 0) ... -s.index[0]:get(1) +test.get(s.index[0], 1) --- - [1, 1, 2, 3] ... -s.index[0]:get({1}) +test.get(s.index[0], {1}) --- - [1, 1, 2, 3] ... -s.index[0]:get({1, 2}) +test.get(s.index[0], {1, 2}) --- - error: Invalid key part count in an exact match (expected 1, got 2) ... -s.index[0]:get(0) +test.get(s.index[0], 0) --- +- null ... -s.index[0]:get({0}) +test.get(s.index[0], {0}) --- +- null ... -s.index[0]:get("0") +test.get(s.index[0], "0") --- - error: 'Supplied key type of part 0 does not match index part type: expected NUM' ... -s.index[0]:get({"0"}) +test.get(s.index[0], {"0"}) --- - error: 'Supplied key type of part 0 does not match index part type: expected NUM' ... -s.index[1]:get(1) +test.get(s.index[1], 1) --- - error: Invalid key part count in an exact match (expected 2, got 1) ... -s.index[1]:get({1}) +test.get(s.index[1], {1}) --- - error: Invalid key part count in an exact match (expected 2, got 1) ... -s.index[1]:get({1, 2}) +test.get(s.index[1], {1, 2}) --- - [2, 1, 2, 3] ... -------------------------------------------------------------------------------- -- select tests -------------------------------------------------------------------------------- -s.index[0]:select() +s.index[0].select == s.index[0].select_ffi or s.index[0].select == s.index[0].select_luac +--- +- true +... +test.select(s.index[0]) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -89,7 +131,7 @@ s.index[0]:select() - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}) +test.select(s.index[0], {}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -112,7 +154,7 @@ s.index[0]:select({}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil) +test.select(s.index[0], nil) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -135,7 +177,7 @@ s.index[0]:select(nil) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select({}, {iterator = 'ALL'}) +test.select(s.index[0], {}, {iterator = 'ALL'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -158,7 +200,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.select(s.index[0], nil, {iterator = box.index.ALL }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -181,7 +223,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.select(s.index[0], {}, {iterator = box.index.ALL, limit = 10}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -194,15 +236,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.select(s.index[0], nil, {iterator = box.index.ALL, limit = 0}) --- - [] ... -s.index[0]:select({}, {iterator = 'ALL', limit = 1, offset = 15}) +test.select(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.select(s.index[0], nil, {iterator = 'ALL', limit = 20, offset = 15}) --- - - [16, 1, 2, 3] - [17, 1, 2, 3] @@ -210,7 +252,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.select(s.index[0], nil, {iterator = box.index.EQ}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -233,7 +275,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.select(s.index[0], {}, {iterator = 'EQ'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -256,7 +298,7 @@ s.index[0]:select({}, {iterator = 'EQ'}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[0]:select(nil, {iterator = 'REQ'}) +test.select(s.index[0], nil, {iterator = 'REQ'}) --- - - [20, 1, 2, 3] - [19, 1, 2, 3] @@ -279,7 +321,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.select(s.index[0], {}, {iterator = box.index.REQ}) --- - - [20, 1, 2, 3] - [19, 1, 2, 3] @@ -302,45 +344,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.select(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.select(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.select(s.index[0], 1) --- - - [1, 1, 2, 3] ... -s.index[0]:select({1}) +test.select(s.index[0], {1}) --- - - [1, 1, 2, 3] ... -s.index[0]:select({1, 2}) +test.select(s.index[0], {1, 2}) --- - error: Invalid key part count (expected [0..1], got 2) ... -s.index[0]:select(0) +test.select(s.index[0], 0) --- - [] ... -s.index[0]:select({0}) +test.select(s.index[0], {0}) --- - [] ... -s.index[0]:select("0") +test.select(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.select(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.select(s.index[1], 1) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -363,7 +405,7 @@ s.index[1]:select(1) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[1]:select({1}) +test.select(s.index[1], {1}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -386,12 +428,12 @@ s.index[1]:select({1}) - [19, 1, 2, 3] - [20, 1, 2, 3] ... -s.index[1]:select({1}, {limit = 2}) +test.select(s.index[1], {1}, {limit = 2}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] ... -s.index[1]:select(1, {iterator = 'EQ'}) +test.select(s.index[1], 1, {iterator = 'EQ'}) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -414,29 +456,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.select(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.select(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.select(s.index[1], {1, 2}, {iterator = 'EQ'}) --- - - [2, 1, 2, 3] ... -s.index[1]:select({1, 2}, {iterator = box.index.REQ}) +test.select(s.index[1], {1, 2}, {iterator = box.index.REQ}) --- - - [2, 1, 2, 3] ... -s.index[1]:select({1, 2}) +test.select(s.index[1], {1, 2}) --- - - [2, 1, 2, 3] ... -s.index[0]:select(nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) +test.select(s.index[0], nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -459,7 +501,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.select(s.index[0], {}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -482,19 +524,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.select(s.index[0], 1) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = box.index.EQ }) +test.select(s.index[0], 1, { iterator = box.index.EQ }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'EQ' }) +test.select(s.index[0], 1, { iterator = 'EQ' }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'GE' }) +test.select(s.index[0], 1, { iterator = 'GE' }) --- - - [1, 1, 2, 3] - [2, 1, 2, 3] @@ -517,16 +559,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.select(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.select(s.index[0], 1, { iterator = 'LE', limit = 2 }) --- - - [1, 1, 2, 3] ... -s.index[0]:select(1, { iterator = 'GE', offset = 10, limit = 2 }) +test.select(s.index[0], 1, { iterator = 'GE', offset = 10, limit = 2 }) --- - - [11, 1, 2, 3] - [12, 1, 2, 3] @@ -535,6 +577,43 @@ s:select(2) --- - - [2, 1, 2, 3] ... +-------------------------------------------------------------------------------- +-- min/max tests +-------------------------------------------------------------------------------- +test.min(s.index[1]) +--- +- [1, 1, 2, 3] +... +test.max(s.index[1]) +--- +- [20, 1, 2, 3] +... +-------------------------------------------------------------------------------- +-- count tests +-------------------------------------------------------------------------------- +test.count(s.index[1]) +--- +- 20 +... +test.count(s.index[0], nil) +--- +- 20 +... +test.count(s.index[0], {}) +--- +- 20 +... +test.count(s.index[0], 10, { iterator = 'GT'}) +--- +- 10 +... +-------------------------------------------------------------------------------- +-- random tests +-------------------------------------------------------------------------------- +test.random(s.index[0], 48) +--- +- [9, 1, 2, 3] +... s:drop() --- ... diff --git a/test/box/select.test.lua b/test/box/select.test.lua index 2ac00390ff93c1d046dba40c749b81a9d48af898..d36d866f59d121f9ede0b8f4636a5b98877acbcb 100644 --- a/test/box/select.test.lua +++ b/test/box/select.test.lua @@ -1,80 +1,124 @@ +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'}}) for i = 1, 20 do s:insert({ i, 1, 2, 3 }) end +--# setopt delimiter ';' +local function test_op(op, idx, ...) + local t1 = idx[op .. '_ffi'](idx, ...) + local t2 = idx[op .. '_luac'](idx, ...) + if msgpack.encode(t1) ~= msgpack.encode(t2) then + return 'different result from '..op..'_ffi and '..op..'_luac', t1, t2 + end + return t1 +end +test = setmetatable({}, { + __index = function(_, op) return function(...) return test_op(op, ...) end end +}) +--# setopt delimiter '' + + -------------------------------------------------------------------------------- -- get tests -------------------------------------------------------------------------------- -s.index[0]:get() -s.index[0]:get({}) -s.index[0]:get(nil) -s.index[0]:get(1) -s.index[0]:get({1}) -s.index[0]:get({1, 2}) -s.index[0]:get(0) -s.index[0]:get({0}) -s.index[0]:get("0") -s.index[0]:get({"0"}) - -s.index[1]:get(1) -s.index[1]:get({1}) -s.index[1]:get({1, 2}) +s.index[0].get == s.index[0].get_ffi or s.index[0].get == s.index[0].get_luac + +test.get(s.index[0]) +test.get(s.index[0], {}) +test.get(s.index[0], nil) +test.get(s.index[0], 1) +test.get(s.index[0], {1}) +test.get(s.index[0], {1, 2}) +test.get(s.index[0], 0) +test.get(s.index[0], {0}) +test.get(s.index[0], "0") +test.get(s.index[0], {"0"}) + +test.get(s.index[1], 1) +test.get(s.index[1], {1}) +test.get(s.index[1], {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 }) +s.index[0].select == s.index[0].select_ffi or s.index[0].select == s.index[0].select_luac + +test.select(s.index[0]) +test.select(s.index[0], {}) +test.select(s.index[0], nil) +test.select(s.index[0], {}, {iterator = 'ALL'}) + +test.select(s.index[0], nil, {iterator = box.index.ALL }) +test.select(s.index[0], {}, {iterator = box.index.ALL, limit = 10}) +test.select(s.index[0], nil, {iterator = box.index.ALL, limit = 0}) +test.select(s.index[0], {}, {iterator = 'ALL', limit = 1, offset = 15}) +test.select(s.index[0], nil, {iterator = 'ALL', limit = 20, offset = 15}) + +test.select(s.index[0], nil, {iterator = box.index.EQ}) +test.select(s.index[0], {}, {iterator = 'EQ'}) +test.select(s.index[0], nil, {iterator = 'REQ'}) +test.select(s.index[0], {}, {iterator = box.index.REQ}) + +test.select(s.index[0], nil, {iterator = 'EQ', limit = 2, offset = 1}) +test.select(s.index[0], {}, {iterator = box.index.REQ, limit = 2, offset = 1}) + +test.select(s.index[0], 1) +test.select(s.index[0], {1}) +test.select(s.index[0], {1, 2}) +test.select(s.index[0], 0) +test.select(s.index[0], {0}) +test.select(s.index[0], "0") +test.select(s.index[0], {"0"}) + +test.select(s.index[1], 1) +test.select(s.index[1], {1}) +test.select(s.index[1], {1}, {limit = 2}) +test.select(s.index[1], 1, {iterator = 'EQ'}) +test.select(s.index[1], {1}, {iterator = box.index.EQ, offset = 16, limit = 2}) +test.select(s.index[1], {1}, {iterator = box.index.REQ, offset = 16, limit = 2 }) +test.select(s.index[1], {1, 2}, {iterator = 'EQ'}) +test.select(s.index[1], {1, 2}, {iterator = box.index.REQ}) +test.select(s.index[1], {1, 2}) + +test.select(s.index[0], nil, { iterator = 'ALL', offset = 0, limit = 4294967295 }) +test.select(s.index[0], {}, { iterator = 'ALL', offset = 0, limit = 4294967295 }) + +test.select(s.index[0], 1) +test.select(s.index[0], 1, { iterator = box.index.EQ }) +test.select(s.index[0], 1, { iterator = 'EQ' }) +test.select(s.index[0], 1, { iterator = 'GE' }) +test.select(s.index[0], 1, { iterator = 'GE', limit = 2 }) +test.select(s.index[0], 1, { iterator = 'LE', limit = 2 }) +test.select(s.index[0], 1, { iterator = 'GE', offset = 10, limit = 2 }) s:select(2) +-------------------------------------------------------------------------------- +-- min/max tests +-------------------------------------------------------------------------------- + +test.min(s.index[1]) +test.max(s.index[1]) + +-------------------------------------------------------------------------------- +-- count tests +-------------------------------------------------------------------------------- + +test.count(s.index[1]) +test.count(s.index[0], nil) +test.count(s.index[0], {}) +test.count(s.index[0], 10, { iterator = 'GT'}) + +-------------------------------------------------------------------------------- +-- random tests +-------------------------------------------------------------------------------- + +test.random(s.index[0], 48) + s:drop() s = box.schema.space.create('select', { temporary = true })