From 255bd42563eb27f29090a9022844b961419ce11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B9=20=D0=91=D0=B5?= =?UTF-8?q?=D0=BB=D0=BE=D0=BD=D0=BE=D0=B3=D0=BE=D0=B2?= Date: Wed, 11 Nov 2026 00:10:55 +0300 Subject: [PATCH] feat(core): add NP/PP box iterator support - Added NP/PP box iterator support. More information: https://github.com/tarantool/tarantool/commit/96df090fe71a7152ddee105eca454efc0c096c28 --- CHANGELOG.md | 2 + .../tarantool/core/protocol/BoxIterator.java | 132 +++++++++++++++++- .../core/integration/IProtoClientTest.java | 68 +++++++++ 3 files changed, 201 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 640af90d..54f174e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Core +- Added NP/PP box index + iterator [support](https://github.com/tarantool/tarantool/commit/96df090fe71a7152ddee105eca454efc0c096c28) - Added [LINEARIZABLE](https://github.com/tarantool/tarantool/blob/e643f88a5e0c6d9ef2789b2033088fd04e7d39ae/src/box/txn.h#L228) isolation level. diff --git a/tarantool-core/src/main/java/io/tarantool/core/protocol/BoxIterator.java b/tarantool-core/src/main/java/io/tarantool/core/protocol/BoxIterator.java index b537de60..2e7a971c 100644 --- a/tarantool-core/src/main/java/io/tarantool/core/protocol/BoxIterator.java +++ b/tarantool-core/src/main/java/io/tarantool/core/protocol/BoxIterator.java @@ -5,19 +5,149 @@ package io.tarantool.core.protocol; +/** Tarantool iterator types for data selection. Used in {@code box.select} requests. */ public enum BoxIterator { + + /** + * Matches keys equal to the provided search key (ASC order). + * + *

Binary value: {@code 0} + * + * @since Supported in all versions. + */ EQ(0), + + /** + * Matches keys equal to the provided search key (DESC order). + * + *

Binary value: {@code 1} + * + * @since Supported in all versions. + */ REQ(1), + + /** + * Iterates over all tuples in the index. The search key is ignored. + * + *

Binary value: {@code 2} + * + * @since Supported in all versions. + */ ALL(2), + + /** + * Matches keys strictly less than the provided search key ({@code key < x}). + * + *

Binary value: {@code 3} + * + * @since Supported in all versions. + */ LT(3), + + /** + * Matches keys less than or equal to the provided search key ({@code key <= x}). + * + *

Binary value: {@code 4} + * + * @since Supported in all versions. + */ LE(4), + + /** + * Matches keys greater than or equal to the provided search key ({@code key >= x}). + * + *

Binary value: {@code 5} + * + * @since Supported in all versions. + */ GE(5), + + /** + * Matches keys strictly greater than the provided search key ({@code key > x}). + * + *

Binary value: {@code 6} + * + * @since Supported in all versions. + */ GT(6), + + /** + * Matches keys where all bits from the search key are set (Bitset index only). + * + *

Binary value: {@code 7} + * + * @since Supported in all versions. + */ BITS_ALL_SET(7), + + /** + * Matches keys where at least one bit from the search key is set (Bitset index only). + * + *

Binary value: {@code 8} + * + * @since Supported in all versions. + */ BITS_ANY_SET(8), + + /** + * Matches keys where all bits from the search key are not set (Bitset index only). + * + *

Binary value: {@code 9} + * + * @since Supported in all versions. + */ BITS_ALL_NOT_SET(9), + + /** + * Matches keys that overlap with the provided search key (RTREE index only). + * + *

Binary value: {@code 10} + * + * @since Supported in all versions. + */ OVERLAPS(10), - NEIGHBOR(11); + + /** + * Iterates over tuples as they move away from the specified point {@code x} (RTREE neighbor + * search). + * + *

Binary value: {@code 11} + * + * @since Supported in all versions. + */ + NEIGHBOR(11), + + /** + * Next Prefix iterator. + * + *

Binary value: {@code 12} + * + *

Performs a prefix-based search on the last part of the key (if it is a string, ASC order). + * It skips all keys sharing the same prefix as the search key and starts the sequential scan from + * the first key whose prefix is strictly greater than the search key. + * + *

Degrades to {@link #GT} if the last part of the key is not a string. Works only in {@code + * memtx} tree indexes. + * + * @since Tarantool 3.2.0 + */ + NP(12), + + /** + * Previous Prefix iterator. + * + *

Binary value: {@code 13} + * + *

Performs a prefix-based search on the last part of the key (if it is a string, DESC order). + * It looks for the last key whose prefix is strictly less than the search key, and then proceeds + * with a sequential reverse-order scan. + * + *

Degrades to {@link #LT} if the last part of the key is not a string. Works only in {@code + * memtx} tree indexes. + * + * @since Tarantool 3.2.0 + */ + PP(13); private final int code; diff --git a/tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientTest.java b/tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientTest.java index f6ca2476..ea200d0a 100644 --- a/tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientTest.java +++ b/tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientTest.java @@ -891,6 +891,74 @@ public void testSelect( assertEquals(expected, decodeTuple(client, data)); } + public static Stream dataForTestIterator3() { + final String prepared = + "box.space.space_a:insert({'a', 'a'});" + + "box.space.space_a:insert({'aa', 'aa'});" + + "box.space.space_a:insert({'ab', 'ab'});" + + "box.space.space_a:insert({'b', 'b'});" + + "box.space.space_a:insert({'ba', 'ba'});" + + "box.space.space_a:insert({'bb', 'bb'});" + + "box.space.space_a:insert({'c', 'c'});" + + "box.space.space_a:insert({'ca', 'ca'});" + + "box.space.space_a:insert({'cb', 'cb'});"; + + return Stream.of( + Arguments.of( + BoxIterator.NP.toString(), + prepared, + ValueFactory.newArray(ValueFactory.newString("b")), + ValueFactory.newArray( + ValueFactory.newArray(ValueFactory.newString("c"), ValueFactory.newString("c")), + ValueFactory.newArray(ValueFactory.newString("ca"), ValueFactory.newString("ca")), + ValueFactory.newArray(ValueFactory.newString("cb"), ValueFactory.newString("cb"))), + spaceAId, + BoxIterator.NP), + Arguments.of( + BoxIterator.PP.toString(), + prepared, + ValueFactory.newArray(ValueFactory.newString("b")), + ValueFactory.newArray( + ValueFactory.newArray(ValueFactory.newString("ab"), ValueFactory.newString("ab")), + ValueFactory.newArray(ValueFactory.newString("aa"), ValueFactory.newString("aa")), + ValueFactory.newArray(ValueFactory.newString("a"), ValueFactory.newString("a"))), + spaceAId, + BoxIterator.PP)); + } + + @EnabledIfEnvironmentVariable(named = "TARANTOOL_VERSION", matches = "3.*") + @ParameterizedTest(name = "{0}") + @MethodSource("dataForTestIterator3") + void testIterator3( + String label, + String toPrepare, + ArrayValue key, + ArrayValue expected, + Integer space, + BoxIterator iterator) + throws Exception { + testIteratorCommon(toPrepare, key, expected, space, 0, Integer.MAX_VALUE, 0, iterator); + } + + void testIteratorCommon( + String toPrepare, + ArrayValue key, + ArrayValue expected, + Integer space, + Integer index, + Integer limit, + Integer offset, + BoxIterator iterator) + throws Exception { + TarantoolContainerClientHelper.executeCommand(tt, toPrepare); + IProtoClient client = createClientAndConnect(address, true); + client.authorize(API_USER, CREDS.get(API_USER)).join(); + IProtoMessage message = client.select(space, index, key, limit, offset, iterator).get(); + checkMessageHeader(message, IPROTO_OK, 4); + ArrayValue data = message.getBodyArrayValue(IPROTO_DATA); + Assertions.assertEquals(expected, decodeTuple(client, data)); + } + @ParameterizedTest @MethodSource("dataForTestSelect") public void testRawSelect(