diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index 9f95b4d54731379862dd233ace2665859810f4e0..34f1c140681796660cd744b8930cd50b2da9bd8b 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -908,11 +908,11 @@ localhost> lua box.counter.dec(0, 'top.mail.ru') <variablelist> <title>Package <code xml:id="box.tuple" xreflabel="box.tuple">box.tuple</code></title> - <para>The package contains no functions, but stands for - <code>box.tuple</code> userdata type. It is possible to access individual - tuple fields using an index, select a range of fields, iterate - over all fields in a tuple or convert a tuple to a Lua table. - Tuples are immutable.</para> + <para>The package stands for <code>box.tuple</code> userdata + type. It is possible to access individual tuple fields using + an index, select a range of fields, iterate over all fields in + a tuple or convert a tuple to a Lua table. Tuples are + immutable.</para> <varlistentry> <term><emphasis role="lua"> </emphasis></term> <listitem><para> @@ -1000,6 +1000,19 @@ localhost> lua t:find(1, 'abc') </para></listitem> </varlistentry> + <varlistentry> + <term><emphasis role="lua">box.tuple.new(...)</emphasis></term> + <listitem><para> + Construct a new tuple from a Lua table or a scalar. + </para> + <bridgehead renderas="sect4">Example</bridgehead> + <programlisting>localhost> lua box.tuple.new({tonumber64('18446744073709551615'), 'string', 1}) +--- + - 18446744073709551615: {'string', 1} +... +</programlisting> + </listitem> + </varlistentry> </variablelist> <variablelist> diff --git a/src/box/box.lua b/src/box/box.lua index 2f68c47aa841c94a1b694fe96d50c675ffb4f146..e067fa0d4a02a4deec50878c56c6094d6c562626 100644 --- a/src/box/box.lua +++ b/src/box/box.lua @@ -244,11 +244,15 @@ function box.on_reload_configuration() space_mt.replace = function(space, ...) return box.replace(space.n, ...) end space_mt.delete = function(space, ...) return box.delete(space.n, ...) end space_mt.truncate = function(space) - local pk = space.index[0].idx - local part_count = pk:part_count() - while #pk > 0 do - for k, v in pk.next, pk, nil do - space:delete(v:slice(0, part_count)) + local pk = space.index[0] + while #pk.idx > 0 do + for _k, t in pk.idx.next, pk.idx, nil do + local key = {}; + -- ipairs does not work because pk.key_field is zero-indexed + for _k2, key_field in pairs(pk.key_field) do + table.insert(key, t[key_field.fieldno]) + end + space:delete(unpack(key)) end end end diff --git a/src/box/box_lua.m b/src/box/box_lua.m index ea505c424c7879367a5c055a2a40e53945b28980..fabd7617549c925fd548e64d7ab77d81d4f76934 100644 --- a/src/box/box_lua.m +++ b/src/box/box_lua.m @@ -79,6 +79,9 @@ static const char *tuplelib_name = "box.tuple"; static void lbox_pushtuple(struct lua_State *L, struct tuple *tuple); +static struct tuple * +lua_totuple(struct lua_State *L, int index); + static inline struct tuple * lua_checktuple(struct lua_State *L, int narg) { @@ -100,6 +103,17 @@ lua_istuple(struct lua_State *L, int narg) return tuple; } +static int +lbox_tuple_new(lua_State *L) +{ + int argc = lua_gettop(L); + if (argc < 1) + luaL_error(L, "tuple.new(): bad arguments"); + struct tuple *tuple = lua_totuple(L, 1); + lbox_pushtuple(L, tuple); + return 1; +} + static int lbox_tuple_gc(struct lua_State *L) { @@ -495,7 +509,7 @@ lbox_tuple_pairs(struct lua_State *L) return 3; } -static const struct luaL_reg lbox_tuple_meta [] = { +static const struct luaL_reg lbox_tuple_meta[] = { {"__gc", lbox_tuple_gc}, {"__len", lbox_tuple_len}, {"__index", lbox_tuple_index}, @@ -510,6 +524,11 @@ static const struct luaL_reg lbox_tuple_meta [] = { {NULL, NULL} }; +static const struct luaL_reg lbox_tuplelib[] = { + {"new", lbox_tuple_new}, + {NULL, NULL} +}; + /* }}} */ /** {{{ box.index Lua library: access to spaces and indexes @@ -1012,8 +1031,8 @@ lua_table_to_tuple(struct lua_State *L, int index) return tuple; } -static void -port_add_lua_ret(struct port *port, struct lua_State *L, int index) +static struct tuple* +lua_totuple(struct lua_State *L, int index) { int type = lua_type(L, index); struct tuple *tuple; @@ -1073,6 +1092,13 @@ port_add_lua_ret(struct port *port, struct lua_State *L, int index) tnt_raise(ClientError, :ER_PROC_RET, lua_typename(L, type)); break; } + return tuple; +} + +static void +port_add_lua_ret(struct port *port, struct lua_State *L, int index) +{ + struct tuple *tuple = lua_totuple(L, index); @try { port_add_tuple(port, tuple, BOX_RETURN_TUPLE); } @finally { @@ -1230,6 +1256,8 @@ mod_lua_init(struct lua_State *L) { /* box, box.tuple */ tarantool_lua_register_type(L, tuplelib_name, lbox_tuple_meta); + luaL_register(L, tuplelib_name, lbox_tuplelib); + lua_pop(L, 1); luaL_register(L, "box", boxlib); lua_pop(L, 1); /* box.index */ diff --git a/src/box/index.m b/src/box/index.m index ca67def06477c481d5127e5a2bb62815f7d4e0b6..6e7f1fdaf4992958d3e47413d816aa106ef5bad1 100644 --- a/src/box/index.m +++ b/src/box/index.m @@ -46,19 +46,6 @@ static struct index_traits hash_index_traits = { const char *field_data_type_strs[] = {"NUM", "NUM64", "STR", "\0"}; const char *index_type_strs[] = { "HASH", "TREE", "\0" }; -static struct tuple * -iterator_next_equal(struct iterator *it __attribute__((unused))) -{ - return NULL; -} - -static struct tuple * -iterator_first_equal(struct iterator *it) -{ - it->next_equal = iterator_next_equal; - return it->next(it); -} - static void check_key_parts(struct key_def *key_def, int part_count, bool partial_key_allowed) @@ -261,25 +248,38 @@ check_key_parts(struct key_def *key_def, /* }}} */ -/* {{{ HashIndex -- base class for all hashes. ********************/ +/* {{{ HashIndex Iterators ****************************************/ -struct hash_iterator { +struct hash_i32_iterator { struct iterator base; /* Must be the first member. */ struct mh_i32ptr_t *hash; mh_int_t h_pos; }; -static struct hash_iterator * -hash_iterator(struct iterator *it) +struct hash_i64_iterator { + struct iterator base; + struct mh_i64ptr_t *hash; + mh_int_t h_pos; +}; + +struct hash_lstr_iterator { + struct iterator base; + struct mh_lstrptr_t *hash; + mh_int_t h_pos; +}; + +void +hash_iterator_free(struct iterator *iterator) { - return (struct hash_iterator *) it; + assert(iterator->free == hash_iterator_free); + free(iterator); } struct tuple * -hash_iterator_next(struct iterator *iterator) +hash_iterator_i32_ge(struct iterator *ptr) { - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(ptr->free == hash_iterator_free); + struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { if (mh_exist(it->hash, it->h_pos)) @@ -289,13 +289,64 @@ hash_iterator_next(struct iterator *iterator) return NULL; } -void -hash_iterator_free(struct iterator *iterator) +struct tuple * +hash_iterator_i64_ge(struct iterator *ptr) { - assert(iterator->next == hash_iterator_next); - free(iterator); + assert(ptr->free == hash_iterator_free); + struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; + + while (it->h_pos < mh_end(it->hash)) { + if (mh_exist(it->hash, it->h_pos)) + return mh_value(it->hash, it->h_pos++); + it->h_pos++; + } + return NULL; +} + +struct tuple * +hash_iterator_lstr_ge(struct iterator *ptr) +{ + assert(ptr->free == hash_iterator_free); + struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; + + while (it->h_pos < mh_end(it->hash)) { + if (mh_exist(it->hash, it->h_pos)) + return mh_value(it->hash, it->h_pos++); + it->h_pos++; + } + return NULL; +} + +static struct tuple * +hash_iterator_eq_next(struct iterator *it __attribute__((unused))) +{ + return NULL; +} + +static struct tuple * +hash_iterator_i32_eq(struct iterator *it) +{ + it->next_equal = hash_iterator_eq_next; + return hash_iterator_i32_ge(it); +} + +static struct tuple * +hash_iterator_i64_eq(struct iterator *it) +{ + it->next_equal = hash_iterator_eq_next; + return hash_iterator_i64_ge(it); +} + +static struct tuple * +hash_iterator_lstr_eq(struct iterator *it) +{ + it->next_equal = hash_iterator_eq_next; + return hash_iterator_lstr_ge(it); } +/* }}} */ + +/* {{{ HashIndex -- base class for all hashes. ********************/ @implementation HashIndex @@ -370,17 +421,6 @@ hash_iterator_free(struct iterator *iterator) return [self findUnsafe :field :1]; } -- (struct iterator *) allocIterator -{ - struct hash_iterator *it = malloc(sizeof(struct hash_iterator)); - if (it) { - memset(it, 0, sizeof(struct hash_iterator)); - it->base.next = hash_iterator_next; - it->base.free = hash_iterator_free; - } - return (struct iterator *) it; -} - @end /* }}} */ @@ -479,10 +519,21 @@ int32_key_to_value(void *key) #endif } +- (struct iterator *) allocIterator +{ + struct hash_i32_iterator *it = malloc(sizeof(struct hash_i32_iterator)); + if (it) { + memset(it, 0, sizeof(*it)); + it->base.next = hash_iterator_i32_ge; + it->base.free = hash_iterator_free; + } + return (struct iterator *) it; +} + - (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type { - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_i32_ge); + struct hash_i32_iterator *it = (struct hash_i32_iterator *) iterator; if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); @@ -499,11 +550,11 @@ int32_key_to_value(void *key) if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_i32_ge); + struct hash_i32_iterator *it = (struct hash_i32_iterator *) iterator; u32 num = int32_key_to_value(key); - it->base.next_equal = iterator_first_equal; + it->base.next_equal = hash_iterator_i32_eq; it->h_pos = mh_i32ptr_get(int_hash, num); it->hash = int_hash; } @@ -602,17 +653,28 @@ int64_key_to_value(void *key) #endif } +- (struct iterator *) allocIterator +{ + struct hash_i64_iterator *it = malloc(sizeof(struct hash_i64_iterator)); + if (it) { + memset(it, 0, sizeof(*it)); + it->base.next = hash_iterator_i64_ge; + it->base.free = hash_iterator_free; + } + return (struct iterator *) it; +} + - (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type { - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_i64_ge); + struct hash_i64_iterator *it = (struct hash_i64_iterator *) iterator; if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); it->base.next_equal = 0; /* Should not be used if not positioned. */ it->h_pos = mh_begin(int64_hash); - it->hash = (struct mh_i32ptr_t *) int64_hash; + it->hash = int64_hash; } - (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type @@ -622,14 +684,14 @@ int64_key_to_value(void *key) if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_i64_ge); + struct hash_i64_iterator *it = (struct hash_i64_iterator *) iterator; u64 num = int64_key_to_value(key); - it->base.next_equal = iterator_first_equal; + it->base.next_equal = hash_iterator_i64_eq; it->h_pos = mh_i64ptr_get(int64_hash, num); - it->hash = (struct mh_i32ptr_t *) int64_hash; + it->hash = int64_hash; } @end @@ -720,17 +782,29 @@ int64_key_to_value(void *key) #endif } +- (struct iterator *) allocIterator +{ + struct hash_lstr_iterator *it = malloc(sizeof(struct hash_lstr_iterator)); + if (it) { + memset(it, 0, sizeof(*it)); + it->base.next = hash_iterator_lstr_ge; + it->base.free = hash_iterator_free; + } + return (struct iterator *) it; +} + + - (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type { - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_lstr_ge); + struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) iterator; if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); it->base.next_equal = 0; /* Should not be used if not positioned. */ it->h_pos = mh_begin(str_hash); - it->hash = (struct mh_i32ptr_t *) str_hash; + it->hash = str_hash; } - (void) initIteratorUnsafe: (struct iterator *) iterator @@ -741,12 +815,12 @@ int64_key_to_value(void *key) if (type == ITER_REVERSE) tnt_raise(IllegalParams, :"hash iterator is forward only"); - assert(iterator->next == hash_iterator_next); - struct hash_iterator *it = hash_iterator(iterator); + assert(iterator->next == hash_iterator_lstr_ge); + struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) iterator; - it->base.next_equal = iterator_first_equal; + it->base.next_equal = hash_iterator_lstr_eq; it->h_pos = mh_lstrptr_get(str_hash, key); - it->hash = (struct mh_i32ptr_t *) str_hash; + it->hash = str_hash; } @end diff --git a/test/big/lua.result b/test/big/lua.result index 6698c0b0fdd27fa686b9f4ea6f2367fddede0481..e6b2e3261694f82d0316c3e9f109cc6aa3da3178 100644 --- a/test/big/lua.result +++ b/test/big/lua.result @@ -557,3 +557,37 @@ error: 'Duplicate key exists in unique index 1' lua box.space[1]:truncate() --- ... +# +# A test case for Bug #1042798 +# Truncate hangs when primary key is not in linear or starts at the first field +# https://bugs.launchpad.net/tarantool/+bug/1042798 +# +lua for k, f in pairs(box.space[23].index[0].key_field) do print(k, ' => ', f.fieldno) end +--- +0 => 2 +1 => 1 +... +lua box.insert(23, 1, 2, 3, 4) +--- + - 1: {2, 3, 4} +... +lua box.insert(23, 10, 20, 30, 40) +--- + - 10: {20, 30, 40} +... +lua box.insert(23, 20, 30, 40, 50) +--- + - 20: {30, 40, 50} +... +lua for _k, v in box.space[23]:pairs() do print(v) end +--- +1: {2, 3, 4} +10: {20, 30, 40} +20: {30, 40, 50} +... +lua box.space[23]:truncate() +--- +... +lua for _k, v in box.space[23]:pairs() do print(v) end +--- +... diff --git a/test/big/lua.test b/test/big/lua.test index a3f0bdf005338f714637f88f69cc126b0767a809..2f90fb5ef18ac6297cfefebbf07ccf2ec013ac6b 100644 --- a/test/big/lua.test +++ b/test/big/lua.test @@ -205,3 +205,20 @@ print """# Test that we print index number in error ER_INDEX_VIOLATION""" exec admin "lua box.space[1]:insert(1, 'hello', 'world')" exec admin "lua box.space[1]:insert(2, 'hello', 'world')" exec admin "lua box.space[1]:truncate()" + +print """# +# A test case for Bug #1042798 +# Truncate hangs when primary key is not in linear or starts at the first field +# https://bugs.launchpad.net/tarantool/+bug/1042798 +#""" + +# Print key fields in pk +exec admin "lua for k, f in pairs(box.space[23].index[0].key_field) do print(k, ' => ', f.fieldno) end" +exec admin "lua box.insert(23, 1, 2, 3, 4)" +exec admin "lua box.insert(23, 10, 20, 30, 40)" +exec admin "lua box.insert(23, 20, 30, 40, 50)" +exec admin "lua for _k, v in box.space[23]:pairs() do print(v) end" +# Truncate must not hang +exec admin "lua box.space[23]:truncate()" +# Empty result +exec admin "lua for _k, v in box.space[23]:pairs() do print(v) end" diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 6bea4bc90024896075b87899402ed716a53277fa..06c6c576a70f5f7e656e2be8e45395bea327c757 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -243,3 +243,12 @@ space[19].index[0].key_field[0].fieldno = 0 space[19].index[0].key_field[0].type = "NUM" space[19].index[0].key_field[1].fieldno = 2 space[19].index[0].key_field[1].type = "NUM" + +# Space #23, https://bugs.launchpad.net/tarantool/+bug/1042798 +space[23].enabled = 1 +space[23].index[0].type = "TREE" +space[23].index[0].unique = 1 +space[23].index[0].key_field[0].fieldno = 2 +space[23].index[0].key_field[0].type = "NUM" +space[23].index[0].key_field[1].fieldno = 1 +space[23].index[0].key_field[1].type = "NUM" diff --git a/test/box/lua.result b/test/box/lua.result index 429192fad9f45fa5a4a1f06108f5c440b317a94c..3b091689e690971ce16c65b4ad5fb0636a2bbc15 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -15,29 +15,30 @@ lua for n in pairs(box) do print(' - box.', n) end - box.fiber - box.select_reverse_range - box.uuid - - box.ipc + - box.select_limit - box.delete - - box.replace - box.space + - box.replace - box.cfg - box.on_reload_configuration + - box.counter - box.select_range - box.insert - - box.counter - box.auto_increment + - box.update - box.info - box.session - box.uuid_hex - - box.update + - box.select - box.dostring - box.process - - box.select_limit + - box.ipc - box.slab - - box.select + - box.stat - box.flags - box.unpack + - box.tuple - box.index - - box.stat - box.pack ... lua box.pack() @@ -1731,3 +1732,24 @@ lua tostring(tonumber64(tonumber64(3))) --- - 3ULL ... +# box.tuple.new test +lua box.tuple.new() +--- +error: 'tuple.new(): bad arguments' +... +lua box.tuple.new(1) +--- + - 1: {} +... +lua box.tuple.new('string') +--- + - 'string': {} +... +lua box.tuple.new(tonumber64('18446744073709551615')) +--- + - 18446744073709551615: {} +... +lua box.tuple.new({tonumber64('18446744073709551615'), 'string', 1}) +--- + - 18446744073709551615: {'string', 1} +... diff --git a/test/box/lua.test b/test/box/lua.test index 5b869eb5ed4ad752d8263ce2f31ef8cc621cc488..778f01daa7358c7824529dd340301c3f8969af3b 100644 --- a/test/box/lua.test +++ b/test/box/lua.test @@ -577,3 +577,10 @@ exec admin "lua bit.bor(1, 2)" print """# A test case for Bug#1061747 'tonumber64 is not transitive'""" exec admin "lua tonumber64(tonumber64(2))" exec admin "lua tostring(tonumber64(tonumber64(3)))" + +print """# box.tuple.new test""" +exec admin "lua box.tuple.new()" +exec admin "lua box.tuple.new(1)" +exec admin "lua box.tuple.new('string')" +exec admin "lua box.tuple.new(tonumber64('18446744073709551615'))" +exec admin "lua box.tuple.new({tonumber64('18446744073709551615'), 'string', 1})"