diff --git a/doc/user/iterator-types.xml b/doc/user/iterator-types.xml new file mode 100644 index 0000000000000000000000000000000000000000..a07950d5b979559d0566ebf5291961d26c34a50f --- /dev/null +++ b/doc/user/iterator-types.xml @@ -0,0 +1,159 @@ +<!DOCTYPE table [ +<!ENTITY % tnt SYSTEM "../tnt.ent"> +%tnt; +]> +<table xmlns="http://docbook.org/ns/docbook" version="5.0" + xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:xlink="http://www.w3.org/1999/xlink" + xml:id="iterator-types" frame='all' pgwide='1'> + +<title>Iterator types</title> + +<tgroup cols='3' colsep='1' rowsep='1'> +<colspec colnum="5" colname="col4" colwidth="6*"/> + +<thead> + <row> + <entry>Type</entry> + <entry>Arguments</entry> + <entry>HASH</entry> + <entry>TREE</entry> + <entry>Description</entry> + </row> +</thead> + +<tbody> + <row> + <entry>box.index.ALL</entry> + <entry>none</entry> + <entry>yes</entry> + <entry>yes</entry> + <entry> + Iterate over all tuples in an index. When iterating + over a TREE index, tuples are returned in ascending + order of the key. When iterating over a HASH index, + tuples are returned in physical order or, in other words, + unordered. + </entry> + </row> + + <row> + <entry>box.index.EQ</entry> + <entry>key</entry> + <entry>yes</entry> + <entry>yes</entry> + <entry> + <simpara> + Equality iterator: iterate over all tuples matching + the key. + Parts of a multipart key need to be separated by + comma. + </simpara> + <simpara> + Semantics of the match depends on the index. + A HASH index only supports exact match: all parts + of a key participating in the index must be provided. + In case of TREE index, only few parts of a key or a + key prefix are accepted for search. + In this case, all tuples with the same prefix or + matching key parts are considered matching the search + criteria. + </simpara> + <simpara> + A non-unique HASH index returns tuples in unspecified + order. + When a TREE index is not unique, or only part of a key + is given as a search criteria, matching tuples are + returned in ascending order. + </simpara> + </entry> + </row> + + <row> + <entry>box.index.REQ</entry> + <entry>key</entry> + <entry>no</entry> + <entry>yes</entry> + <entry> + Reverse equality iterator. Is equivalent to + <code>box.index.EQ</code> with only distinction that + the order of returned tuples is descending, not + ascending. Is only supported by TREE + index. + </entry> + </row> + + <row> + <entry>box.index.GT</entry> + <entry>key</entry> + <entry>yes (*)</entry> + <entry>yes </entry> + <entry> + Iterate over tuples strictly greater than the search key. + For TREE indexes, a key prefix or key part can be sufficient. + If the key is <code>nil</code>, iteration starts from + the smallest key in the index. The tuples are returned + in ascending order of the key. + HASH index also supports this iterator type, but returns + tuples in unspecified order. However, if the server + does not receive updates, this iterator can be used + to retrieve all tuples via a HASH index piece by piece, + by supplying the last key from the previous range as the + start key for an iterator over the next range. + </entry> + </row> + + <row> + <entry>box.index.GE</entry> + <entry>key</entry> + <entry>no</entry> + <entry>yes</entry> + <entry> + Iterate over all tuples for which the corresponding fields are + greater or equal to the search key. TREE index returns + tuples in ascending order. Similarly to <code>box.index.EQ</code>, + key prefix or key part can be used to seed the iterator. + If the key is <code>nil</code>, iteration starts from the + smallest key in the index. + </entry> + </row> + + <row> + <entry>box.index.LT</entry> + <entry>key</entry> + <entry>no</entry> + <entry>yes</entry> + <entry> + Similar to <code>box.index.GT</code>, + but returns all tuples which are strictly less + than the search key. The tuples are returned + in the descending order of the key. + <code>nil</code> key can be used to start + from the end of the index range. + </entry> + </row> + + <row> + <entry>box.index.LE</entry> + <entry>key</entry> + <entry>no</entry> + <entry>yes</entry> + <entry> + Similar to <code>box.index.GE</code>, but + returns all tuples which are less or equal to the + search key or key prefix, and returns tuples + in descending order, from biggest to smallest. + If the key is <code>nil</code>, iteration starts + from the end of the index range. + </entry> + </row> + +</tbody> + +</tgroup> +</table> + +<!-- +vim: tw=66 syntax=docbk +vim: spell spelllang=en_us +--> diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index 22c57c94581d99c021712afe920b63e1e499bbc7..f142fc5deeb37d7c7f2e216dd96561c64ae9421d 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -293,8 +293,9 @@ localhost> lua type(i), type(i*2), type(i/2), i, i*2, i/2 </varlistentry> </variablelist> -<variablelist> - <title>Package <code xml:id="box" xreflabel="box">box</code> function index</title> +<section> + <title>Package <code>box</code></title> +<variablelist xml:id="box" xreflabel="box"> <varlistentry> <term> <emphasis role="lua">box.process(op, request)</emphasis> @@ -344,7 +345,9 @@ localhost> lua type(i), type(i*2), type(i/2), i, i*2, i/2 <varlistentry> <term> - <emphasis role="lua">box.select(space_no, index_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.select" xreflabel="box.select"> + box.select(space_no, index_no, ...) + </emphasis> </term> <listitem> <para> @@ -507,24 +510,27 @@ Call ERROR, Illegal parameters, key is not u32 (ER_ILLEGAL_PARAMS) </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.select_range(space_no, index_no, limit, key, ...)</emphasis> + <emphasis role="lua" xml:id="box.select_range" xreflabel="box.select_range"> + box.select_range(space_no, index_no, limit, key, ...) + </emphasis> </term> <listitem><para> Select a range of tuples, starting from offset specified by <code>key</code>. The key can be - multipart. - Limit selection with at most <code>limit</code> - tuples. - If no key is specified, start from the first key in - the index. - </para> - <para> + multipart. Limit selection with at most + <code>limit</code> tuples. If no key is specified, + start from the first key in the index. + </para> + <para> For TREE indexes, this returns tuples in sorted order. For HASH indexes, the order of tuples is unspecified, and can change significantly if data is inserted or deleted between two calls to <code>box.select_range()</code>. If <code>key</code> is <code>nil</code> or unspecified, the selection starts from the start of the index. + This is a simple wrapper around <code xlink:href="#box.space.select_range">box.space[space_no]:select_range(index_no, ...)</code>. + </para> + <para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> show configuration --- @@ -577,7 +583,9 @@ localhost> lua box.select_range(4, 1, 2, '1') </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.select_reverse_range(space_no, index_no, limit, key, ...)</emphasis> + <emphasis role="lua" xml:id="box.select_reverse_range" xreflabel="box.select_reverse_range"> + box.select_reverse_range(space_no, index_no, limit, key, ...) + </emphasis> </term> <listitem><para> Select a reverse range of tuples, starting from the offset @@ -590,7 +598,7 @@ localhost> lua box.select_range(4, 1, 2, '1') </para> <para> For TREE indexes, this returns tuples in sorted order. - Reverse iteration for HASH indexes is not supported. + Reverse iteration for HASH indexes is not supported. If <code>key</code> is <code>nil</code> or unspecified, the selection starts from the end of the index. <bridgehead renderas="sect4">Example</bridgehead> @@ -905,9 +913,12 @@ localhost> lua box.counter.dec(0, 'top.mail.ru') </listitem> </varlistentry> </variablelist> +</section> -<variablelist> - <title>Package <code xml:id="box.tuple" xreflabel="box.tuple">box.tuple</code></title> +<section> + <title>Package <code>box.tuple</code></title> + +<variablelist xml:id="box.tuple" xreflabel="box.tuple"> <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 @@ -1001,9 +1012,11 @@ localhost> lua t:find(1, 'abc') </para></listitem> </varlistentry> </variablelist> +</section> -<variablelist> - <title>Package <code xml:id="box.space" xreflabel="box.space">box.space</code></title> +<section> + <title>Package <code>box.space</code></title> +<variablelist xml:id="box.space" xreflabel="box.space"> <para>This package is a container for all configured spaces. A space object provides access to space attributes, such as id, whether or not a space is @@ -1056,16 +1069,43 @@ localhost> lua t:find(1, 'abc') <varlistentry> <term> - <emphasis role="lua">space:select_range(index_no, limit, key, ...)</emphasis> + <emphasis role="lua" xml:id="box.space.select_range" xreflabel="box.space[i].select_range()"> + space:select_range(index_no, limit, key) + </emphasis> </term> - <listitem><simpara></simpara></listitem> + <listitem> + <simpara> + Select a range of tuples, starting from offset specified by + <code>key</code>. The key can be multipart. + Limit selection with at most <code>limit</code> tuples. + If no key is specified, start from the first key in the index. + </simpara> + <simpara> + For TREE indexes, this returns tuples in sorted order. + For other indexes, the order of tuples is unspecified, and + can change significantly if data is inserted or deleted + between two calls to <code>select_range()</code>. + If <code>key</code> is <code>nil</code> or unspecified, + the selection starts from the start of the index. + </simpara> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:select_reverse_range(index_no, limit, key, ...)</emphasis> + <emphasis role="lua" + xml:id="box.space.select_reverse_range" + xreflabel="box.space.select_reverse_range"> + space:select_reverse_range(limit, key)</emphasis> </term> - <listitem><simpara></simpara></listitem> + <listitem> + <simpara> + Select a reverse range of tuples, limited by + <code>limit</code>, starting from <code>key</code>. + The key can be multipart. TREE index returns tuples in descending + order. Not supported by HASH indexes. + </simpara> + </listitem> </varlistentry> <varlistentry> @@ -1145,10 +1185,11 @@ localhost> lua for k,v in box.space[0]:pairs() do print(v) end </listitem> </varlistentry> </variablelist> +</section> -<variablelist> - <title>Package <code xml:id="box.index" -xreflabel="box.index">box.index</code></title> +<section> + <title>Package <code >box.index</code></title> +<variablelist xml:id="box.index" xreflabel="box.index"> <para> This package implements methods of type <code>box.index</code>. Indexes are contained in <code @@ -1192,22 +1233,100 @@ xreflabel="box.index">box.index</code></title> <varlistentry> <term> - <emphasis role="lua">space:select_range(limit, key)</emphasis> + <emphasis role="lua" xml:id="box.index.iterator" xreflabel="box.index.iterator"> + index:iterator(type, ...)</emphasis> </term> - <listitem><simpara>Select a range of tuples, limited by - <code>limit</code>, starting from <code>key</code>. - </simpara></listitem> - </varlistentry> + <listitem> + <simpara> + This method provides iteration support within an + index. Parameter <code>type</code> is used to + identify the semantics of iteration. Different + index types support different iterators. The + remaining arguments of the function are varying + and depend on the iteration type. For example, + TREE index maintains a strict order of keys and + can return all tuples in ascending or descending + order, starting from the specified key. A HASH + index, however, doesn't support ordering. + </simpara> + <para> + <bridgehead renderas="sect4">Parameters</bridgehead> + <simplelist> + <member><code>type</code> — iteration strategy as defined in a table below.</member> + </simplelist> - <varlistentry> - <term> - <emphasis role="lua">space:select_reverse_range(limit, key)</emphasis> - </term> - <listitem><simpara>Select a reverse range of tuples, limited by - <code>limit</code>, starting from <code>key</code>. - </simpara></listitem> + <bridgehead renderas="sect4">Returns</bridgehead> + This method returns an iterator closure, i.e. + a <code>function</code> which can be used to + get the next value on each invocation. + <bridgehead renderas="sect4">Errors</bridgehead> + Selected iteration type is not supported in + the subject index type or supplied parameters + do not match iteration type. + </para> + + <xi:include href="iterator-types.xml"/> + + <para> + <bridgehead renderas="sect4">Examples</bridgehead> +<programlisting> +localhost> show configuration +--- +... + space[0].enabled: "true" + space[0].index[0].type: "HASH" + space[0].index[0].unique: "true" + space[0].index[0].key_field[0].fieldno: "0" + space[0].index[0].key_field[0].type: "NUM" + space[0].index[1].type: "TREE" + space[0].index[1].unique: "false" + space[0].index[1].key_field[0].fieldno: "1" + space[0].index[1].key_field[0].type: "NUM" + space[0].index[1].key_field[1].fieldno: "2" + space[0].index[1].key_field[1].type: "NUM" +... +localhost> INSERT INTO t0 VALUES (1, 1, 0) +Insert OK, 1 rows affected +localhost> INSERT INTO t0 VALUES (2, 1, 1) +Insert OK, 1 rows affected +localhost> INSERT INTO t0 VALUES (3, 1, 2) +Insert OK, 1 rows affected +localhost> INSERT INTO t0 VALUES (4, 2, 0) +Insert OK, 1 rows affected +localhost> INSERT INTO t0 VALUES (5, 2, 1) +Insert OK, 1 rows affected +localhost> INSERT INTO t0 VALUES (6, 2, 2) +Insert OK, 1 rows affected +localhost> lua it = box.space[0].index[1]:iterator(box.index.EQ, 1); print(it(), " ", it(), " ", it()); +--- +1: {1, 0} 2: {1, 1} 3: {1, 2} +... +localhost> lua it = box.space[0].index[1]:iterator(box.index.EQ, 1, 2); print(it(), " ", it(), " ", it()); +--- +3: {1, 2} nil nil +... +localhost> lua i = box.space[0].index[1]:iterator(box.index.GE, 2, 1); print(it(), " ", it(), " ", it()); +--- +5: {2, 1} 6: {2, 2} nil +... +localhost> lua for v in box.space[0].index[1]:iterator(box.index.ALL) do print(v) end +--- +1: {1, 0} +2: {1, 1} +3: {1, 2} +4: {2, 0} +5: {2, 1} +6: {2, 2} +... +localhost> lua i = box.space[0].index[0]:iterator(box.index.LT, 1); +--- +error: 'Iterator type is not supported' +</programlisting> + </para> + </listitem> </varlistentry> + <varlistentry> <term> <emphasis role="lua">index:min()</emphasis> @@ -1229,170 +1348,26 @@ xreflabel="box.index">box.index</code></title> </simpara> </listitem> </varlistentry> -</variablelist> - -<para> - The following functions provide iteration support within an index. - <code>next</code> and <code>next_equal</code> provide common - iterators which go from the beginning to the end of the index. - <code>prev</code> and <code>prev_equal</code> provide reverse - iterators which travers the index in the reverse order. - In a non-unique index the routines with the <code>'_equal'</code> - suffix will traverse sequences of tuples with the same key value. -</para> -<variablelist> - <varlistentry> - <term> - <emphasis role="lua">index:next([iteration_state or key])</emphasis> - </term> - <listitem><simpara></simpara></listitem> - </varlistentry> - <varlistentry> - <term> - <emphasis role="lua">index:prev([iteration_state or key])</emphasis> - </term> - <listitem><simpara></simpara></listitem> - </varlistentry> - <varlistentry> - <term> - <emphasis role="lua">index:next_equal([iteration_state or key])</emphasis> - </term> - <listitem><simpara></simpara></listitem> - </varlistentry> - - <varlistentry> - <term> - <emphasis role="lua">index:prev_equal([iteration_state or key])</emphasis> - </term> - <listitem> - <para> - All these function can be used either to start iteration, or to fetch -the next tuple which satisfies iteration criteria. - </para> - <para> -To start iteration, either supply a key (possibly multipart) to -position the iterator at the matching tuple. If key is nil or -a function is called with no arguments, iteration is started -from the first (last) tuple in the index. - </para> - <para> - The functions follow the - <link xlink:href='http://pgl.yoyo.org/luai/i/next'>Lua iteration - pattern</link> returning <code>{iteration_state, tuple}</code> pair, - where <code>iteration_state</code> is the iterator and - <code>tuple</code> is the tuple the iterator points at. - The function returns <code>nil</code> if the iterator reaches the - end of the index or there are no more tuples satisfying the - <code>key</code> (for <code>_equal</code> functions). - </para> - <bridgehead renderas="sect4">Example</bridgehead> -<programlisting> -localhost> lua box.insert(0, "United Kingdom, The", "Europe") -localhost> lua box.insert(0, "France", "Europe") -localhost> lua box.insert(0, "Netherlands", "Europe") -localhost> lua box.insert(0, "Australia", "Oceania") -localhost> lua box.insert(0, "Vanuatu", "Oceania") -localhost> lua box.insert(0, "United States of America, The", "North America") -localhost> lua box.insert(0, "Canada", "North America") -localhost> lua box.insert(0, "Mexico", "North America") -localhost> lua pi = box.space[0].index[0] -- primary index -localhost> lua si = box.space[0].index[1] -- secondary index -localhost> lua k,v=pi:next() -- init iterator by primary from the beginning ---- -... -localhost> lua print(v) ---- -'Australia': {'Oceania'} -... -localhost> lua k,v=pi:next(k) -- move to next ---- -... -localhost> lua print(v) ---- -'Canada': {'North America'} -... -localhost> lua k,v=pi:next(k) -- move to next ---- -... -localhost> lua print(v) ---- -'France': {'Europe'} -... -localhost> lua k,v=si:next_equal() -- init iterator by secondary from the beginning ---- -... -localhost> lua print(v) ---- -'United Kingdom, The': {'Europe'} -... -localhost> lua k,v=si:next_equal(k) -- move to next ---- -... -localhost> lua print(v) ---- -'France': {'Europe'} -... -localhost> lua k,v=si:next_equal(k) -- move to next ---- -... -localhost> lua print(v) ---- -'Netherlands': {'Europe'} -... -localhost> lua for k,v in pi.next, pi, nil do print(v) end ---- -'Australia': {'Oceania'} -'Canada': {'North America'} -'France': {'Europe'} -'Mexico': {'North America'} -'Netherlands': {'Europe'} -'United Kingdom, The': {'Europe'} -'United States of America, The': {'North America'} -'Vanuatu': {'Oceania'} -... -localhost> lua for k,v in pi.prev, pi, nil do print(v) end ---- -'Vanuatu': {'Oceania'} -'United States of America, The': {'North America'} -'United Kingdom, The': {'Europe'} -'Netherlands': {'Europe'} -'Mexico': {'North America'} -'France': {'Europe'} -'Canada': {'North America'} -'Australia': {'Oceania'} -... -localhost> lua for k,v in si.next_equal, si, 'Oceania' do print(v) end ---- -'Australia': {'Oceania'} -'Vanuatu': {'Oceania'} -... -localhost> lua for k,v in si.prev_equal, si, 'North America' do print(v) end ---- -'Mexico': {'North America'} -'Canada': {'North America'} -'United States of America, The': {'North America'} -... -</programlisting> - </listitem> - </varlistentry> <varlistentry> <term> <emphasis role="lua">index:count()</emphasis> </term> <listitem><simpara> - Iterate over an index, count the number of tuples which equal the - provided search criteria. The argument can either point to a - tuple, a key, or one or more key parts. Returns the number of matched - tuples. + Iterate over an index, count the number of tuples which equal the + provided search criteria. The argument can either point to a + tuple, a key, or one or more key parts. Returns the number of matched + tuples. </simpara> </listitem> </varlistentry> </variablelist> +</section> -<variablelist> +<section> <title>Package <code>box.fiber</code></title> +<variablelist xml:id="box.fiber"> <para>Functions in this package allow you to create, run and manage existing <emphasis>fibers</emphasis>. </para> @@ -1490,7 +1465,7 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <emphasis role="lua">box.fiber.create(function) </emphasis> </term> <listitem><simpara> - Create a fiber for <code>function</code>. + Create a fiber for <code>function</code>. </simpara> <bridgehead renderas="sect4">Errors</bridgehead> <simpara>Can hit a recursion limit.</simpara> @@ -1561,11 +1536,13 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs </simpara></listitem> </varlistentry> </variablelist> +</section> <!-- end of lib --> -<variablelist> +<section> <title>Package <code>box.ipc</code> — inter procedure communication</title> +<variablelist xml:id="box.ipc"> <simpara> </simpara> <varlistentry> @@ -1622,8 +1599,10 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <term><emphasis role="lua">box.ipc.channel.broadcast(channel, message, timeout)</emphasis></term> <listitem> <simpara> - If the channel is empty, is equivalent to <emphasis role="lua">box.ipc.channel.put()</emphasis>. Otherwise sends the message to - all readers of the channel. + If the channel is empty, is equivalent to + <emphasis role="lua">box.ipc.channel.put()</emphasis>. + Otherwise sends the message to all readers of the + channel. </simpara> </listitem> </varlistentry> @@ -1719,9 +1698,15 @@ end </listitem> </varlistentry> </variablelist> +</section> + +<section> + <title>Packages <code>box.cfg</code>, + <code>box.info</code>, <code>box.slab</code> and + <code>box.stat</code>: server introspection</title> <variablelist> - <title>Package <code>box.cfg</code></title> + <title>Package <code xml:id="box.cfg">box.cfg</code></title> <para>This package provides read-only access to all server configuration parameters.</para> <varlistentry> @@ -1740,6 +1725,7 @@ logger = cat - >> tarantool.log </programlisting></listitem> </varlistentry> </variablelist> + <variablelist> <title>Package <code>box.info</code></title> <para> @@ -1835,7 +1821,6 @@ options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_STATIC=OFF -DENABLE_ </listitem> </varlistentry> </variablelist> - <variablelist> <title>Package <code>box.slab</code></title> <para>This package provides access to slab allocator statistics.</para> @@ -1868,7 +1853,7 @@ bytes_free:4194144 </variablelist> <variablelist> - <title>Package <code>box.stat</code></title> + <title>Package <code xml:id="box.stat">box.stat</code></title> <para>This package provides access to request statistics.</para> <varlistentry> @@ -1916,6 +1901,7 @@ localhost> xlink:href="https://github.com/mailru/tntlua">Lua stored procedures repository</link> and in the server test suite. </para> +</section> <section> <title>Limitation of stored procedures</title> diff --git a/src/box/box.lua b/src/box/box.lua index 978773879230122f3666ff3e43f7704d1c57000a..4a5a3f02ffe350bf1de48673cd24e2da77bd8bd4 100644 --- a/src/box/box.lua +++ b/src/box/box.lua @@ -184,21 +184,34 @@ function box.bless_space(space) index_mt.min = function(index) return index.idx:min() end index_mt.max = function(index) return index.idx:max() end -- iteration + index_mt.iterator = function(index, ...) + return index.idx:iterator(...) + end + -- + -- pairs/next/prev methods are provided for backward compatibility purposes only index_mt.pairs = function(index) return index.idx.next, index.idx, nil end -- + local next_compat = function(idx, iterator_type, ...) + local arg = {...} + if #arg == 1 and type(arg[1]) == "userdata" then + return idx:next(...) + else + return idx:next(iterator_type, ...) + end + end index_mt.next = function(index, ...) - return index.idx:next(...) + return next_compat(index.idx, box.index.GE, ...); end index_mt.prev = function(index, ...) - return index.idx:prev(...) + return next_compat(index.idx, box.index.LE, ...); end index_mt.next_equal = function(index, ...) - return index.idx:next_equal(...) + return next_compat(index.idx, box.index.EQ, ...); end index_mt.prev_equal = function(index, ...) - return index.idx:prev_equal(...) + return next_compat(index.idx, box.index.REQ, ...); end -- index subtree size index_mt.count = function(index, ...) @@ -207,9 +220,10 @@ function box.bless_space(space) -- index_mt.select_range = function(index, limit, ...) local range = {} - local iterator_state, v = index:next(...) - while true do - if (#range >= limit) or (iterator_state == nil) then break end + for v in index:iterator(box.index.GE, ...) do + if #range >= limit then + break + end table.insert(range, v) iterator_state, v = index:next(iterator_state) end @@ -217,9 +231,10 @@ function box.bless_space(space) end index_mt.select_reverse_range = function(index, limit, ...) local range = {} - local iterator_state, v = index:prev(...) - while true do - if (#range >= limit) or (iterator_state == nil) then break end + for v in index:iterator(box.index.LE, ...) do + if #range >= limit then + break + end table.insert(range, v) iterator_state, v = index:prev(iterator_state) end @@ -247,7 +262,7 @@ function box.bless_space(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 + for v in pk:iterator() do space:delete(v:slice(0, part_count)) end end diff --git a/src/box/box.m b/src/box/box.m index 8d6668944cac5e7f35c520542c5ebc1484a3b412..79e032367aac92728735e88a3d0a7ec435be4f4b 100644 --- a/src/box/box.m +++ b/src/box/box.m @@ -513,7 +513,7 @@ snapshot_space(struct space *sp, void *udata) struct { struct log_io *l; struct fio_batch *batch; } *ud = udata; Index *pk = space_index(sp, 0); struct iterator *it = pk->position; - [pk initIterator: it :ITER_FORWARD]; + [pk initIterator: it :ITER_ALL :NULL :0]; while ((tuple = it->next(it))) snapshot_write_tuple(ud->l, ud->batch, space_n(sp), tuple); diff --git a/src/box/box_lua.m b/src/box/box_lua.m index cf8d8284c967bd9fd9ee906ce12c622b6fc18e6a..81b695171198f97c71a79841859dae0551e7131c 100644 --- a/src/box/box_lua.m +++ b/src/box/box_lua.m @@ -519,7 +519,7 @@ static const char *indexlib_name = "box.index"; static const char *iteratorlib_name = "box.index.iterator"; static struct iterator * -lua_checkiterator(struct lua_State *L, int i) +lbox_checkiterator(struct lua_State *L, int i) { struct iterator **it = luaL_checkudata(L, i, iteratorlib_name); assert(it != NULL); @@ -538,7 +538,7 @@ lbox_pushiterator(struct lua_State *L, struct iterator *it) static int lbox_iterator_gc(struct lua_State *L) { - struct iterator *it = lua_checkiterator(L, -1); + struct iterator *it = lbox_checkiterator(L, -1); it->free(it); return 0; } @@ -654,62 +654,71 @@ void append_key_part(struct lua_State *L, int i, tbuf_append(tbuf, str, size); } -/** +/* * Lua iterator over a Taratnool/Box index. * - * (iteration_state, tuple) = index.next(index, [iteration_state]) - * (iteration_state, tuple) = index.prev(index, [iteration_state]) + * (iteration_state, tuple) = index.next(index, [params]) * - * When [iteration_state] is absent or nil - * returns a pointer to a new iterator and + * When [params] are absent or nil + * returns a pointer to a new ALL iterator and * to the first tuple (or nil, if the index is * empty). * - * When [iteration_state] is a userdata, + * When [params] is a userdata, * i.e. we're inside an iteration loop, retrieves * the next tuple from the iterator. * - * Otherwise, [iteration_state] can be used to seed - * the iterator with one or several Lua scalars - * (numbers, strings) and start iteration from an - * offset. + * Otherwise, [params] can be used to seed + * a new iterator with iterator type and + * type-specific arguments. For exaple, + * for GE iterator, a list of Lua scalars + * cann follow the box.index.GE: this will + * start iteration from the offset specified by + * the given (multipart) key. + * + * @return Returns an iterator object, either created + * or taken from Lua stack. */ + static inline struct iterator * -lbox_index_iterator(struct lua_State *L, enum iterator_type type) +lbox_create_iterator(struct lua_State *L) { Index *index = lua_checkindex(L, 1); - int argc = lua_gettop(L) - 1; - struct iterator *it = NULL; - if (argc == 0 || (argc == 1 && lua_type(L, 2) == LUA_TNIL)) { - /* - * If there is nothing or nil on top of the stack, - * start iteration from the beginning (ITER_FORWARD) or - * end (ITER_REVERSE). - */ - it = [index allocIterator]; - [index initIterator: it :type]; - lbox_pushiterator(L, it); - } else if (argc > 1 || lua_type(L, 2) != LUA_TUSERDATA) { + int argc = lua_gettop(L); + /* Create a new iterator. */ + enum iterator_type type; + int field_count; + void *key; + if (argc == 1 || (argc == 2 && lua_type(L, 2) == LUA_TNIL)) { /* - * We've got something different from iterator's - * userdata: must be a key to start iteration from - * an offset. Seed the iterator with this key. + * Nothing or nil on top of the stack, + * iteration over entire range from the + * beginning (ITER_ALL). */ - int field_count; - void *key; - - if (argc == 1 && lua_type(L, 2) == LUA_TUSERDATA) { - /* Searching by tuple. */ + type = ITER_ALL; + field_count = 0; + key = NULL; + } else { + type = luaL_checkint(L, 2); + if (type >= iterator_type_MAX) + luaL_error(L, "unknown iterator type: %d", type); + /* What else do we have on the stack? */ + if (argc == 2 || (argc == 3 && lua_type(L, 3) == LUA_TNIL)) { + /* Nothing */ + field_count = 0; + key = NULL; + } else if (argc == 3 && lua_type(L, 3) == LUA_TUSERDATA) { + /* Tuple. */ struct tuple *tuple = lua_checktuple(L, 2); - key = tuple->data; field_count = tuple->field_count; + key = tuple->data; } else { /* Single or multi- part key. */ - field_count = argc; + field_count = argc - 2; struct tbuf *data = tbuf_alloc(fiber->gc_pool); - for (int i = 0; i < argc; ++i) - append_key_part(L, i + 2, data, - index->key_def->parts[i].type); + for (int i = 3; i <= argc; i++) + append_key_part(L, i, data, + index->key_def->parts[i-3].type); key = data->data; } /* @@ -717,81 +726,78 @@ lbox_index_iterator(struct lua_State *L, enum iterator_type type) * indexes. HASH indexes can only use single-part * keys. */ - assert(field_count != 0); if (field_count > index->key_def->part_count) - luaL_error(L, "index.next(): key part count (%d) " - "does not match index field count (%d)", + luaL_error(L, "Key part count %d" + " is greater than index part count %d", field_count, index->key_def->part_count); - it = [index allocIterator]; - [index initIteratorByKey: it :type :key :field_count]; - lbox_pushiterator(L, it); - } else { /* 1 item on the stack and it's a userdata. */ - it = lua_checkiterator(L, 2); } + struct iterator *it = [index allocIterator]; + [index initIterator: it :type :key :field_count]; + lbox_pushiterator(L, it); return it; } /** - * Lua forward index iterator function. - * See lbox_index_iterator comment for a functional - * description. + * Lua-style next() function, for use in pairs(). + * @example: + * for k, v in box.space[0].index[0].idx.next, box.space[0].index[0].idx, nil do + * print(v) + * end */ static int lbox_index_next(struct lua_State *L) { - struct iterator *it = lbox_index_iterator(L, ITER_FORWARD); + int argc = lua_gettop(L); + struct iterator *it = NULL; + if (argc == 2 && lua_type(L, 2) == LUA_TUSERDATA) { + /* + * Apart from the index itself, we have only one + * other argument, and it's a userdata: must be + * iteration state created before. + */ + it = lbox_checkiterator(L, 2); + } else { + it = lbox_create_iterator(L); + } struct tuple *tuple = it->next(it); /* If tuple is NULL, pushes nil as end indicator. */ lbox_pushtuple(L, tuple); return tuple ? 2 : 1; } -/** - * Lua reverse index iterator function. - * See lbox_index_iterator comment for a functional - * description. - */ +/** iterator() closure function. */ static int -lbox_index_prev(struct lua_State *L) +lbox_index_iterator_closure(struct lua_State *L) { - struct iterator *it = lbox_index_iterator(L, ITER_REVERSE); + /* Extract closure arguments. */ + struct iterator *it = lbox_checkiterator(L, lua_upvalueindex(1)); + struct tuple *tuple = it->next(it); - /* If tuple is NULL, pushes nil as end indicator. */ - lbox_pushtuple(L, tuple); - return tuple ? 2 : 1; -} -/** - * Lua forward index iterator function. - * See lbox_index_iterator comment for a functional - * description. - */ -static int -lbox_index_next_equal(struct lua_State *L) -{ - struct iterator *it = lbox_index_iterator(L, ITER_FORWARD); - struct tuple *tuple = it->next_equal(it); - /* If tuple is NULL, pushes nil as end indicator. */ + /* If tuple is NULL, push nil as end indicator. */ lbox_pushtuple(L, tuple); - return tuple ? 2 : 1; + return 1; } /** - * Lua reverse index iterator function. - * See lbox_index_iterator comment for a functional - * description. + * @brief Create iterator closure over a Taratnool/Box index. + * @example lua it = box.space[0].index[0]:iterator(box.index.GE, 1); + * print(it(), it()). + * @param L lua stack + * @see http://www.lua.org/pil/7.1.html + * @return number of return values put on the stack */ static int -lbox_index_prev_equal(struct lua_State *L) +lbox_index_iterator(struct lua_State *L) { - struct iterator *it = lbox_index_iterator(L, ITER_REVERSE); - struct tuple *tuple = it->next_equal(it); - /* If tuple is NULL, pushes nil as end indicator. */ - lbox_pushtuple(L, tuple); - return tuple ? 2 : 1; + /* Create iterator and push it onto the stack. */ + (void) lbox_create_iterator(L); + lua_pushcclosure(L, &lbox_index_iterator_closure, 1); + return 1; } + /** * Lua index subtree count function. * Iterate over an index, count the number of tuples which equal the @@ -826,10 +832,10 @@ lbox_index_count(struct lua_State *L) u32 count = 0; /* preparing index iterator */ struct iterator *it = index->position; - [index initIteratorByKey: it :ITER_FORWARD :key :key_part_count]; + [index initIterator: it :ITER_EQ :key :key_part_count]; /* iterating over the index and counting tuples */ struct tuple *tuple; - while ((tuple = it->next_equal(it)) != NULL) { + while ((tuple = it->next(it)) != NULL) { if (tuple->flags & GHOST) continue; count++; @@ -846,9 +852,7 @@ static const struct luaL_reg lbox_index_meta[] = { {"min", lbox_index_min}, {"max", lbox_index_max}, {"next", lbox_index_next}, - {"prev", lbox_index_prev}, - {"next_equal", lbox_index_next_equal}, - {"prev_equal", lbox_index_prev_equal}, + {"iterator", lbox_index_iterator}, {"count", lbox_index_count}, {NULL, NULL} }; @@ -1104,7 +1108,8 @@ port_add_lua_multret(struct port *port, struct lua_State *L) * Signature: * box.process(op_code, request) */ -static int lbox_process(lua_State *L) +static int +lbox_process(lua_State *L) { u32 op = lua_tointeger(L, 1); /* Get the first arg. */ struct tbuf req; @@ -1145,8 +1150,8 @@ static const struct luaL_reg boxlib[] = { * A helper to find a Lua function by name and put it * on top of the stack. */ -static -void box_lua_find(lua_State *L, const char *name, const char *name_end) +static void +box_lua_find(lua_State *L, const char *name, const char *name_end) { int index = LUA_GLOBALSINDEX; const char *start = name, *end; @@ -1217,6 +1222,17 @@ box_lua_execute(struct request *request, struct port *port) } } +static void +box_index_init_iterator_types(struct lua_State *L, int idx) +{ + for (int i = 0; i < iterator_type_MAX; i++) { + assert(strncmp(iterator_type_strs[i], "ITER_", 5) == 0); + lua_pushnumber(L, i); + /* cut ITER_ prefix from enum name */ + lua_setfield(L, idx, iterator_type_strs[i] + 5); + } +} + void mod_lua_init(struct lua_State *L) { @@ -1227,6 +1243,7 @@ mod_lua_init(struct lua_State *L) /* box.index */ tarantool_lua_register_type(L, indexlib_name, lbox_index_meta); luaL_register(L, "box.index", indexlib); + box_index_init_iterator_types(L, -2); lua_pop(L, 1); tarantool_lua_register_type(L, iteratorlib_name, lbox_iterator_meta); /* Load box.lua */ diff --git a/src/box/index.h b/src/box/index.h index fae5231dc7981f489c0f5799739388d49706476c..bf9697c48b3b98826d39af543a4588688ffa4d71 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -47,7 +47,44 @@ extern const char *field_data_type_strs[]; enum index_type { HASH, TREE, index_type_MAX }; extern const char *index_type_strs[]; -enum iterator_type { ITER_FORWARD, ITER_REVERSE }; +/** + * @abstract Iterator type + * Controls how to iterate over tuples in an index. + * Different index types support different iterator types. + * For example, one can start iteration from a particular value + * (request key) and then retrieve all tuples where keys are + * greater or equal (= GE) to this key. + * + * If iterator type is not supported by the selected index type, + * iterator constructor must fail with ER_UNSUPPORTED. To be + * selectable for primary key, an index must support at least + * ITER_EQ and ITER_GE types. + * + * NULL value of request key corresponds to the first or last + * key in the index, depending on iteration direction. + * (first key for GE and GT types, and last key for LE and LT). + * Therefore, to iterate over all tuples in an index, one can + * use ITER_GE or ITER_LE iteration types with start key equal + * to NULL. + * For ITER_EQ, the key must not be NULL. + */ +#define ITERATOR_TYPE(_) \ + _(ITER_ALL, 0) /* all tuples */ \ + _(ITER_EQ, 1) /* key == x ASC order */ \ + _(ITER_REQ, 2) /* key == x DESC order */ \ + _(ITER_LT, 3) /* key < x */ \ + _(ITER_LE, 4) /* key <= x */ \ + _(ITER_GE, 5) /* key >= x */ \ + _(ITER_GT, 6) /* key > x */ \ + +ENUM(iterator_type, ITERATOR_TYPE); +extern const char *iterator_type_strs[]; + +static inline bool +iterator_type_is_reverse(enum iterator_type type) +{ + return type == ITER_REQ || type == ITER_LT || type == ITER_LE; +} /** Descriptor of a single part in a multipart key. */ struct key_part { @@ -144,23 +181,22 @@ struct index_traits */ - (struct iterator *) allocIterator; - (void) initIterator: (struct iterator *) iterator - :(enum iterator_type) type; -- (void) initIteratorByKey: (struct iterator *) iterator - :(enum iterator_type) type - :(void *) key :(int) part_count; + :(enum iterator_type) type + :(void *) key :(int) part_count; + /** * Unsafe search methods that do not check key part count. */ - (struct tuple *) findUnsafe: (void *) key :(int) part_count; -- (void) initIteratorUnsafe: (struct iterator *) iterator - :(enum iterator_type) type - :(void *) key :(int) part_count; @end struct iterator { struct tuple *(*next)(struct iterator *); - struct tuple *(*next_equal)(struct iterator *); void (*free)(struct iterator *); }; +void +check_key_parts(struct key_def *key_def, int part_count, + bool partial_key_allowed); + #endif /* TARANTOOL_BOX_INDEX_H_INCLUDED */ diff --git a/src/box/index.m b/src/box/index.m index e5fae24f2733a1253a2130b7a8f433083e90c67c..3c01401ca30ce1d32003cf839525a4a61bed8763 100644 --- a/src/box/index.m +++ b/src/box/index.m @@ -46,20 +46,9 @@ 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; -} +STRS(iterator_type, ITERATOR_TYPE); -static struct tuple * -iterator_first_equal(struct iterator *it) -{ - it->next_equal = iterator_next_equal; - return it->next(it); -} - -static void +void check_key_parts(struct key_def *key_def, int part_count, bool partial_key_allowed) { @@ -233,22 +222,10 @@ check_key_parts(struct key_def *key_def, return NULL; } -- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type -{ - (void) iterator; - (void) type; - [self subclassResponsibility: _cmd]; -} - -- (void) initIteratorByKey: (struct iterator *) iterator :(enum iterator_type) type - :(void *) key :(int) part_count -{ - check_key_parts(key_def, part_count, traits->allows_partial_key); - [self initIteratorUnsafe: iterator :type :key :part_count]; -} -- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type - :(void *) key :(int) part_count +- (void) initIterator: (struct iterator *) iterator + :(enum iterator_type) type + :(void *) key :(int) part_count { (void) iterator; (void) type; @@ -281,10 +258,17 @@ struct hash_lstr_iterator { mh_int_t h_pos; }; +void +hash_iterator_free(struct iterator *iterator) +{ + assert(iterator->free == hash_iterator_free); + free(iterator); +} + struct tuple * -hash_iterator_i32_next(struct iterator *ptr) +hash_iterator_i32_ge(struct iterator *ptr) { - assert(ptr->next == hash_iterator_i32_next); + assert(ptr->free == hash_iterator_free); struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { @@ -296,9 +280,9 @@ hash_iterator_i32_next(struct iterator *ptr) } struct tuple * -hash_iterator_i64_next(struct iterator *ptr) +hash_iterator_i64_ge(struct iterator *ptr) { - assert(ptr->next == hash_iterator_i64_next); + assert(ptr->free == hash_iterator_free); struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { @@ -310,9 +294,9 @@ hash_iterator_i64_next(struct iterator *ptr) } struct tuple * -hash_iterator_lstr_next(struct iterator *ptr) +hash_iterator_lstr_ge(struct iterator *ptr) { - assert(ptr->next == hash_iterator_lstr_next); + assert(ptr->free == hash_iterator_free); struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { @@ -323,13 +307,32 @@ hash_iterator_lstr_next(struct iterator *ptr) return NULL; } -void -hash_iterator_free(struct iterator *iterator) +static struct tuple * +hash_iterator_eq_next(struct iterator *it __attribute__((unused))) { - assert(iterator->free == hash_iterator_free); - free(iterator); + return NULL; } +static struct tuple * +hash_iterator_i32_eq(struct iterator *it) +{ + it->next = hash_iterator_eq_next; + return hash_iterator_i32_ge(it); +} + +static struct tuple * +hash_iterator_i64_eq(struct iterator *it) +{ + it->next = hash_iterator_eq_next; + return hash_iterator_i64_ge(it); +} + +static struct tuple * +hash_iterator_lstr_eq(struct iterator *it) +{ + it->next = hash_iterator_eq_next; + return hash_iterator_lstr_ge(it); +} @implementation HashIndex @@ -371,7 +374,7 @@ hash_iterator_free(struct iterator *iterator) struct iterator *it = pk->position; struct tuple *tuple; - [pk initIterator: it :ITER_FORWARD]; + [pk initIterator: it :ITER_ALL :NULL :0]; while ((tuple = it->next(it))) [self replace: NULL :tuple]; @@ -511,39 +514,47 @@ int32_key_to_value(void *key) struct hash_i32_iterator *it = malloc(sizeof(struct hash_i32_iterator)); if (it) { memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i32_next; + it->base.next = hash_iterator_i32_ge; it->base.free = hash_iterator_free; } return (struct iterator *) it; } -- (void) initIterator: (struct iterator *) ptr:(enum iterator_type) type -{ - assert(ptr->free == hash_iterator_free); - struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; - - if (type == ITER_REVERSE) - tnt_raise(IllegalParams, :"hash iterator is forward only"); - - it->base.next_equal = 0; /* Should not be used. */ - it->h_pos = mh_begin(int_hash); - it->hash = int_hash; -} - -- (void) initIteratorUnsafe: (struct iterator *) ptr: (enum iterator_type) type +- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type :(void *) key :(int) part_count { (void) part_count; - if (type == ITER_REVERSE) - tnt_raise(IllegalParams, :"hash iterator is forward only"); - assert(ptr->free == hash_iterator_free); struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; - u32 num = int32_key_to_value(key); - it->base.next_equal = iterator_first_equal; - const struct mh_i32ptr_node_t node = { .key = num }; - it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL); + switch (type) { + case ITER_GE: + if (key != NULL) { + check_key_parts(key_def, part_count, + traits->allows_partial_key); + u32 num = int32_key_to_value(key); + const struct mh_i32ptr_node_t node = { .key = num }; + it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL); + it->base.next = hash_iterator_i32_ge; + break; + } + /* Fall through. */ + case ITER_ALL: + it->h_pos = mh_begin(int_hash); + it->base.next = hash_iterator_i32_ge; + break; + case ITER_EQ: + check_key_parts(key_def, part_count, + traits->allows_partial_key); + u32 num = int32_key_to_value(key); + const struct mh_i32ptr_node_t node = { .key = num }; + it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL); + it->base.next = hash_iterator_i32_eq; + break; + default: + tnt_raise(ClientError, :ER_UNSUPPORTED, + "Hash index", "requested iterator type"); + } it->hash = int_hash; } @end @@ -650,40 +661,48 @@ int64_key_to_value(void *key) struct hash_i64_iterator *it = malloc(sizeof(struct hash_i64_iterator)); if (it) { memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i64_next; + it->base.next = hash_iterator_i64_ge; it->base.free = hash_iterator_free; } return (struct iterator *) it; } -- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type -{ - assert(ptr->free == hash_iterator_free); - struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; - - 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 = int64_hash; -} - -- (void) initIteratorUnsafe: (struct iterator *) ptr: (enum iterator_type) type +- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type :(void *) key :(int) part_count { (void) part_count; - if (type == ITER_REVERSE) - tnt_raise(IllegalParams, :"hash iterator is forward only"); - assert(ptr->free == hash_iterator_free); struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; - u64 num = int64_key_to_value(key); - - it->base.next_equal = iterator_first_equal; - const struct mh_i64ptr_node_t node = { .key = num }; - it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL); + switch (type) { + case ITER_GE: + if (key != NULL) { + check_key_parts(key_def, part_count, + traits->allows_partial_key); + u64 num = int64_key_to_value(key); + const struct mh_i64ptr_node_t node = { .key = num }; + it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL); + it->base.next = hash_iterator_i64_ge; + break; + } + /* Fallthrough. */ + case ITER_ALL: + it->base.next = hash_iterator_i64_ge; + it->h_pos = mh_begin(int64_hash); + break; + case ITER_EQ: + check_key_parts(key_def, part_count, + traits->allows_partial_key); + u64 num = int64_key_to_value(key); + const struct mh_i64ptr_node_t node = { .key = num }; + it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL); + it->base.next = hash_iterator_i64_eq; + break; + default: + tnt_raise(ClientError, :ER_UNSUPPORTED, + "Hash index", "requested iterator type"); + } it->hash = int64_hash; } @end @@ -784,40 +803,48 @@ int64_key_to_value(void *key) struct hash_lstr_iterator *it = malloc(sizeof(struct hash_lstr_iterator)); if (it) { memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_lstr_next; + it->base.next = hash_iterator_lstr_ge; it->base.free = hash_iterator_free; } return (struct iterator *) it; } -- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type -{ - assert(ptr->free == hash_iterator_free); - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; - - 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 = str_hash; -} - -- (void) initIteratorUnsafe: (struct iterator *) ptr +- (void) initIterator: (struct iterator *) ptr :(enum iterator_type) type :(void *) key :(int) part_count { (void) part_count; - if (type == ITER_REVERSE) - tnt_raise(IllegalParams, :"hash iterator is forward only"); assert(ptr->free == hash_iterator_free); struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; - it->base.next_equal = iterator_first_equal; - const struct mh_lstrptr_node_t node = { .key = key }; - it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL); + switch (type) { + case ITER_GE: + if (key != NULL) { + check_key_parts(key_def, part_count, + traits->allows_partial_key); + const struct mh_lstrptr_node_t node = { .key = key }; + it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL); + it->base.next = hash_iterator_lstr_ge; + break; + } + /* Fall through. */ + case ITER_ALL: + it->base.next = hash_iterator_lstr_ge; + it->h_pos = mh_begin(str_hash); + break; + case ITER_EQ: + check_key_parts(key_def, part_count, + traits->allows_partial_key); + const struct mh_lstrptr_node_t node = { .key = key }; + it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL); + it->base.next = hash_iterator_lstr_eq; + break; + default: + tnt_raise(ClientError, :ER_UNSUPPORTED, + "Hash index", "requested iterator type"); + } it->hash = str_hash; } @end diff --git a/src/box/request.m b/src/box/request.m index fbf7a0f482f7c7591c77280e310b6c22c3215c06..b42ac6dfbe23398d9123e5ead51d380d9ce36d9c 100644 --- a/src/box/request.m +++ b/src/box/request.m @@ -755,10 +755,10 @@ execute_select(struct request *request, struct port *port) read_key(data, &key, &key_part_count); struct iterator *it = index->position; - [index initIteratorByKey: it :ITER_FORWARD :key :key_part_count]; + [index initIterator: it :ITER_EQ :key :key_part_count]; struct tuple *tuple; - while ((tuple = it->next_equal(it)) != NULL) { + while ((tuple = it->next(it)) != NULL) { if (tuple->flags & GHOST) continue; diff --git a/src/box/tree.m b/src/box/tree.m index acbce1f51a7e49296c00ab099255727752ae9392..79f99cb824b5dad24bf0ac6c6534a0773ec1947f 100644 --- a/src/box/tree.m +++ b/src/box/tree.m @@ -742,51 +742,56 @@ struct tree_iterator { struct key_data key_data; }; +static void +tree_iterator_free(struct iterator *iterator); + static inline struct tree_iterator * tree_iterator(struct iterator *it) { + assert(it->free == tree_iterator_free); return (struct tree_iterator *) it; } -static struct tuple * -tree_iterator_next(struct iterator *iterator) +static void +tree_iterator_free(struct iterator *iterator) { - assert(iterator->next == tree_iterator_next); struct tree_iterator *it = tree_iterator(iterator); + if (it->iter) + sptree_index_iterator_free(it->iter); + free(it); +} +static struct tuple * +tree_iterator_ge(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); void *node = sptree_index_iterator_next(it->iter); return [it->index unfold: node]; } static struct tuple * -tree_iterator_reverse_next(struct iterator *iterator) +tree_iterator_le(struct iterator *iterator) { - assert(iterator->next == tree_iterator_reverse_next); struct tree_iterator *it = tree_iterator(iterator); - void *node = sptree_index_iterator_reverse_next(it->iter); return [it->index unfold: node]; } static struct tuple * -tree_iterator_next_equal(struct iterator *iterator) +tree_iterator_eq(struct iterator *iterator) { - assert(iterator->next == tree_iterator_next); struct tree_iterator *it = tree_iterator(iterator); void *node = sptree_index_iterator_next(it->iter); - if (node != NULL - && it->index->tree.compare(&it->key_data, node, it->index) == 0) { + if (node && it->index->tree.compare(&it->key_data, node, it->index) == 0) return [it->index unfold: node]; - } return NULL; } static struct tuple * -tree_iterator_reverse_next_equal(struct iterator *iterator) +tree_iterator_req(struct iterator *iterator) { - assert(iterator->next == tree_iterator_reverse_next); struct tree_iterator *it = tree_iterator(iterator); void *node = sptree_index_iterator_reverse_next(it->iter); @@ -798,15 +803,38 @@ tree_iterator_reverse_next_equal(struct iterator *iterator) return NULL; } -static void -tree_iterator_free(struct iterator *iterator) +static struct tuple * +tree_iterator_lt(struct iterator *iterator) { - assert(iterator->free == tree_iterator_free); struct tree_iterator *it = tree_iterator(iterator); - if (it->iter) - sptree_index_iterator_free(it->iter); - free(it); + void *node ; + while ((node = sptree_index_iterator_reverse_next(it->iter)) != NULL) { + if (it->index->tree.compare(&it->key_data, node, + it->index) != 0) { + it->base.next = tree_iterator_le; + return [it->index unfold: node]; + } + } + + return NULL; +} + +static struct tuple * +tree_iterator_gt(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + + void *node; + while ((node = sptree_index_iterator_next(it->iter)) != NULL) { + if (it->index->tree.compare(&it->key_data, node, + it->index) != 0) { + it->base.next = tree_iterator_ge; + return [it->index unfold: node]; + } + } + + return NULL; } /* }}} */ @@ -951,29 +979,58 @@ tree_iterator_free(struct iterator *iterator) return (struct iterator *) it; } -- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type -{ - [self initIteratorUnsafe: iterator :type :NULL :0]; -} - -- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type - :(void *) key :(int) part_count +- (void) initIterator: (struct iterator *) iterator + :(enum iterator_type) type + :(void *) key :(int) part_count { - assert(iterator->free == tree_iterator_free); struct tree_iterator *it = tree_iterator(iterator); + if (part_count != 0) { + check_key_parts(key_def, part_count, + traits->allows_partial_key); + } else { + /* + * If no key is specified, downgrade equality + * iterators to a full range. + */ + type = iterator_type_is_reverse(type) ? ITER_LE : ITER_GE; + key = NULL; + } it->key_data.data = key; it->key_data.part_count = part_count; + fold_with_key_parts(key_def, &it->key_data); - if (type == ITER_FORWARD) { - it->base.next = tree_iterator_next; - it->base.next_equal = tree_iterator_next_equal; - sptree_index_iterator_init_set(&tree, &it->iter, &it->key_data); - } else if (type == ITER_REVERSE) { - it->base.next = tree_iterator_reverse_next; - it->base.next_equal = tree_iterator_reverse_next_equal; - sptree_index_iterator_reverse_init_set(&tree, &it->iter, &it->key_data); + if (iterator_type_is_reverse(type)) + sptree_index_iterator_reverse_init_set(&tree, &it->iter, + &it->key_data); + else + sptree_index_iterator_init_set(&tree, &it->iter, + &it->key_data); + + switch (type) { + case ITER_EQ: + it->base.next = tree_iterator_eq; + break; + case ITER_REQ: + it->base.next = tree_iterator_req; + break; + case ITER_ALL: + case ITER_GE: + it->base.next = tree_iterator_ge; + break; + case ITER_GT: + it->base.next = tree_iterator_gt; + break; + case ITER_LE: + it->base.next = tree_iterator_le; + break; + case ITER_LT: + it->base.next = tree_iterator_lt; + break; + default: + tnt_raise(ClientError, :ER_UNSUPPORTED, + "Tree index", "requested iterator type"); } } @@ -1046,7 +1103,7 @@ tree_iterator_free(struct iterator *iterator) } struct iterator *it = pk->position; - [pk initIterator: it :ITER_FORWARD]; + [pk initIterator: it :ITER_ALL :NULL :0]; struct tuple *tuple; diff --git a/src/memcached.m b/src/memcached.m index 0c77fdecef937bd4234c282e26c6622515d71630..2053cadfb705bc94639d22bb354724a5badfbcb1 100644 --- a/src/memcached.m +++ b/src/memcached.m @@ -304,7 +304,7 @@ flush_all(va_list ap) fiber_sleep(delay - ev_now()); struct tuple *tuple; struct iterator *it = [memcached_index allocIterator]; - [memcached_index initIterator: it :ITER_FORWARD]; + [memcached_index initIterator: it :ITER_ALL :NULL :0]; while ((tuple = it->next(it))) { meta(tuple)->exptime = 1; } @@ -533,7 +533,7 @@ memcached_expire_loop(va_list ap __attribute__((unused))) @try { restart: if (tuple == NULL) - [memcached_index initIterator: memcached_it :ITER_FORWARD]; + [memcached_index initIterator: memcached_it :ITER_ALL :NULL :0]; struct tbuf *keys_to_delete = tbuf_alloc(fiber->gc_pool); diff --git a/test/big/iterator.lua b/test/big/iterator.lua new file mode 100644 index 0000000000000000000000000000000000000000..f9557af5e3d8bea67a22e527fba8e3f830a5994c --- /dev/null +++ b/test/big/iterator.lua @@ -0,0 +1,27 @@ +function iterate(space_no, index_no, f1, f2, ...) + local sorted = (box.space[space_no].index[index_no].type == "TREE"); + local pkeys = {}; + local tkeys = {}; + local values = {}; + for v in box.space[space_no].index[index_no]:iterator(...) do + local pk = v:slice(0, 1); + local tk = '$'; + for f = f1, f2-1, 1 do tk = (tk..(v[f])..'$'); end; + table.insert(values, tk); + if pkeys[pk] ~= nil then + print('Duplicate tuple (primary key): ', pk); + end + if box.space[space_no].index[index_no].unique and tkeys[tk] ~= nil then + print('Duplicate tuple (test key): ', tk); + end; + tkeys[pk] = true; + tkeys[tk] = true; + end; + + if not sorted then + table.sort(values); + print('sorted output'); + end; + + for i,v in ipairs(values) do print(v) end; +end; diff --git a/test/big/iterator.result b/test/big/iterator.result new file mode 100644 index 0000000000000000000000000000000000000000..117481a34bc27fce90e26daa497e4fd584b4481b --- /dev/null +++ b/test/big/iterator.result @@ -0,0 +1,853 @@ +lua dofile('iterator.lua') +--- +... +lua box.insert(20, 'pid_001', 'sid_001', 'tid_998', 'a') +--- + - 'pid_001': {'sid_001', 'tid_998', 'a'} +... +lua box.insert(20, 'pid_002', 'sid_001', 'tid_997', 'a') +--- + - 'pid_002': {'sid_001', 'tid_997', 'a'} +... +lua box.insert(20, 'pid_003', 'sid_002', 'tid_997', 'b') +--- + - 'pid_003': {'sid_002', 'tid_997', 'b'} +... +lua box.insert(20, 'pid_005', 'sid_002', 'tid_996', 'b') +--- + - 'pid_005': {'sid_002', 'tid_996', 'b'} +... +lua box.insert(20, 'pid_007', 'sid_003', 'tid_996', 'a') +--- + - 'pid_007': {'sid_003', 'tid_996', 'a'} +... +lua box.insert(20, 'pid_011', 'sid_004', 'tid_996', 'c') +--- + - 'pid_011': {'sid_004', 'tid_996', 'c'} +... +lua box.insert(20, 'pid_013', 'sid_005', 'tid_996', 'b') +--- + - 'pid_013': {'sid_005', 'tid_996', 'b'} +... +lua box.insert(20, 'pid_017', 'sid_006', 'tid_996', 'a') +--- + - 'pid_017': {'sid_006', 'tid_996', 'a'} +... +lua box.insert(20, 'pid_019', 'sid_005', 'tid_995', 'a') +--- + - 'pid_019': {'sid_005', 'tid_995', 'a'} +... +lua box.insert(20, 'pid_023', 'sid_005', 'tid_994', 'a') +--- + - 'pid_023': {'sid_005', 'tid_994', 'a'} +... + +#-----------------------------------------------------------------------------# +# Iterator: tree single-part unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 0, 0, 1) +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.ALL) +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.EQ) +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.REQ) +--- +$pid_023$ +$pid_019$ +$pid_017$ +$pid_013$ +$pid_011$ +$pid_007$ +$pid_005$ +$pid_003$ +$pid_002$ +$pid_001$ +... +lua iterate(20, 0, 0, 1, box.index.GE) +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.GT) +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.LE) +--- +$pid_023$ +$pid_019$ +$pid_017$ +$pid_013$ +$pid_011$ +$pid_007$ +$pid_005$ +$pid_003$ +$pid_002$ +$pid_001$ +... +lua iterate(20, 0, 0, 1, box.index.LT) +--- +$pid_023$ +$pid_019$ +$pid_017$ +$pid_013$ +$pid_011$ +$pid_007$ +$pid_005$ +$pid_003$ +$pid_002$ +$pid_001$ +... +lua iterate(20, 0, 0, 1, box.index.EQ, 'pid_003') +--- +$pid_003$ +... +lua iterate(20, 0, 0, 1, box.index.REQ, 'pid_003') +--- +$pid_003$ +... +lua iterate(20, 0, 0, 1, box.index.EQ, 'pid_666') +--- +... +lua iterate(20, 0, 0, 1, box.index.REQ, 'pid_666') +--- +... +lua iterate(20, 0, 0, 1, box.index.GE, 'pid_001') +--- +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.GT, 'pid_001') +--- +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 0, 0, 1, box.index.GE, 'pid_999') +--- +... +lua iterate(20, 0, 0, 1, box.index.GT, 'pid_999') +--- +... +lua iterate(20, 0, 0, 1, box.index.LE, 'pid_002') +--- +$pid_002$ +$pid_001$ +... +lua iterate(20, 0, 0, 1, box.index.LT, 'pid_002') +--- +$pid_001$ +... +lua iterate(20, 0, 0, 1, box.index.LE, 'pid_000') +--- +... +lua iterate(20, 0, 0, 1, box.index.LT, 'pid_000') +--- +... + +#-----------------------------------------------------------------------------# +# Iterator: tree single-part non-unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 1, 1, 2, box.index.ALL) +--- +$sid_001$ +$sid_001$ +$sid_002$ +$sid_002$ +$sid_003$ +$sid_004$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.EQ) +--- +$sid_001$ +$sid_001$ +$sid_002$ +$sid_002$ +$sid_003$ +$sid_004$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.REQ) +--- +$sid_006$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_004$ +$sid_003$ +$sid_002$ +$sid_002$ +$sid_001$ +$sid_001$ +... +lua iterate(20, 1, 1, 2, box.index.GE) +--- +$sid_001$ +$sid_001$ +$sid_002$ +$sid_002$ +$sid_003$ +$sid_004$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.GT) +--- +$sid_001$ +$sid_001$ +$sid_002$ +$sid_002$ +$sid_003$ +$sid_004$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.LE) +--- +$sid_006$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_004$ +$sid_003$ +$sid_002$ +$sid_002$ +$sid_001$ +$sid_001$ +... +lua iterate(20, 1, 1, 2, box.index.LT) +--- +$sid_006$ +$sid_005$ +$sid_005$ +$sid_005$ +$sid_004$ +$sid_003$ +$sid_002$ +$sid_002$ +$sid_001$ +$sid_001$ +... +lua iterate(20, 1, 1, 2, box.index.EQ, 'sid_005') +--- +$sid_005$ +$sid_005$ +$sid_005$ +... +lua iterate(20, 1, 1, 2, box.index.REQ, 'sid_005') +--- +$sid_005$ +$sid_005$ +$sid_005$ +... +lua iterate(20, 1, 1, 2, box.index.GE, 'sid_005') +--- +$sid_005$ +$sid_005$ +$sid_005$ +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.GT, 'sid_005') +--- +$sid_006$ +... +lua iterate(20, 1, 1, 2, box.index.GE, 'sid_999') +--- +... +lua iterate(20, 1, 1, 2, box.index.GT, 'sid_999') +--- +... +lua iterate(20, 1, 1, 2, box.index.LE, 'sid_005') +--- +$sid_005$ +$sid_005$ +$sid_005$ +$sid_004$ +$sid_003$ +$sid_002$ +$sid_002$ +$sid_001$ +$sid_001$ +... +lua iterate(20, 1, 1, 2, box.index.LT, 'sid_005') +--- +$sid_004$ +$sid_003$ +$sid_002$ +$sid_002$ +$sid_001$ +$sid_001$ +... +lua iterate(20, 1, 1, 2, box.index.LE, 'sid_000') +--- +... +lua iterate(20, 1, 1, 2, box.index.LT, 'sid_000') +--- +... + +#-----------------------------------------------------------------------------# +# Iterator: tree multi-part unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 2, 1, 3, box.index.ALL) +--- +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.EQ) +--- +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.REQ) +--- +$sid_006$tid_996$ +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.GE) +--- +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GT) +--- +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.LE) +--- +$sid_006$tid_996$ +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LT) +--- +$sid_006$tid_996$ +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005') +--- +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005') +--- +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +... +lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +... +lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005', 'tid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005') +--- +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005') +--- +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005', 'tid_995') +--- +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999') +--- +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005', 'tid_999') +--- +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.GT, 'sid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005') +--- +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005') +--- +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005', 'tid_997') +--- +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005', 'tid_997') +--- +$sid_005$tid_996$ +$sid_005$tid_995$ +$sid_005$tid_994$ +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005', 'tid_000') +--- +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005', 'tid_000') +--- +$sid_004$tid_996$ +$sid_003$tid_996$ +$sid_002$tid_997$ +$sid_002$tid_996$ +$sid_001$tid_998$ +$sid_001$tid_997$ +... +lua iterate(20, 2, 1, 3, box.index.LE, 'sid_000') +--- +... +lua iterate(20, 2, 1, 3, box.index.LT, 'sid_000') +--- +... + +#-----------------------------------------------------------------------------# +# Iterator: tree multi-part non-unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 3, 2, 4, box.index.ALL) +--- +$tid_994$a$ +$tid_995$a$ +$tid_996$a$ +$tid_996$a$ +$tid_996$b$ +$tid_996$b$ +$tid_996$c$ +$tid_997$a$ +$tid_997$b$ +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.EQ) +--- +$tid_994$a$ +$tid_995$a$ +$tid_996$a$ +$tid_996$a$ +$tid_996$b$ +$tid_996$b$ +$tid_996$c$ +$tid_997$a$ +$tid_997$b$ +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.REQ) +--- +$tid_998$a$ +$tid_997$b$ +$tid_997$a$ +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +$tid_995$a$ +$tid_994$a$ +... +lua iterate(20, 3, 2, 4, box.index.GE) +--- +$tid_994$a$ +$tid_995$a$ +$tid_996$a$ +$tid_996$a$ +$tid_996$b$ +$tid_996$b$ +$tid_996$c$ +$tid_997$a$ +$tid_997$b$ +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.GT) +--- +$tid_994$a$ +$tid_995$a$ +$tid_996$a$ +$tid_996$a$ +$tid_996$b$ +$tid_996$b$ +$tid_996$c$ +$tid_997$a$ +$tid_997$b$ +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.LE) +--- +$tid_998$a$ +$tid_997$b$ +$tid_997$a$ +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +$tid_995$a$ +$tid_994$a$ +... +lua iterate(20, 3, 2, 4, box.index.LT) +--- +$tid_998$a$ +$tid_997$b$ +$tid_997$a$ +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +$tid_995$a$ +$tid_994$a$ +... +lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996') +--- +$tid_996$a$ +$tid_996$a$ +$tid_996$b$ +$tid_996$b$ +$tid_996$c$ +... +lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996', 'a') +--- +$tid_996$a$ +$tid_996$a$ +... +lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996', 'z') +--- +... +lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996') +--- +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +... +lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996', 'a') +--- +$tid_996$a$ +$tid_996$a$ +... +lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996', '0') +--- +... +lua iterate(20, 3, 2, 4, box.index.GE, 'tid_997') +--- +$tid_997$a$ +$tid_997$b$ +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.GT, 'tid_997') +--- +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.GE, 'tid_998') +--- +$tid_998$a$ +... +lua iterate(20, 3, 2, 4, box.index.GT, 'tid_998') +--- +... +lua iterate(20, 3, 2, 4, box.index.LE, 'tid_997') +--- +$tid_997$b$ +$tid_997$a$ +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +$tid_995$a$ +$tid_994$a$ +... +lua iterate(20, 3, 2, 4, box.index.LT, 'tid_997') +--- +$tid_996$c$ +$tid_996$b$ +$tid_996$b$ +$tid_996$a$ +$tid_996$a$ +$tid_995$a$ +$tid_994$a$ +... +lua iterate(20, 3, 2, 4, box.index.LE, 'tid_000') +--- +... +lua iterate(20, 3, 2, 4, box.index.LT, 'tid_000') +--- +... +lua iterate(20, 3, 2, 4, box.index.LT, 'tid_996', 'to', 'many', 'keys') +--- +error: 'iterator.lua:6: Key part count 4 is greater than index part count 2' +... + +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part non-unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 4, 0, 1) +--- +sorted output +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 4, 0, 1, box.index.ALL) +--- +sorted output +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 4, 0, 1, box.index.EQ) +--- +error: 'Partial key in an exact match (key field count: 0, expected: 1)' +... +lua iterate(20, 4, 0, 1, box.index.GE) +--- +sorted output +$pid_001$ +$pid_002$ +$pid_003$ +$pid_005$ +$pid_007$ +$pid_011$ +$pid_013$ +$pid_017$ +$pid_019$ +$pid_023$ +... +lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_003') +--- +sorted output +$pid_003$ +... +lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_666') +--- +sorted output +... +lua iterate(20, 4, 0, 1, box.index.GE, 'pid_001') +--- +sorted output +$pid_001$ +$pid_002$ +$pid_005$ +$pid_017$ +$pid_023$ +... +lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999') +--- +sorted output +... + +#-----------------------------------------------------------------------------# +# Iterator: various +#-----------------------------------------------------------------------------# + +lua box.space[20].index[0]:iterator(-666) +--- +error: 'unknown iterator type: -666' +... +lua box.space[20]:truncate() +--- +... diff --git a/test/big/iterator.test b/test/big/iterator.test new file mode 100644 index 0000000000000000000000000000000000000000..2182f7c2b2a03ac0e6436db35ed609cf29ed9c49 --- /dev/null +++ b/test/big/iterator.test @@ -0,0 +1,159 @@ +# encoding: tarantool +# +import os +import shutil + +iterator_lua_path = os.path.join(vardir, "iterator.lua") +shutil.copy("big/iterator.lua", iterator_lua_path) + +exec admin "lua dofile('iterator.lua')" +shutil.rmtree(iterator_lua_path, True) + +exec admin "lua box.insert(20, 'pid_001', 'sid_001', 'tid_998', 'a')" +exec admin "lua box.insert(20, 'pid_002', 'sid_001', 'tid_997', 'a')" +exec admin "lua box.insert(20, 'pid_003', 'sid_002', 'tid_997', 'b')" +exec admin "lua box.insert(20, 'pid_005', 'sid_002', 'tid_996', 'b')" +exec admin "lua box.insert(20, 'pid_007', 'sid_003', 'tid_996', 'a')" +exec admin "lua box.insert(20, 'pid_011', 'sid_004', 'tid_996', 'c')" +exec admin "lua box.insert(20, 'pid_013', 'sid_005', 'tid_996', 'b')" +exec admin "lua box.insert(20, 'pid_017', 'sid_006', 'tid_996', 'a')" +exec admin "lua box.insert(20, 'pid_019', 'sid_005', 'tid_995', 'a')" +exec admin "lua box.insert(20, 'pid_023', 'sid_005', 'tid_994', 'a')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: tree single-part unique +#-----------------------------------------------------------------------------# +""" +exec admin "lua iterate(20, 0, 0, 1)" +exec admin "lua iterate(20, 0, 0, 1, box.index.ALL)" +exec admin "lua iterate(20, 0, 0, 1, box.index.EQ)" +exec admin "lua iterate(20, 0, 0, 1, box.index.REQ)" +exec admin "lua iterate(20, 0, 0, 1, box.index.GE)" +exec admin "lua iterate(20, 0, 0, 1, box.index.GT)" +exec admin "lua iterate(20, 0, 0, 1, box.index.LE)" +exec admin "lua iterate(20, 0, 0, 1, box.index.LT)" +exec admin "lua iterate(20, 0, 0, 1, box.index.EQ, 'pid_003')" +exec admin "lua iterate(20, 0, 0, 1, box.index.REQ, 'pid_003')" +exec admin "lua iterate(20, 0, 0, 1, box.index.EQ, 'pid_666')" +exec admin "lua iterate(20, 0, 0, 1, box.index.REQ, 'pid_666')" +exec admin "lua iterate(20, 0, 0, 1, box.index.GE, 'pid_001')" +exec admin "lua iterate(20, 0, 0, 1, box.index.GT, 'pid_001')" +exec admin "lua iterate(20, 0, 0, 1, box.index.GE, 'pid_999')" +exec admin "lua iterate(20, 0, 0, 1, box.index.GT, 'pid_999')" +exec admin "lua iterate(20, 0, 0, 1, box.index.LE, 'pid_002')" +exec admin "lua iterate(20, 0, 0, 1, box.index.LT, 'pid_002')" +exec admin "lua iterate(20, 0, 0, 1, box.index.LE, 'pid_000')" +exec admin "lua iterate(20, 0, 0, 1, box.index.LT, 'pid_000')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: tree single-part non-unique +#-----------------------------------------------------------------------------# +""" +exec admin "lua iterate(20, 1, 1, 2, box.index.ALL)" +exec admin "lua iterate(20, 1, 1, 2, box.index.EQ)" +exec admin "lua iterate(20, 1, 1, 2, box.index.REQ)" +exec admin "lua iterate(20, 1, 1, 2, box.index.GE)" +exec admin "lua iterate(20, 1, 1, 2, box.index.GT)" +exec admin "lua iterate(20, 1, 1, 2, box.index.LE)" +exec admin "lua iterate(20, 1, 1, 2, box.index.LT)" +exec admin "lua iterate(20, 1, 1, 2, box.index.EQ, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.REQ, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.GE, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.GT, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.GE, 'sid_999')" +exec admin "lua iterate(20, 1, 1, 2, box.index.GT, 'sid_999')" +exec admin "lua iterate(20, 1, 1, 2, box.index.LE, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.LT, 'sid_005')" +exec admin "lua iterate(20, 1, 1, 2, box.index.LE, 'sid_000')" +exec admin "lua iterate(20, 1, 1, 2, box.index.LT, 'sid_000')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: tree multi-part unique +#-----------------------------------------------------------------------------# +""" +exec admin "lua iterate(20, 2, 1, 3, box.index.ALL)" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ)" +exec admin "lua iterate(20, 2, 1, 3, box.index.REQ)" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE)" +exec admin "lua iterate(20, 2, 1, 3, box.index.GT)" +exec admin "lua iterate(20, 2, 1, 3, box.index.LE)" +exec admin "lua iterate(20, 2, 1, 3, box.index.LT)" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.REQ, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GT, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GT, 'sid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005', 'tid_997')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005', 'tid_997')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LE, 'sid_005', 'tid_000')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LT, 'sid_005', 'tid_000')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LE, 'sid_000')" +exec admin "lua iterate(20, 2, 1, 3, box.index.LT, 'sid_000')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: tree multi-part non-unique +#-----------------------------------------------------------------------------# +""" + +exec admin "lua iterate(20, 3, 2, 4, box.index.ALL)" +exec admin "lua iterate(20, 3, 2, 4, box.index.EQ)" +exec admin "lua iterate(20, 3, 2, 4, box.index.REQ)" +exec admin "lua iterate(20, 3, 2, 4, box.index.GE)" +exec admin "lua iterate(20, 3, 2, 4, box.index.GT)" +exec admin "lua iterate(20, 3, 2, 4, box.index.LE)" +exec admin "lua iterate(20, 3, 2, 4, box.index.LT)" +exec admin "lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996')" +exec admin "lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996', 'a')" +exec admin "lua iterate(20, 3, 2, 4, box.index.EQ, 'tid_996', 'z')" +exec admin "lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996')" +exec admin "lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996', 'a')" +exec admin "lua iterate(20, 3, 2, 4, box.index.REQ, 'tid_996', '0')" +exec admin "lua iterate(20, 3, 2, 4, box.index.GE, 'tid_997')" +exec admin "lua iterate(20, 3, 2, 4, box.index.GT, 'tid_997')" +exec admin "lua iterate(20, 3, 2, 4, box.index.GE, 'tid_998')" +exec admin "lua iterate(20, 3, 2, 4, box.index.GT, 'tid_998')" +exec admin "lua iterate(20, 3, 2, 4, box.index.LE, 'tid_997')" +exec admin "lua iterate(20, 3, 2, 4, box.index.LT, 'tid_997')" +exec admin "lua iterate(20, 3, 2, 4, box.index.LE, 'tid_000')" +exec admin "lua iterate(20, 3, 2, 4, box.index.LT, 'tid_000')" +exec admin "lua iterate(20, 3, 2, 4, box.index.LT, 'tid_996', 'to', 'many', 'keys')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part non-unique +#-----------------------------------------------------------------------------# +""" + +exec admin "lua iterate(20, 4, 0, 1)" +exec admin "lua iterate(20, 4, 0, 1, box.index.ALL)" +exec admin "lua iterate(20, 4, 0, 1, box.index.EQ)" +exec admin "lua iterate(20, 4, 0, 1, box.index.GE)" +exec admin "lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_003')" +exec admin "lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_666')" +exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_001')" +exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999')" + +print """ +#-----------------------------------------------------------------------------# +# Iterator: various +#-----------------------------------------------------------------------------# +""" + +exec admin "lua box.space[20].index[0]:iterator(-666)" + +exec admin "lua box.space[20]:truncate()" diff --git a/test/big/lua.result b/test/big/lua.result index 6698c0b0fdd27fa686b9f4ea6f2367fddede0481..4f53097c83102f900109ec900e37bf3166acbe31 100644 --- a/test/big/lua.result +++ b/test/big/lua.result @@ -222,7 +222,7 @@ lua box.insert(16, 'pid_6', 'sid_2', 'tid_994') --- - 'pid_6': {'sid_2', 'tid_994'} ... -lua for k, v in box.space[16].index[1].idx.next, box.space[16].index[1].idx, 'sid_1' do print(v) end +lua for k, v in box.space[16].index[1].next, box.space[16].index[1], 'sid_1' do print(v) end --- 'pid_3': {'sid_1', 'tid_997'} 'pid_2': {'sid_1', 'tid_998'} @@ -231,7 +231,7 @@ lua for k, v in box.space[16].index[1].idx.next, box.space[16].index[1].idx, 'si 'pid_5': {'sid_2', 'tid_995'} 'pid_4': {'sid_2', 'tid_996'} ... -lua for k, v in box.space[16].index[1].idx.prev, box.space[16].index[1].idx, 'sid_2' do print(v) end +lua for k, v in box.space[16].index[1].prev, box.space[16].index[1], 'sid_2' do print(v) end --- 'pid_4': {'sid_2', 'tid_996'} 'pid_5': {'sid_2', 'tid_995'} @@ -240,25 +240,25 @@ lua for k, v in box.space[16].index[1].idx.prev, box.space[16].index[1].idx, 'si 'pid_2': {'sid_1', 'tid_998'} 'pid_3': {'sid_1', 'tid_997'} ... -lua for k, v in box.space[16].index[1].idx.next_equal, box.space[16].index[1].idx, 'sid_1' do print(v) end +lua for k, v in box.space[16].index[1].next_equal, box.space[16].index[1], 'sid_1' do print(v) end --- 'pid_3': {'sid_1', 'tid_997'} 'pid_2': {'sid_1', 'tid_998'} 'pid_1': {'sid_1', 'tid_999'} ... -lua for k, v in box.space[16].index[1].idx.prev_equal, box.space[16].index[1].idx, 'sid_1' do print(v) end +lua for k, v in box.space[16].index[1].prev_equal, box.space[16].index[1], 'sid_1' do print(v) end --- 'pid_1': {'sid_1', 'tid_999'} 'pid_2': {'sid_1', 'tid_998'} 'pid_3': {'sid_1', 'tid_997'} ... -lua for k, v in box.space[16].index[1].idx.next_equal, box.space[16].index[1].idx, 'sid_2' do print(v) end +lua for k, v in box.space[16].index[1].next_equal, box.space[16].index[1], 'sid_2' do print(v) end --- 'pid_6': {'sid_2', 'tid_994'} 'pid_5': {'sid_2', 'tid_995'} 'pid_4': {'sid_2', 'tid_996'} ... -lua for k, v in box.space[16].index[1].idx.prev_equal, box.space[16].index[1].idx, 'sid_2' do print(v) end +lua for k, v in box.space[16].index[1].prev_equal, box.space[16].index[1], 'sid_2' do print(v) end --- 'pid_4': {'sid_2', 'tid_996'} 'pid_5': {'sid_2', 'tid_995'} @@ -291,31 +291,31 @@ lua box.insert(17, 6, 3, 2) --- - 6: {3, 2} ... -lua box.space[17].index[1].idx:count(1) +lua box.space[17].index[1]:count(1) --- - 1 ... -lua box.space[17].index[1].idx:count(2) +lua box.space[17].index[1]:count(2) --- - 2 ... -lua box.space[17].index[1].idx:count(2, 1) +lua box.space[17].index[1]:count(2, 1) --- - 1 ... -lua box.space[17].index[1].idx:count(2, 2) +lua box.space[17].index[1]:count(2, 2) --- - 0 ... -lua box.space[17].index[1].idx:count(3) +lua box.space[17].index[1]:count(3) --- - 3 ... -lua box.space[17].index[1].idx:count(3, 3) +lua box.space[17].index[1]:count(3, 3) --- - 0 ... -lua box.space[17].index[1].idx:count() +lua box.space[17].index[1]:count() --- error: 'index.count(): one or more arguments expected' ... diff --git a/test/big/lua.test b/test/big/lua.test index a3f0bdf005338f714637f88f69cc126b0767a809..dcbe51601e084594127696dbd47911f28e2ec7cb 100644 --- a/test/big/lua.test +++ b/test/big/lua.test @@ -89,12 +89,12 @@ for sid in {1, 2}: pid += 1 tid -= 1 -exec admin "lua for k, v in box.space[16].index[1].idx.next, box.space[16].index[1].idx, 'sid_1' do print(v) end" -exec admin "lua for k, v in box.space[16].index[1].idx.prev, box.space[16].index[1].idx, 'sid_2' do print(v) end" -exec admin "lua for k, v in box.space[16].index[1].idx.next_equal, box.space[16].index[1].idx, 'sid_1' do print(v) end" -exec admin "lua for k, v in box.space[16].index[1].idx.prev_equal, box.space[16].index[1].idx, 'sid_1' do print(v) end" -exec admin "lua for k, v in box.space[16].index[1].idx.next_equal, box.space[16].index[1].idx, 'sid_2' do print(v) end" -exec admin "lua for k, v in box.space[16].index[1].idx.prev_equal, box.space[16].index[1].idx, 'sid_2' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].next, box.space[16].index[1], 'sid_1' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].prev, box.space[16].index[1], 'sid_2' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].next_equal, box.space[16].index[1], 'sid_1' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].prev_equal, box.space[16].index[1], 'sid_1' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].next_equal, box.space[16].index[1], 'sid_2' do print(v) end" +exec admin "lua for k, v in box.space[16].index[1].prev_equal, box.space[16].index[1], 'sid_2' do print(v) end" exec admin "lua box.space[16]:truncate()" # @@ -106,13 +106,13 @@ exec admin "lua box.insert(17, 3, 2, 1)" exec admin "lua box.insert(17, 4, 3, 0)" exec admin "lua box.insert(17, 5, 3, 1)" exec admin "lua box.insert(17, 6, 3, 2)" -exec admin "lua box.space[17].index[1].idx:count(1)" -exec admin "lua box.space[17].index[1].idx:count(2)" -exec admin "lua box.space[17].index[1].idx:count(2, 1)" -exec admin "lua box.space[17].index[1].idx:count(2, 2)" -exec admin "lua box.space[17].index[1].idx:count(3)" -exec admin "lua box.space[17].index[1].idx:count(3, 3)" -exec admin "lua box.space[17].index[1].idx:count()" +exec admin "lua box.space[17].index[1]:count(1)" +exec admin "lua box.space[17].index[1]:count(2)" +exec admin "lua box.space[17].index[1]:count(2, 1)" +exec admin "lua box.space[17].index[1]:count(2, 2)" +exec admin "lua box.space[17].index[1]:count(3)" +exec admin "lua box.space[17].index[1]:count(3, 3)" +exec admin "lua box.space[17].index[1]:count()" exec admin "lua box.space[17]:truncate()" # diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 6bea4bc90024896075b87899402ed716a53277fa..5ecbbbd01a3a48e39bf7df5f36499941e3d1897a 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -200,7 +200,7 @@ space[15].index[0].unique = true space[15].index[0].key_field[0].fieldno = 0 space[15].index[0].key_field[0].type = "STR" -# Tests for box.index iterators +# Tests for box.index iterators (old) space[16].enabled = true space[16].index[0].type = "TREE" space[16].index[0].unique = 1 @@ -243,3 +243,42 @@ 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" + +# +# Tests for box.index iterators (new) +# + +# Tree single-part unique +space[20].enabled = true +space[20].index[0].type = "TREE" +space[20].index[0].unique = 1 +space[20].index[0].key_field[0].fieldno = 0 +space[20].index[0].key_field[0].type = "STR" + +# Tree single-part non-unique +space[20].index[1].type = "TREE" +space[20].index[1].unique = 0 +space[20].index[1].key_field[0].fieldno = 1 +space[20].index[1].key_field[0].type = "STR" + +# Tree multi-part unique +space[20].index[2].type = "TREE" +space[20].index[2].unique = 1 +space[20].index[2].key_field[0].fieldno = 1 +space[20].index[2].key_field[0].type = "STR" +space[20].index[2].key_field[1].fieldno = 2 +space[20].index[2].key_field[1].type = "STR" + +# Tree multi-part non-unique +space[20].index[3].type = "TREE" +space[20].index[3].unique = 0 +space[20].index[3].key_field[0].fieldno = 2 +space[20].index[3].key_field[0].type = "STR" +space[20].index[3].key_field[1].fieldno = 3 +space[20].index[3].key_field[1].type = "STR" + +# Hash single-part unique +space[20].index[4].type = "HASH" +space[20].index[4].unique = 1 +space[20].index[4].key_field[0].fieldno = 0 +space[20].index[4].key_field[0].type = "STR"