From 05f7a7862df75a4e508d42da9f7a07f52eb94f5c Mon Sep 17 00:00:00 2001 From: korlov42 Date: Fri, 3 Apr 2026 16:51:49 +0300 Subject: [PATCH 1/3] IGNITE-28452 Sql. Cleanup ScannableTable interface --- .../exec/ExecutableTableRegistryImpl.java | 11 +++- .../engine/exec/LogicalRelImplementor.java | 1 - .../sql/engine/exec/ScannableTable.java | 5 -- .../sql/engine/exec/ScannableTableImpl.java | 50 ++++++++++++++++--- .../sql/engine/exec/rel/IndexScanNode.java | 20 +------- .../sql/engine/exec/DummyScannableTable.java | 3 -- .../{rel => }/ScannableTableSelfTest.java | 49 +++++++----------- .../exec/rel/IndexScanNodeExecutionTest.java | 21 ++++---- .../exec/rel/TableScanNodeExecutionTest.java | 3 +- .../sql/engine/framework/TestBuilders.java | 5 +- .../sql/engine/framework/TestClusterTest.java | 4 +- 11 files changed, 85 insertions(+), 87 deletions(-) rename modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/{rel => }/ScannableTableSelfTest.java (95%) diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutableTableRegistryImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutableTableRegistryImpl.java index b88167869088..67b22af346f0 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutableTableRegistryImpl.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutableTableRegistryImpl.java @@ -17,6 +17,8 @@ package org.apache.ignite.internal.sql.engine.exec; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.Objects; import java.util.function.Supplier; import org.apache.ignite.internal.hlc.ClockService; @@ -24,6 +26,8 @@ import org.apache.ignite.internal.schema.SchemaDescriptor; import org.apache.ignite.internal.schema.SchemaManager; import org.apache.ignite.internal.schema.SchemaRegistry; +import org.apache.ignite.internal.sql.engine.exec.ScannableTableImpl.IndexMeta; +import org.apache.ignite.internal.sql.engine.schema.IgniteIndex; import org.apache.ignite.internal.sql.engine.schema.IgniteTable; import org.apache.ignite.internal.sql.engine.schema.PartitionCalculator; import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManager; @@ -92,8 +96,13 @@ private ExecutableTable loadTable(IgniteTable sqlTable) { tableDescriptor, schemaRegistry, schemaDescriptor ); + Int2ObjectMap indexMeta = new Int2ObjectOpenHashMap<>(); + for (IgniteIndex index : sqlTable.indexes().values()) { + indexMeta.put(index.id(), new ScannableTableImpl.IndexMeta(index.collation().getFieldCollations().size())); + } + InternalTable internalTable = table.internalTable(); - ScannableTable scannableTable = new ScannableTableImpl(internalTable, converterFactory); + ScannableTable scannableTable = new ScannableTableImpl(internalTable, indexMeta, converterFactory); TableRowConverter rowConverter = converterFactory.create(null); UpdatableTableImpl updatableTable = new UpdatableTableImpl( diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java index 4a6305b0cce0..75a7a3eaa54a 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/LogicalRelImplementor.java @@ -522,7 +522,6 @@ public Node visit(IgniteIndexScan rel) { rowFactory, idx, scannableTable, - tbl.descriptor(), partitionProvider, comp, ranges, diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java index 4f385ebf49d7..b753554c0f4f 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java @@ -17,7 +17,6 @@ package org.apache.ignite.internal.sql.engine.exec; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow.Publisher; import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory; @@ -55,7 +54,6 @@ Publisher scan( * @param partWithConsistencyToken Partition. * @param rowFactory Row factory. * @param indexId Index id. - * @param columns Index columns. * @param cond Index condition. * @param requiredColumns Required columns. * @return A publisher that produces rows. @@ -65,7 +63,6 @@ Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns ); @@ -78,7 +75,6 @@ Publisher indexRangeScan( * @param partWithConsistencyToken Partition. * @param rowFactory Row factory. * @param indexId Index id. - * @param columns Index columns. * @param key A key to lookup. * @param requiredColumns Required columns. * @return A publisher that produces rows. @@ -88,7 +84,6 @@ Publisher indexLookup( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, RowT key, int @Nullable [] requiredColumns ); diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java index 44cb6a129468..9179fa5d64e6 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java @@ -23,7 +23,7 @@ import static org.apache.ignite.internal.storage.index.SortedIndexStorage.LESS; import static org.apache.ignite.internal.storage.index.SortedIndexStorage.LESS_OR_EQUAL; -import java.util.List; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow.Publisher; import org.apache.ignite.internal.binarytuple.BinaryTuple; @@ -39,6 +39,7 @@ import org.apache.ignite.internal.table.OperationContext; import org.apache.ignite.internal.table.TxContext; import org.apache.ignite.internal.tx.InternalTransaction; +import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.subscription.TransformingPublisher; import org.jetbrains.annotations.Nullable; @@ -49,11 +50,18 @@ public class ScannableTableImpl implements ScannableTable { private final InternalTable internalTable; + private final Int2ObjectMap indexMeta; + private final TableRowConverterFactory converterFactory; /** Constructor. */ - public ScannableTableImpl(InternalTable internalTable, TableRowConverterFactory converterFactory) { + public ScannableTableImpl( + InternalTable internalTable, + Int2ObjectMap indexMeta, + TableRowConverterFactory converterFactory + ) { this.internalTable = internalTable; + this.indexMeta = indexMeta; this.converterFactory = converterFactory; } @@ -83,7 +91,6 @@ public Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns ) { @@ -94,6 +101,14 @@ public Publisher indexRangeScan( BinaryTuplePrefix lower; BinaryTuplePrefix upper; + IndexMeta meta = indexMeta.get(indexId); + + if (meta == null) { + throw new IllegalStateException(format("Index metadata not found [tableId={}, tableName={}, indexId={}].", + internalTable.tableId(), internalTable.name(), indexId)); + } + + int indexKeySize = meta.indexKeySize; int flags = 0; if (cond == null) { @@ -101,8 +116,8 @@ public Publisher indexRangeScan( lower = null; upper = null; } else { - lower = toBinaryTuplePrefix(columns.size(), handler, cond.lower()); - upper = toBinaryTuplePrefix(columns.size(), handler, cond.upper()); + lower = toBinaryTuplePrefix(indexKeySize, handler, cond.lower()); + upper = toBinaryTuplePrefix(indexKeySize, handler, cond.upper()); flags |= (cond.lowerInclude()) ? GREATER_OR_EQUAL : GREATER; flags |= (cond.upperInclude()) ? LESS_OR_EQUAL : LESS; @@ -130,7 +145,6 @@ public Publisher indexLookup( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, RowT key, int @Nullable [] requiredColumns ) { @@ -140,8 +154,19 @@ public Publisher indexLookup( BinaryTuple keyTuple = handler.toBinaryTuple(key); - assert keyTuple.elementCount() == columns.size() - : format("Key should contain exactly {} fields, but was {}", columns.size(), handler.toString(key)); + if (IgniteUtils.assertionsEnabled()) { + IndexMeta meta = indexMeta.get(indexId); + + if (meta == null) { + throw new IllegalStateException(format("Index metadata not found [tableId={}, tableName={}, indexId={}].", + internalTable.tableId(), internalTable.name(), indexId)); + } + + int indexKeySize = meta.indexKeySize; + + assert keyTuple.elementCount() == indexKeySize + : format("Key should contain exactly {} fields, but was {}", indexKeySize, handler.toString(key)); + } int partId = partWithConsistencyToken.partId(); @@ -224,4 +249,13 @@ private static TxContext transactionalContextFrom(TxAttributes txAttributes, lon return TxContext.readWrite(txAttributes.id(), txAttributes.coordinatorId(), commitPartition, enlistmentConsistencyToken); } + + /** Metadata required to process scan over particular index. */ + public static class IndexMeta { + private final int indexKeySize; + + IndexMeta(int indexKeySize) { + this.indexKeySize = indexKeySize; + } + } } diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java index cd423dcab759..f0364045923e 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java @@ -24,8 +24,6 @@ import java.util.concurrent.Flow.Publisher; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; -import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.util.ImmutableIntList; import org.apache.ignite.internal.lang.IgniteStringBuilder; import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory; @@ -35,9 +33,7 @@ import org.apache.ignite.internal.sql.engine.exec.ScannableTable; import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition; import org.apache.ignite.internal.sql.engine.exec.exp.RangeIterable; -import org.apache.ignite.internal.sql.engine.schema.ColumnDescriptor; import org.apache.ignite.internal.sql.engine.schema.IgniteIndex; -import org.apache.ignite.internal.sql.engine.schema.TableDescriptor; import org.apache.ignite.internal.sql.engine.util.Commons; import org.apache.ignite.internal.util.SubscriptionUtils; import org.apache.ignite.internal.util.TransformingIterator; @@ -64,14 +60,11 @@ public class IndexScanNode extends StorageScanNode { private final @Nullable Comparator comp; - private final List columns; - /** * Constructor. * * @param ctx Execution context. * @param rowFactory Row factory. - * @param tableDescriptor Table descriptor. * @param partitionProvider Partition provider. * @param comp Rows comparator. * @param rangeConditions Range conditions. @@ -84,7 +77,6 @@ public IndexScanNode( RowFactory rowFactory, IgniteIndex schemaIndex, ScannableTable table, - TableDescriptor tableDescriptor, PartitionProvider partitionProvider, @Nullable Comparator comp, @Nullable RangeIterable rangeConditions, @@ -101,12 +93,6 @@ public IndexScanNode( this.rangeConditions = rangeConditions; this.comp = comp; this.factory = rowFactory; - - columns = schemaIndex.collation().getFieldCollations().stream() - .map(RelFieldCollation::getFieldIndex) - .map(tableDescriptor::columnDescriptor) - .map(ColumnDescriptor::name) - .collect(Collectors.toList()); } /** {@inheritDoc} */ @@ -147,12 +133,10 @@ private Publisher partitionPublisher( switch (schemaIndex.type()) { case SORTED: - return table.indexRangeScan(ctx, partWithConsistencyToken, factory, indexId, - columns, cond, requiredColumns); + return table.indexRangeScan(ctx, partWithConsistencyToken, factory, indexId, cond, requiredColumns); case HASH: - return table.indexLookup(ctx, partWithConsistencyToken, factory, indexId, - columns, cond.lower(), requiredColumns); + return table.indexLookup(ctx, partWithConsistencyToken, factory, indexId, cond.lower(), requiredColumns); default: throw new AssertionError("Unexpected index type: " + schemaIndex.type()); diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java index b2f0f01e44fa..245450835145 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java @@ -17,7 +17,6 @@ package org.apache.ignite.internal.sql.engine.exec; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow.Publisher; import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory; @@ -44,7 +43,6 @@ public Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns ) { @@ -57,7 +55,6 @@ public Publisher indexLookup( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, RowT key, int @Nullable [] requiredColumns ) { diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableSelfTest.java similarity index 95% rename from modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java rename to modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableSelfTest.java index 420d3aad04e3..649177f996d1 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableSelfTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.ignite.internal.sql.engine.exec.rel; +package org.apache.ignite.internal.sql.engine.exec; import static org.apache.ignite.internal.lang.IgniteStringFormatter.format; import static org.apache.ignite.internal.storage.index.SortedIndexStorage.GREATER_OR_EQUAL; @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; @@ -53,7 +54,6 @@ import java.util.stream.Stream; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory.Builder; -import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.ImmutableIntList; import org.apache.ignite.internal.binarytuple.BinaryTuple; @@ -62,12 +62,7 @@ import org.apache.ignite.internal.schema.BinaryRow; import org.apache.ignite.internal.schema.BinaryRowEx; import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory; -import org.apache.ignite.internal.sql.engine.exec.ExecutionContext; -import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken; -import org.apache.ignite.internal.sql.engine.exec.ScannableTable; -import org.apache.ignite.internal.sql.engine.exec.ScannableTableImpl; -import org.apache.ignite.internal.sql.engine.exec.TableRowConverter; -import org.apache.ignite.internal.sql.engine.exec.TxAttributes; +import org.apache.ignite.internal.sql.engine.exec.ScannableTableImpl.IndexMeta; import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition; import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler; import org.apache.ignite.internal.sql.engine.framework.NoOpTransaction; @@ -99,6 +94,7 @@ public class ScannableTableSelfTest extends BaseIgniteAbstractTest { private static final IgniteTypeFactory TYPE_FACTORY = Commons.typeFactory(); + private static final int INDEX_ID = 3; @Mock(lenient = true) private InternalTable internalTable; @@ -179,7 +175,7 @@ public void testIndexScan(NoOpTransaction tx, Bound lower, Bound upper) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; Object[] lowerValue = lower == Bound.NONE ? null : new Object[]{1}; Object[] upperValue = upper == Bound.NONE ? null : new Object[]{10}; TestRangeCondition condition = new TestRangeCondition<>(); @@ -241,7 +237,7 @@ public void testIndexScanWithRequiredColumns(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; TestRangeCondition condition = new TestRangeCondition<>(); // Set any valid bounds, they are not of our interest here. @@ -283,7 +279,7 @@ public void testIndexScanError(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; TestRangeCondition condition = new TestRangeCondition<>(); // Set any valid bounds, they are not of our interest here. condition.setLower(Bound.INCLUSIVE, new Object[]{0}); @@ -312,7 +308,7 @@ public void testIndexScanInvalidCondition(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; TestRangeCondition condition = new TestRangeCondition<>(); // Bound columns != input columns. condition.setLower(Bound.INCLUSIVE, new Object[]{1, 2}); @@ -342,7 +338,7 @@ public void testIndexScanPartialCondition(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; TestRangeCondition condition = new TestRangeCondition<>(); condition.setLower(Bound.INCLUSIVE, new Object[]{1, 2}); @@ -387,7 +383,7 @@ public void testIndexLookup(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; Object[] key = {1}; ArgumentCaptor criteriaCaptor = ArgumentCaptor.forClass(IndexScanCriteria.Lookup.class); @@ -430,7 +426,7 @@ public void testIndexLookupWithRequiredColumns(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; Object[] key = {1}; ResultCollector collector = tester.indexLookUp(partitionId, consistencyToken, tx, indexId, key); @@ -466,7 +462,7 @@ public void testIndexLookupError(NoOpTransaction tx) { int partitionId = 1; long consistencyToken = 2; - int indexId = 3; + int indexId = INDEX_ID; Object[] key = {1}; ResultCollector collector = tester.indexLookUp(partitionId, consistencyToken, tx, indexId, key); @@ -500,7 +496,11 @@ private class Tester { Tester(TestInput input) { this.input = input; rowConverter = new RowCollectingTableRowConverter(input); - scannableTable = new ScannableTableImpl(internalTable, rf -> rowConverter); + scannableTable = new ScannableTableImpl( + internalTable, + Int2ObjectMaps.singleton(INDEX_ID, new IndexMeta(input.indexColumns.cardinality())), + rf -> rowConverter + ); } ResultCollector tableScan(int partitionId, long consistencyToken, NoOpTransaction tx) { @@ -552,14 +552,12 @@ ResultCollector indexScan( RowFactory rowFactory = ArrayRowHandler.INSTANCE.create(input.rowSchema); RangeCondition rangeCondition = condition.asRangeCondition(); - List indexColumns = input.getIndexColumns(); Publisher publisher = scannableTable.indexRangeScan( ctx, new PartitionWithConsistencyToken(partitionId, consistencyToken), rowFactory, indexId, - indexColumns, rangeCondition, requiredFields ); @@ -584,14 +582,12 @@ ResultCollector indexLookUp(int partitionId, long consistencyToken, NoOpTransact eq(OperationContext.create(txContext))); RowFactory rowFactory = ArrayRowHandler.INSTANCE.create(input.rowSchema); - List indexColumns = input.getIndexColumns(); Publisher publisher = scannableTable.indexLookup( ctx, new PartitionWithConsistencyToken(partitionId, consistencyToken), rowFactory, indexId, - indexColumns, key, requiredFields ); @@ -660,17 +656,6 @@ void done() { void sendError(Throwable t) { publisher.closeExceptionally(t); } - - private List getIndexColumns() { - List columns = new ArrayList<>(); - - indexColumns.stream().forEach(i -> { - RelDataTypeField field = rowType.getFieldList().get(i); - columns.add(field.getName()); - }); - - return columns; - } } // Collects rows received from an input source. diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java index eb97e9dfc7a5..a21e9126a53f 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java @@ -97,7 +97,7 @@ public void testSortedIndex() { Comparator cmp = Comparator.comparing(row -> (Comparable) row[0]); - IndexScanNode node = tester.createSortedIndex(indexDescriptor, tableDescriptor, scannableTable, cmp); + IndexScanNode node = tester.createSortedIndex(indexDescriptor, scannableTable, cmp); List result = tester.execute(node); validateResult(result, List.of(new Object[]{1}, new Object[]{2}, new Object[]{4}, new Object[]{5})); @@ -121,7 +121,7 @@ public void testHashIndex() { scannableTable.setPartitionData(0, new Object[]{2}, new Object[]{1}); scannableTable.setPartitionData(2, new Object[]{0}); - IndexScanNode node = tester.createHashIndex(indexDescriptor, tableDescriptor, scannableTable); + IndexScanNode node = tester.createHashIndex(indexDescriptor, scannableTable); List result = tester.execute(node); validateResult(result, List.of(new Object[]{2}, new Object[]{1}, new Object[]{0})); @@ -180,7 +180,7 @@ private void checkIndexScan(boolean sorted, int bufferSize, int partitionsCount, ? TestBuilders.indexRangeScan(DataProvider.fromRow(new Object[]{42}, partDataSize)) : TestBuilders.indexLookup(DataProvider.fromRow(new Object[]{42}, partDataSize)); - IndexScanNode scanNode = new IndexScanNode<>(ctx, rowFactory, indexDescriptor, scannableIndex, tableDescriptor, + IndexScanNode scanNode = new IndexScanNode<>(ctx, rowFactory, indexDescriptor, scannableIndex, c -> partitions, comparator, conditions, null, null, null); RootNode rootNode = new RootNode<>(ctx); @@ -232,14 +232,13 @@ private static class Tester { this.ctx = ctx; } - IndexScanNode createSortedIndex(IgniteIndex indexDescriptor, TableDescriptor tableDescriptor, + IndexScanNode createSortedIndex(IgniteIndex indexDescriptor, TestScannableTable scannableTable, Comparator cmp) { - return createIndexNode(ctx, indexDescriptor, tableDescriptor, scannableTable, cmp); + return createIndexNode(ctx, indexDescriptor, scannableTable, cmp); } - IndexScanNode createHashIndex(IgniteIndex desc, TableDescriptor tableDescriptor, - TestScannableTable scannableTable) { - return createIndexNode(ctx, desc, tableDescriptor, scannableTable, null); + IndexScanNode createHashIndex(IgniteIndex desc, TestScannableTable scannableTable) { + return createIndexNode(ctx, desc, scannableTable, null); } List execute(IndexScanNode indexNode) { @@ -271,7 +270,7 @@ static void validateResult(List actual, List expected) { } private static IndexScanNode createIndexNode(ExecutionContext ctx, IgniteIndex indexDescriptor, - TableDescriptor tableDescriptor, TestScannableTable scannableTable, @Nullable Comparator comparator) { + TestScannableTable scannableTable, @Nullable Comparator comparator) { StructTypeBuilder rowSchemaBuilder = NativeTypes.structBuilder(); @@ -287,7 +286,7 @@ private static IndexScanNode createIndexNode(ExecutionContext partitions = scannableTable.getPartitions(); PartitionProvider partitionProvider = PartitionProvider.fromPartitions(partitions); - return new IndexScanNode<>(ctx, rowFactory, indexDescriptor, scannableTable, tableDescriptor, partitionProvider, + return new IndexScanNode<>(ctx, rowFactory, indexDescriptor, scannableTable, partitionProvider, comparator, conditions, null, null, null); } @@ -325,7 +324,6 @@ public Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns ) { @@ -340,7 +338,6 @@ public Publisher indexLookup( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, RowT key, int @Nullable [] requiredColumns ) { diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java index 56cb29c2c719..5d3941cc864a 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import java.util.LinkedList; import java.util.List; import java.util.Spliterator; @@ -227,7 +228,7 @@ public RowT toRow(ExecutionContext ectx, BinaryRow tableRow, RowFac return (RowT) TestInternalTableImpl.ROW; } }; - ScannableTableImpl scanableTable = new ScannableTableImpl(internalTable, rf -> rowConverter); + ScannableTableImpl scanableTable = new ScannableTableImpl(internalTable, Int2ObjectMaps.emptyMap(), rf -> rowConverter); PartitionProvider partitionProvider = PartitionProvider.fromPartitions(partsWithConsistencyTokens); IgniteTable schemaTable = mock(IgniteTable.class); TableScanNode scanNode = new TableScanNode<>(ctx, rowFactory, schemaTable, scanableTable, diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java index 438afc8ddde4..0ca1189d5e49 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java @@ -271,7 +271,6 @@ public Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns ) { @@ -296,7 +295,7 @@ public static ScannableTable indexLookup(DataProvider dataProvider) { return new AbstractScannableTable() { @Override public Publisher indexLookup(ExecutionContext ctx, PartitionWithConsistencyToken partWithConsistencyToken, - RowFactory rowFactory, int indexId, List columns, RowT key, + RowFactory rowFactory, int indexId, RowT key, int @Nullable [] requiredColumns) { return new TransformingPublisher<>( SubscriptionUtils.fromIterable( @@ -1585,7 +1584,6 @@ public Publisher indexRangeScan( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, @Nullable RangeCondition cond, int @Nullable [] requiredColumns) { throw new UnsupportedOperationException(); @@ -1597,7 +1595,6 @@ public Publisher indexLookup( PartitionWithConsistencyToken partWithConsistencyToken, RowFactory rowFactory, int indexId, - List columns, RowT key, int @Nullable [] requiredColumns ) { diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java index d743fe95c5b3..c7e019f12f33 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java @@ -93,7 +93,7 @@ public Publisher scan( @Override public Publisher indexRangeScan(ExecutionContext ctx, PartitionWithConsistencyToken partWithConsistencyToken, - RowFactory rowFactory, int indexId, List columns, @Nullable RangeCondition cond, + RowFactory rowFactory, int indexId, @Nullable RangeCondition cond, int @Nullable [] requiredColumns) { @@ -108,7 +108,7 @@ public Publisher indexRangeScan(ExecutionContext ctx, Partiti @Override public Publisher indexLookup(ExecutionContext ctx, PartitionWithConsistencyToken partWithConsistencyToken, - RowFactory rowFactory, int indexId, List columns, RowT key, + RowFactory rowFactory, int indexId, RowT key, int @Nullable [] requiredColumns) { return new TransformingPublisher<>( From 81bf73e1a17ee015dca8fa160035dc9c47a867b1 Mon Sep 17 00:00:00 2001 From: korlov42 Date: Tue, 7 Apr 2026 16:42:55 +0300 Subject: [PATCH 2/3] fix failed tests - caching of tables in SchemaManager has been updated to account for change in index configuration as well --- .../engine/schema/SqlSchemaManagerImpl.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java index 07bfa7624c46..e4b5d90d396c 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java @@ -186,11 +186,12 @@ public IgniteTable table(int catalogVersion, int tableId) { throw new IgniteInternalException(Common.INTERNAL_ERR, "Table with given id not found: " + tableId); } - CacheKey tableKey = tableCacheKey(tableDescriptor.id(), tableDescriptor.updateTimestamp()); + HybridTimestamp tableTime = deriveTableTimestamp(tableDescriptor, catalog); + CacheKey tableKey = tableCacheKey(tableDescriptor.id(), tableTime); IgniteTableImpl igniteTable = tableCache.get(tableKey, (x) -> { TableDescriptor descriptor = createTableDescriptorForTable(catalog, tableDescriptor); - return createTableDataOnlyTable(catalog, tableDescriptor, descriptor); + return createTableDataOnlyTable(catalog, tableTime, tableDescriptor, descriptor); }); Map tableIndexes = getIndexes(catalog, @@ -202,6 +203,24 @@ public IgniteTable table(int catalogVersion, int tableId) { }); } + private static HybridTimestamp deriveTableTimestamp(CatalogTableDescriptor table, Catalog catalog) { + // Consolidated time accounting for update time of all table-related objects which affects either + // planning or sql-related execution. + // + // Indexes included because ScannableTable contains index-related meta, hence we need to create + // new instance as soon as any indexes has changed or new index has been added. Such kind of + // consolidation doesn't account for DROP, but in case of indexes this should not be a big deal + // because index DROP is infrequent operation. + HybridTimestamp time = table.updateTimestamp(); + for (CatalogIndexDescriptor index : catalog.indexes(table.id())) { + if (index.updateTimestamp().compareTo(time) > 0) { + time = index.updateTimestamp(); + } + } + + return time; + } + private static long cacheKey(int part1, int part2) { long cacheKey = part1; cacheKey <<= 32; @@ -232,12 +251,13 @@ private IgniteSchema createSqlSchema(Catalog catalog, CatalogSchemaDescriptor sc // Assemble sql-engine.TableDescriptors as they are required by indexes. for (CatalogTableDescriptor tableDescriptor : schemaDescriptor.tables()) { - CacheKey tableKey = tableCacheKey(tableDescriptor.id(), tableDescriptor.updateTimestamp()); + HybridTimestamp tableTime = deriveTableTimestamp(tableDescriptor, catalog); + CacheKey tableKey = tableCacheKey(tableDescriptor.id(), tableTime); // Load cached table by (id, version) IgniteTableImpl igniteTable = tableCache.get(tableKey, (k) -> { TableDescriptor descriptor = createTableDescriptorForTable(catalog, tableDescriptor); - return createTableDataOnlyTable(catalog, tableDescriptor, descriptor); + return createTableDataOnlyTable(catalog, tableTime, tableDescriptor, descriptor); }); // Get actual indices @@ -445,6 +465,7 @@ private static CatalogColumnDescriptor createColumnDescriptor(CatalogTableColumn private IgniteTableImpl createTableDataOnlyTable( Catalog catalog, + HybridTimestamp tableTime, CatalogTableDescriptor table, TableDescriptor descriptor ) { @@ -455,7 +476,7 @@ private IgniteTableImpl createTableDataOnlyTable( CatalogZoneDescriptor zoneDescriptor = getZoneDescriptor(catalog, table.zoneId()); - return createTable(table, descriptor, tableIndexes, zoneDescriptor, sqlStatisticManager); + return createTable(table, tableTime, descriptor, tableIndexes, zoneDescriptor, sqlStatisticManager); } private Map getIndexes(Catalog catalog, int tableId, int primaryKeyIndexId) { @@ -501,6 +522,7 @@ private static CatalogZoneDescriptor getZoneDescriptor(Catalog catalog, int zone private static IgniteTableImpl createTable( CatalogTableDescriptor catalogTableDescriptor, + HybridTimestamp tableTime, TableDescriptor tableDescriptor, Map indexes, CatalogZoneDescriptor zoneDescriptor, @@ -526,7 +548,7 @@ private static IgniteTableImpl createTable( tableName, tableId, catalogTableDescriptor.latestSchemaVersion(), - catalogTableDescriptor.updateTimestamp().longValue(), + tableTime.longValue(), tableDescriptor, primaryKeyColumns, statistic, From 488b3c779a4759c2ed01eeed78336cefa7b56404 Mon Sep 17 00:00:00 2001 From: korlov42 Date: Tue, 7 Apr 2026 16:54:34 +0300 Subject: [PATCH 3/3] minors --- .../ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java index e4b5d90d396c..a43a3a293708 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java @@ -210,7 +210,7 @@ private static HybridTimestamp deriveTableTimestamp(CatalogTableDescriptor table // Indexes included because ScannableTable contains index-related meta, hence we need to create // new instance as soon as any indexes has changed or new index has been added. Such kind of // consolidation doesn't account for DROP, but in case of indexes this should not be a big deal - // because index DROP is infrequent operation. + // because it's rather infrequent operation. HybridTimestamp time = table.updateTimestamp(); for (CatalogIndexDescriptor index : catalog.indexes(table.id())) { if (index.updateTimestamp().compareTo(time) > 0) {