This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 33b578bcad IGNITE-18056 Sql. Make RangeConditions accordant to index
row type (#1329)
33b578bcad is described below
commit 33b578bcad191ace4f2274dbb252e1f8ef6c78c8
Author: Andrew V. Mashenkov <[email protected]>
AuthorDate: Thu Nov 24 14:03:43 2022 +0300
IGNITE-18056 Sql. Make RangeConditions accordant to index row type (#1329)
---
.../apache/ignite/internal/index/HashIndex.java | 11 +-
.../org/apache/ignite/internal/index/Index.java | 13 +-
.../apache/ignite/internal/index/SortedIndex.java | 12 +-
.../ignite/internal/index/SortedIndexImpl.java | 20 ++-
.../internal/sql/engine/exec/ExecutionContext.java | 13 --
.../sql/engine/exec/LogicalRelImplementor.java | 20 ++-
.../internal/sql/engine/exec/RowConverter.java | 59 +++----
.../sql/engine/exec/exp/ExpressionFactory.java | 7 +-
.../sql/engine/exec/exp/ExpressionFactoryImpl.java | 48 +++---
.../internal/sql/engine/exec/exp/RexImpTable.java | 9 ++
.../sql/engine/exec/rel/IndexScanNode.java | 21 +--
.../engine/rel/logical/IgniteLogicalIndexScan.java | 39 +++--
.../FilterSpoolMergeToSortedIndexSpoolRule.java | 5 +-
.../internal/sql/engine/schema/IgniteIndex.java | 30 ++++
.../sql/engine/schema/TableDescriptor.java | 3 +-
.../sql/engine/schema/TableDescriptorImpl.java | 3 +-
.../internal/sql/engine/trait/TraitUtils.java | 23 ++-
.../internal/sql/engine/util/IgniteMethod.java | 2 -
.../ignite/internal/sql/engine/util/RexUtils.java | 45 +++---
.../exec/rel/IndexScanNodeExecutionTest.java | 169 ++++++++++++---------
.../exec/rel/SortedIndexSpoolExecutionTest.java | 3 +-
.../CorrelatedNestedLoopJoinPlannerTest.java | 9 +-
.../planner/ProjectFilterScanMergePlannerTest.java | 58 ++++++-
.../planner/SortedIndexSpoolPlannerTest.java | 26 ++--
.../replicator/PartitionReplicaListener.java | 59 +++----
25 files changed, 431 insertions(+), 276 deletions(-)
diff --git
a/modules/index/src/main/java/org/apache/ignite/internal/index/HashIndex.java
b/modules/index/src/main/java/org/apache/ignite/internal/index/HashIndex.java
index ae03fdd3c5..069b1f7b1b 100644
---
a/modules/index/src/main/java/org/apache/ignite/internal/index/HashIndex.java
+++
b/modules/index/src/main/java/org/apache/ignite/internal/index/HashIndex.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.table.InternalTable;
import org.apache.ignite.internal.table.TableImpl;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.network.ClusterNode;
+import org.jetbrains.annotations.Nullable;
/**
* An object that represents a hash index.
@@ -76,13 +77,19 @@ public class HashIndex implements Index<IndexDescriptor> {
/** {@inheritDoc} */
@Override
- public Publisher<BinaryRow> lookup(int partId, InternalTransaction tx,
BinaryTuple key, BitSet columns) {
+ public Publisher<BinaryRow> lookup(int partId, @Nullable
InternalTransaction tx, BinaryTuple key, @Nullable BitSet columns) {
return table.lookup(partId, tx, id, key, columns);
}
/** {@inheritDoc} */
@Override
- public Publisher<BinaryRow> lookup(int partId, HybridTimestamp timestamp,
ClusterNode recipientNode, BinaryTuple key, BitSet columns) {
+ public Publisher<BinaryRow> lookup(
+ int partId,
+ HybridTimestamp timestamp,
+ ClusterNode recipientNode,
+ BinaryTuple key,
+ @Nullable BitSet columns
+ ) {
return table.lookup(partId, timestamp, recipientNode, id, key,
columns);
}
}
diff --git
a/modules/index/src/main/java/org/apache/ignite/internal/index/Index.java
b/modules/index/src/main/java/org/apache/ignite/internal/index/Index.java
index 68eb57d93d..896ee3ab09 100644
--- a/modules/index/src/main/java/org/apache/ignite/internal/index/Index.java
+++ b/modules/index/src/main/java/org/apache/ignite/internal/index/Index.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.network.ClusterNode;
+import org.jetbrains.annotations.Nullable;
/**
* An object describing an abstract index.
@@ -41,7 +42,7 @@ public interface Index<DescriptorT extends IndexDescriptor> {
/** Returns table id index belong to. */
UUID tableId();
- /** Returns index dewscriptor. */
+ /** Returns index descriptor. */
DescriptorT descriptor();
/**
@@ -53,7 +54,7 @@ public interface Index<DescriptorT extends IndexDescriptor> {
* @param columns Columns to include.
* @return A cursor from resulting rows.
*/
- Publisher<BinaryRow> lookup(int partId, InternalTransaction tx,
BinaryTuple key, BitSet columns);
+ Publisher<BinaryRow> lookup(int partId, @Nullable InternalTransaction tx,
BinaryTuple key, @Nullable BitSet columns);
/**
* Returns cursor for the values corresponding to the given key.
@@ -65,5 +66,11 @@ public interface Index<DescriptorT extends IndexDescriptor> {
* @param columns Columns to include.
* @return A cursor from resulting rows.
*/
- Publisher<BinaryRow> lookup(int partId, HybridTimestamp readTimestamp,
ClusterNode recipientNode, BinaryTuple key, BitSet columns);
+ Publisher<BinaryRow> lookup(
+ int partId,
+ HybridTimestamp readTimestamp,
+ ClusterNode recipientNode,
+ BinaryTuple key,
+ @Nullable BitSet columns
+ );
}
diff --git
a/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndex.java
b/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndex.java
index 08d971871a..0676ba557a 100644
---
a/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndex.java
+++
b/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndex.java
@@ -50,10 +50,10 @@ public interface SortedIndex extends
Index<SortedIndexDescriptor> {
*/
default Publisher<BinaryRow> scan(
int partId,
- InternalTransaction tx,
+ @Nullable InternalTransaction tx,
@Nullable BinaryTuplePrefix left,
@Nullable BinaryTuplePrefix right,
- BitSet columns
+ @Nullable BitSet columns
) {
return scan(partId, tx, left, right, INCLUDE_LEFT, columns);
}
@@ -75,7 +75,7 @@ public interface SortedIndex extends
Index<SortedIndexDescriptor> {
ClusterNode recipientNode,
@Nullable BinaryTuplePrefix left,
@Nullable BinaryTuplePrefix right,
- BitSet columns
+ @Nullable BitSet columns
) {
return scan(partId, readTimestamp, recipientNode, left, right,
INCLUDE_LEFT, columns);
}
@@ -95,11 +95,11 @@ public interface SortedIndex extends
Index<SortedIndexDescriptor> {
*/
Publisher<BinaryRow> scan(
int partId,
- InternalTransaction tx,
+ @Nullable InternalTransaction tx,
@Nullable BinaryTuplePrefix leftBound,
@Nullable BinaryTuplePrefix rightBound,
int flags,
- BitSet columnsToInclude
+ @Nullable BitSet columnsToInclude
);
@@ -124,6 +124,6 @@ public interface SortedIndex extends
Index<SortedIndexDescriptor> {
@Nullable BinaryTuplePrefix leftBound,
@Nullable BinaryTuplePrefix rightBound,
int flags,
- BitSet columnsToInclude
+ @Nullable BitSet columnsToInclude
);
}
diff --git
a/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndexImpl.java
b/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndexImpl.java
index 9b9f1be447..3ec520b895 100644
---
a/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndexImpl.java
+++
b/modules/index/src/main/java/org/apache/ignite/internal/index/SortedIndexImpl.java
@@ -78,13 +78,19 @@ public class SortedIndexImpl implements SortedIndex {
/** {@inheritDoc} */
@Override
- public Publisher<BinaryRow> lookup(int partId, InternalTransaction tx,
BinaryTuple key, BitSet columns) {
+ public Publisher<BinaryRow> lookup(int partId, @Nullable
InternalTransaction tx, BinaryTuple key, @Nullable BitSet columns) {
return table.lookup(partId, tx, id, key, columns);
}
/** {@inheritDoc} */
@Override
- public Publisher<BinaryRow> lookup(int partId, HybridTimestamp timestamp,
ClusterNode recipientNode, BinaryTuple key, BitSet columns) {
+ public Publisher<BinaryRow> lookup(
+ int partId,
+ HybridTimestamp timestamp,
+ ClusterNode recipientNode,
+ BinaryTuple key,
+ @Nullable BitSet columns
+ ) {
return table.lookup(partId, timestamp, recipientNode, id, key,
columns);
}
@@ -92,11 +98,11 @@ public class SortedIndexImpl implements SortedIndex {
@Override
public Publisher<BinaryRow> scan(
int partId,
- InternalTransaction tx,
- BinaryTuplePrefix leftBound,
- BinaryTuplePrefix rightBound,
+ @Nullable InternalTransaction tx,
+ @Nullable BinaryTuplePrefix leftBound,
+ @Nullable BinaryTuplePrefix rightBound,
int flags,
- BitSet columnsToInclude
+ @Nullable BitSet columnsToInclude
) {
return table.scan(partId, tx, id, leftBound, rightBound, flags,
columnsToInclude);
}
@@ -110,7 +116,7 @@ public class SortedIndexImpl implements SortedIndex {
@Nullable BinaryTuplePrefix leftBound,
@Nullable BinaryTuplePrefix rightBound,
int flags,
- BitSet columnsToInclude
+ @Nullable BitSet columnsToInclude
) {
return table.scan(partId, readTimestamp, recipientNode, id, leftBound,
rightBound, flags, columnsToInclude);
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
index 5d79cf740a..ec2d373c02 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
@@ -54,15 +54,6 @@ import org.jetbrains.annotations.Nullable;
* Runtime context allowing access to the tables in a database.
*/
public class ExecutionContext<RowT> extends AbstractQueryContext implements
DataContext {
- /** Placeholder for values, which expressions are not specified. */
- private static final Object UNSPECIFIED_VALUE = new Object() {
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return "<unspecified_value>";
- }
- };
-
private static final IgniteLogger LOG =
Loggers.forClass(ExecutionContext.class);
private static final TimeZone TIME_ZONE = TimeZone.getDefault(); // TODO
DistributedSqlConfiguration#timeZone
@@ -380,10 +371,6 @@ public class ExecutionContext<RowT> extends
AbstractQueryContext implements Data
return cancelFlag.get();
}
- public Object unspecifiedValue() {
- return UNSPECIFIED_VALUE;
- }
-
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
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 0c19d63b03..a258d9a57a 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
@@ -303,9 +303,17 @@ public class LogicalRelImplementor<RowT> implements
IgniteRelVisitor<Node<RowT>>
Predicate<RowT> filters = condition == null ? null :
expressionFactory.predicate(condition, rowType);
Function<RowT, RowT> prj = projects == null ? null :
expressionFactory.project(projects, rowType);
- //TODO: https://issues.apache.org/jira/browse/IGNITE-18056 Use
'idx.getRowType()' instead of 'tbl.getRowType()'
- RangeIterable<RowT> ranges = searchBounds == null ? null :
- expressionFactory.ranges(searchBounds, rel.collation(),
tbl.getRowType(typeFactory));
+ RangeIterable<RowT> ranges = null;
+
+ if (searchBounds != null) {
+ Comparator<RowT> searchRowComparator = null;
+
+ if (idx.collations() != null) {
+ searchRowComparator =
expressionFactory.comparator(TraitUtils.createCollation(idx.collations()));
+ }
+
+ ranges = expressionFactory.ranges(searchBounds,
idx.getRowType(typeFactory, tbl.descriptor()), searchRowComparator);
+ }
RelCollation outputCollation = rel.collation();
@@ -329,7 +337,6 @@ public class LogicalRelImplementor<RowT> implements
IgniteRelVisitor<Node<RowT>>
ctx.rowHandler().factory(ctx.getTypeFactory(), rowType),
idx,
tbl,
- rel.collation().getKeys(),
group.partitions(ctx.localNode().name()),
comp,
ranges,
@@ -445,13 +452,14 @@ public class LogicalRelImplementor<RowT> implements
IgniteRelVisitor<Node<RowT>>
assert rel.searchBounds() != null : rel;
Predicate<RowT> filter = expressionFactory.predicate(rel.condition(),
rel.getRowType());
- RangeIterable<RowT> ranges =
expressionFactory.ranges(rel.searchBounds(), collation, rel.getRowType());
+ Comparator<RowT> comparator = expressionFactory.comparator(collation);
+ RangeIterable<RowT> ranges =
expressionFactory.ranges(rel.searchBounds(), rel.getRowType(), comparator);
IndexSpoolNode<RowT> node = IndexSpoolNode.createTreeSpool(
ctx,
rel.getRowType(),
collation,
- expressionFactory.comparator(collation),
+ comparator,
filter,
ranges
);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowConverter.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowConverter.java
index 1daff10c43..c71b240e21 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowConverter.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowConverter.java
@@ -18,8 +18,7 @@
package org.apache.ignite.internal.sql.engine.exec;
import java.nio.ByteBuffer;
-import java.util.stream.IntStream;
-import org.apache.calcite.util.ImmutableIntList;
+import java.util.List;
import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.binarytuple.BinaryTuplePrefixBuilder;
import org.apache.ignite.internal.schema.BinaryConverter;
@@ -28,8 +27,10 @@ import org.apache.ignite.internal.schema.BinaryTuplePrefix;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
import org.apache.ignite.internal.schema.NativeTypeSpec;
+import org.apache.ignite.internal.sql.engine.exec.exp.RexImpTable;
import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
+import org.apache.ignite.internal.util.IgniteUtils;
/**
* Helper class provides method to convert binary tuple to rows and vice-versa.
@@ -38,9 +39,9 @@ public final class RowConverter {
/**
* Creates binary tuple schema for index rows.
*/
- public static BinaryTupleSchema createIndexRowSchema(TableDescriptor
tableDescriptor, ImmutableIntList idxColumnMapping) {
- Element[] elements = IntStream.of(idxColumnMapping.toIntArray())
- .mapToObj(tableDescriptor::columnDescriptor)
+ public static BinaryTupleSchema createIndexRowSchema(List<String>
indexedColumns, TableDescriptor tableDescriptor) {
+ Element[] elements = indexedColumns.stream()
+ .map(tableDescriptor::columnDescriptor)
.map(colDesc -> new Element(colDesc.physicalType(), true))
.toArray(Element[]::new);
@@ -60,29 +61,28 @@ public final class RowConverter {
public static <RowT> BinaryTuplePrefix toBinaryTuplePrefix(
ExecutionContext<RowT> ectx,
BinaryTupleSchema binarySchema,
- ImmutableIntList idxColumnMapper,
RowHandler.RowFactory<RowT> factory,
RowT searchRow
) {
RowHandler<RowT> handler = factory.handler();
- int prefixColumnsCount = binarySchema.elementCount();
+ int indexedColumnsCount = binarySchema.elementCount();
+ int prefixColumnsCount = handler.columnCount(searchRow);
- //TODO IGNITE-18056: Uncomment. Search row must be a valid index row
prefix.
- // assert handler.columnCount(searchRow) <=
binarySchema.elementCount() : "Invalid range condition";
- //
- // int specifiedCols = handler.columnCount(searchRow);
+ assert prefixColumnsCount == indexedColumnsCount : "Invalid range
condition";
int specifiedCols = 0;
for (int i = 0; i < prefixColumnsCount; i++) {
- if (handler.get(idxColumnMapper.get(i), searchRow) !=
ectx.unspecifiedValue()) {
- specifiedCols++;
+ if (handler.get(i, searchRow) ==
RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
+ break;
}
+
+ specifiedCols++;
}
- BinaryTuplePrefixBuilder tupleBuilder = new
BinaryTuplePrefixBuilder(specifiedCols, prefixColumnsCount);
+ BinaryTuplePrefixBuilder tupleBuilder = new
BinaryTuplePrefixBuilder(specifiedCols, indexedColumnsCount);
- return new BinaryTuplePrefix(binarySchema, toByteBuffer(ectx,
binarySchema, idxColumnMapper, handler, tupleBuilder, searchRow));
+ return new BinaryTuplePrefix(binarySchema, toByteBuffer(ectx,
binarySchema, handler, tupleBuilder, searchRow));
}
/**
@@ -98,35 +98,42 @@ public final class RowConverter {
public static <RowT> BinaryTuple toBinaryTuple(
ExecutionContext<RowT> ectx,
BinaryTupleSchema binarySchema,
- ImmutableIntList idxColumnMapper,
RowHandler.RowFactory<RowT> factory,
RowT searchRow
) {
RowHandler<RowT> handler = factory.handler();
- int prefixColumnsCount = binarySchema.elementCount();
+ int rowColumnsCount = handler.columnCount(searchRow);
- //TODO IGNITE-18056: Uncomment. Search row must be a valid index row.
- // assert handler.columnCount(searchRow) ==
binarySchema.elementCount() : "Invalid lookup condition";
+ assert rowColumnsCount == binarySchema.elementCount() : "Invalid
lookup key.";
- BinaryTupleBuilder tupleBuilder = new
BinaryTupleBuilder(prefixColumnsCount, binarySchema.hasNullableElements());
+ if (IgniteUtils.assertionsEnabled()) {
+ for (int i = 0; i < rowColumnsCount; i++) {
+ if (handler.get(i, searchRow) ==
RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
+ throw new AssertionError("Invalid lookup key.");
+ }
+ }
+ }
- return new BinaryTuple(binarySchema, toByteBuffer(ectx, binarySchema,
idxColumnMapper, handler, tupleBuilder, searchRow));
+ BinaryTupleBuilder tupleBuilder = new
BinaryTupleBuilder(rowColumnsCount, binarySchema.hasNullableElements());
+
+ return new BinaryTuple(binarySchema, toByteBuffer(ectx, binarySchema,
handler, tupleBuilder, searchRow));
}
private static <RowT> ByteBuffer toByteBuffer(
ExecutionContext<RowT> ectx,
BinaryTupleSchema binarySchema,
- ImmutableIntList idxColumnMapper,
RowHandler<RowT> handler,
BinaryTupleBuilder tupleBuilder,
RowT searchRow
) {
- for (int i = 0; i < binarySchema.elementCount(); i++) {
- Object val = handler.get(idxColumnMapper.get(i), searchRow);
+ int columnsCount = handler.columnCount(searchRow);
- if (val == ectx.unspecifiedValue()) {
- break;
+ for (int i = 0; i < columnsCount; i++) {
+ Object val = handler.get(i, searchRow);
+
+ if (val == RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
+ break; // No more columns in prefix.
}
Element element = binarySchema.element(i);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactory.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactory.java
index 9867073d55..0940ebe5ca 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactory.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactory.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rex.RexNode;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
import org.apache.ignite.internal.sql.engine.prepare.bounds.SearchBounds;
+import org.jetbrains.annotations.Nullable;
/**
* Expression factory.
@@ -110,13 +111,13 @@ public interface ExpressionFactory<RowT> {
* Creates iterable search bounds tuples (lower row/upper row) by search
bounds expressions.
*
* @param searchBounds Search bounds.
- * @param collation Collation.
* @param rowType Row type.
+ * @param comparator Comparator to return bounds in particular order.
*/
RangeIterable<RowT> ranges(
List<SearchBounds> searchBounds,
- RelCollation collation,
- RelDataType rowType
+ RelDataType rowType,
+ @Nullable Comparator<RowT> comparator
);
/**
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
index 1bc734e54e..beb5d14dbd 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
@@ -77,6 +77,7 @@ import
org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.Primitives;
+import org.jetbrains.annotations.Nullable;
/**
* Implements rex expression into a function object. Uses JaninoRexCompiler
under the hood. Each expression compiles into a class and a
@@ -141,7 +142,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
@Override
public int compare(RowT o1, RowT o2) {
RowHandler<RowT> hnd = ctx.rowHandler();
- Object unspecifiedVal = ctx.unspecifiedValue();
+ Object unspecifiedVal =
RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER;
for (RelFieldCollation field : collation.getFieldCollations())
{
int fieldIdx = field.getFieldIndex();
@@ -302,8 +303,8 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
@Override
public RangeIterable<RowT> ranges(
List<SearchBounds> searchBounds,
- RelCollation collation,
- RelDataType rowType
+ RelDataType rowType,
+ @Nullable Comparator<RowT> comparator
) {
RowFactory<RowT> rowFactory = ctx.rowHandler().factory(typeFactory,
rowType);
@@ -314,7 +315,6 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
searchBounds,
rowType,
rowFactory,
- collation.getKeys(),
0,
Arrays.asList(new RexNode[searchBounds.size()]),
Arrays.asList(new RexNode[searchBounds.size()]),
@@ -322,7 +322,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
true
);
- return new RangeIterableImpl(ranges, comparator(collation));
+ return new RangeIterableImpl(ranges, comparator);
}
/**
@@ -332,8 +332,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
* @param searchBounds Search bounds.
* @param rowType Row type.
* @param rowFactory Row factory.
- * @param collationKeys Collation keys.
- * @param collationKeyIdx Current collation key index (field to process).
+ * @param fieldIdx Current field index (field to process).
* @param curLower Current lower row.
* @param curUpper Current upper row.
* @param lowerInclude Include current lower row.
@@ -344,16 +343,15 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
List<SearchBounds> searchBounds,
RelDataType rowType,
RowFactory<RowT> rowFactory,
- List<Integer> collationKeys,
- int collationKeyIdx,
+ int fieldIdx,
List<RexNode> curLower,
List<RexNode> curUpper,
boolean lowerInclude,
boolean upperInclude
) {
- if ((collationKeyIdx >= collationKeys.size())
+ if ((fieldIdx >= searchBounds.size())
|| (!lowerInclude && !upperInclude)
- || searchBounds.get(collationKeys.get(collationKeyIdx)) ==
null) {
+ || searchBounds.get(fieldIdx) == null) {
ranges.add(new RangeConditionImpl(
scalar(curLower, rowType),
scalar(curUpper, rowType),
@@ -365,7 +363,6 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
return;
}
- int fieldIdx = collationKeys.get(collationKeyIdx);
SearchBounds fieldBounds = searchBounds.get(fieldIdx);
Collection<SearchBounds> fieldMultiBounds = fieldBounds instanceof
MultiBounds
@@ -405,8 +402,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
searchBounds,
rowType,
rowFactory,
- collationKeys,
- collationKeyIdx + 1,
+ fieldIdx + 1,
curLower,
curUpper,
lowerInclude && fieldLowerInclude,
@@ -415,7 +411,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
}
curLower.set(fieldIdx, null);
- curLower.set(fieldIdx, null);
+ curUpper.set(fieldIdx, null);
}
/**
@@ -432,7 +428,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
/**
* Creates {@link SingleScalar}, a code-generated expressions evaluator.
*
- * @param nodes Expressions. {@code Null} expressions will be evaluated to
{@link ExecutionContext#unspecifiedValue()}.
+ * @param nodes Expressions. {@code Null} expressions will be evaluated to
{@link RexImpTable#UNSPECIFIED_VALUE_PLACEHOLDER}.
* @param type Row type.
* @return SingleScalar.
*/
@@ -511,8 +507,9 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
assert nodes.size() == projects.size();
for (int i = 0; i < projects.size(); i++) {
- Expression val = unspecifiedValues.get(i) ? Expressions.call(ctx,
- IgniteMethod.CONTEXT_UNSPECIFIED_VALUE.method()) :
projects.get(i);
+ Expression val = unspecifiedValues.get(i)
+ ? Expressions.field(null, RexImpTable.class,
"UNSPECIFIED_VALUE_PLACEHOLDER")
+ : projects.get(i);
builder.add(
Expressions.statement(
@@ -716,10 +713,10 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
private final boolean upperInclude;
/** Lower row. */
- private RowT lowerRow;
+ private @Nullable RowT lowerRow;
/** Upper row. */
- private RowT upperRow;
+ private @Nullable RowT upperRow;
/** Row factory. */
private final RowFactory<RowT> factory;
@@ -771,7 +768,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
}
/** Clear cached rows. */
- public void clearCache() {
+ void clearCache() {
lowerRow = upperRow = null;
}
}
@@ -779,11 +776,14 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
private class RangeIterableImpl implements RangeIterable<RowT> {
private final List<RangeCondition<RowT>> ranges;
- private final Comparator<RowT> comparator;
+ private final @Nullable Comparator<RowT> comparator;
private boolean sorted;
- public RangeIterableImpl(List<RangeCondition<RowT>> ranges,
Comparator<RowT> comparator) {
+ RangeIterableImpl(
+ List<RangeCondition<RowT>> ranges,
+ @Nullable Comparator<RowT> comparator
+ ) {
this.ranges = ranges;
this.comparator = comparator;
}
@@ -807,7 +807,7 @@ public class ExpressionFactoryImpl<RowT> implements
ExpressionFactory<RowT> {
// intersection.
// Do not sort again if ranges already were sorted before,
different values of correlated variables
// should not affect ordering.
- if (!sorted) {
+ if (!sorted && comparator != null) {
ranges.sort((o1, o2) -> comparator.compare(o1.lower(),
o2.lower()));
sorted = true;
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
index a08e8b00ef..bac45c0a5e 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexImpTable.java
@@ -247,6 +247,15 @@ public class RexImpTable {
/** Placeholder for DEFAULT operator value. */
public static final Object DEFAULT_VALUE_PLACEHOLDER = new
DefaultValuePlaceholder();
+ /** Placeholder for values, which expressions are not specified. */
+ public static final Object UNSPECIFIED_VALUE_PLACEHOLDER = new Object() {
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "<unspecified_value>";
+ }
+ };
+
/**
* Constructor.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
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 f02932d710..3fd4976ba1 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
@@ -32,7 +32,6 @@ import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Function;
import java.util.function.Predicate;
-import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.index.SortedIndex;
import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryTuple;
@@ -78,8 +77,6 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT> {
private final Function<BinaryRow, RowT> tableRowConverter;
- private final ImmutableIntList idxColumnMapping;
-
/** Participating columns. */
private final @Nullable BitSet requiredColumns;
@@ -95,7 +92,7 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT> {
private boolean inLoop;
- private Subscription activeSubscription;
+ private @Nullable Subscription activeSubscription;
private boolean rangeConditionsProcessed;
@@ -117,7 +114,6 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
RowHandler.RowFactory<RowT> rowFactory,
IgniteIndex schemaIndex,
InternalIgniteTable schemaTable,
- ImmutableIntList idxColumnMapping,
int[] parts,
@Nullable Comparator<RowT> comp,
@Nullable RangeIterable<RowT> rangeConditions,
@@ -129,7 +125,7 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
assert !nullOrEmpty(parts);
- assert context().transaction() != null || context().transactionTime()
!= null : "Transaction not initialized.";
+ assert ctx.transaction() != null || ctx.transactionTime() != null :
"Transaction not initialized.";
this.schemaIndex = schemaIndex;
this.parts = parts;
@@ -137,7 +133,6 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
this.rowTransformer = rowTransformer;
this.requiredColumns = requiredColumns;
this.rangeConditions = rangeConditions;
- this.idxColumnMapping = idxColumnMapping;
this.comp = comp;
this.factory = rowFactory;
@@ -145,7 +140,7 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
tableRowConverter = row -> schemaTable.toRow(context(), row, factory,
requiredColumns);
- indexRowSchema =
RowConverter.createIndexRowSchema(schemaTable.descriptor(), idxColumnMapping);
+ indexRowSchema =
RowConverter.createIndexRowSchema(schemaIndex.columns(),
schemaTable.descriptor());
}
/** {@inheritDoc} */
@@ -321,11 +316,9 @@ public class IndexScanNode<RowT> extends
AbstractNode<RowT> {
);
} else {
assert schemaIndex.type() == Type.HASH;
- BinaryTuple key = null;
+ assert cond != null && cond.lower() != null : "Invalid hash index
condition.";
- if (cond != null) {
- key = toBinaryTuple(cond.lower());
- }
+ BinaryTuple key = toBinaryTuple(cond.lower());
pub = schemaIndex.index().lookup(
part,
@@ -412,7 +405,7 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
return null;
}
- return RowConverter.toBinaryTuplePrefix(context(), indexRowSchema,
idxColumnMapping, factory, condition);
+ return RowConverter.toBinaryTuplePrefix(context(), indexRowSchema,
factory, condition);
}
@Contract("null -> null")
@@ -421,7 +414,7 @@ public class IndexScanNode<RowT> extends AbstractNode<RowT>
{
return null;
}
- return RowConverter.toBinaryTuple(context(), indexRowSchema,
idxColumnMapping, factory, condition);
+ return RowConverter.toBinaryTuple(context(), indexRowSchema, factory,
condition);
}
private RowT convert(BinaryRow binaryRow) {
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/logical/IgniteLogicalIndexScan.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/logical/IgniteLogicalIndexScan.java
index 34f3226577..aef005a0a5 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/logical/IgniteLogicalIndexScan.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/logical/IgniteLogicalIndexScan.java
@@ -56,16 +56,24 @@ public class IgniteLogicalIndexScan extends
AbstractIndexScan {
IgniteIndex index = tbl.getIndex(idxName);
RelCollation collation = TraitUtils.createCollation(index.columns(),
index.collations(), tbl.descriptor());
- if (requiredColumns != null) {
- Mappings.TargetMapping targetMapping =
Commons.mapping(requiredColumns,
- tbl.getRowType(typeFactory).getFieldCount());
- collation = collation.apply(targetMapping);
- }
-
List<SearchBounds> searchBounds;
if (index.type() == Type.HASH) {
- searchBounds = buildHashIndexConditions(cluster, tbl,
index.columns(), cond, requiredColumns);
+ if (requiredColumns != null) {
+ Mappings.TargetMapping targetMapping =
Commons.mapping(requiredColumns, tbl.getRowType(typeFactory).getFieldCount());
+ RelCollation outputCollation = collation.apply(targetMapping);
+
+ searchBounds = (collation.getFieldCollations().size() ==
outputCollation.getFieldCollations().size())
+ ? buildHashIndexConditions(cluster, tbl,
outputCollation, cond, requiredColumns)
+ : null;
+ } else {
+ searchBounds = buildHashIndexConditions(cluster, tbl,
collation, cond, requiredColumns);
+ }
} else if (index.type() == Type.SORTED) {
+ if (requiredColumns != null) {
+ Mappings.TargetMapping targetMapping =
Commons.mapping(requiredColumns, tbl.getRowType(typeFactory).getFieldCount());
+ collation = collation.apply(targetMapping);
+ }
+
searchBounds = buildSortedIndexConditions(cluster, tbl, collation,
cond, requiredColumns);
} else {
throw new AssertionError("Unknown index type [type=" +
index.type() + "]");
@@ -110,7 +118,7 @@ public class IgniteLogicalIndexScan extends
AbstractIndexScan {
super(cluster, traits, List.of(), tbl, idxName, type, proj, cond,
searchBounds, requiredCols);
}
- private static List<SearchBounds> buildSortedIndexConditions(
+ private static @Nullable List<SearchBounds> buildSortedIndexConditions(
RelOptCluster cluster,
InternalIgniteTable table,
RelCollation collation,
@@ -118,7 +126,7 @@ public class IgniteLogicalIndexScan extends
AbstractIndexScan {
@Nullable ImmutableBitSet requiredColumns
) {
if (collation.getFieldCollations().isEmpty()) {
- return List.of();
+ return null;
}
return RexUtils.buildSortedIndexConditions(
@@ -133,11 +141,16 @@ public class IgniteLogicalIndexScan extends
AbstractIndexScan {
private static List<SearchBounds> buildHashIndexConditions(
RelOptCluster cluster,
InternalIgniteTable table,
- List<String> indexedColumns,
- @Nullable RexNode cond,
+ RelCollation collation,
+ RexNode cond,
@Nullable ImmutableBitSet requiredColumns
) {
- return RexUtils.buildHashIndexConditions(cluster, indexedColumns, cond,
- table.getRowType(Commons.typeFactory(cluster)),
requiredColumns);
+ return RexUtils.buildHashIndexConditions(
+ cluster,
+ collation,
+ cond,
+ table.getRowType(Commons.typeFactory(cluster)),
+ requiredColumns
+ );
}
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
index 2d1467f5f6..f0048d28a6 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
@@ -24,7 +24,6 @@ import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.List;
-import java.util.stream.Collectors;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptRule;
@@ -120,8 +119,8 @@ public class FilterSpoolMergeToSortedIndexSpoolRule extends
RelRule<FilterSpoolM
List<RelFieldCollation> collationFields =
inCollation.getFieldCollations().subList(0, searchKeys.size());
- assert
searchKeys.containsAll(collationFields.stream().map(RelFieldCollation::getFieldIndex)
- .collect(Collectors.toSet())) : "Search condition should
be a prefix of collation [searchKeys="
+ assert searchKeys.size() == collationFields.size()
+ : "Search condition should be a prefix of collation
[searchKeys="
+ searchKeys + ", collation=" + inCollation + ']';
searchCollation = RelCollations.of(collationFields);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteIndex.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteIndex.java
index 750c3bd555..459baaec3e 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteIndex.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/IgniteIndex.java
@@ -17,13 +17,18 @@
package org.apache.ignite.internal.sql.engine.schema;
+import static
org.apache.ignite.internal.sql.engine.util.TypeUtils.native2relationalType;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.ignite.internal.index.ColumnCollation;
import org.apache.ignite.internal.index.Index;
import org.apache.ignite.internal.index.SortedIndex;
import org.apache.ignite.internal.index.SortedIndexDescriptor;
+import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.jetbrains.annotations.Nullable;
/**
@@ -114,6 +119,31 @@ public class IgniteIndex {
return type;
}
+ //TODO: cache rowType as it can't be changed.
+
+ /** Returns index row type.
+ *
+ * <p>This is a struct type whose fields describe the names and types of
indexed columns.</p>
+ *
+ * <p>The implementer must use the type factory provided. This ensures that
+ * the type is converted into a canonical form; other equal types in the
same
+ * query will use the same object.</p>
+ *
+ * @param typeFactory Type factory with which to create the type
+ * @param tableDescriptor Table descriptor.
+ * @return Row type.
+ */
+ public RelDataType getRowType(IgniteTypeFactory typeFactory,
TableDescriptor tableDescriptor) {
+ RelDataTypeFactory.Builder b = new
RelDataTypeFactory.Builder(typeFactory);
+
+ for (String colName : columns) {
+ ColumnDescriptor colDesc =
tableDescriptor.columnDescriptor(colName);
+ b.add(colName, native2relationalType(typeFactory,
colDesc.physicalType(), colDesc.nullable()));
+ }
+
+ return b.build();
+ }
+
private static @Nullable List<Collation> deriveCollations(Index<?> index) {
if (index.descriptor() instanceof SortedIndexDescriptor) {
SortedIndexDescriptor descriptor = (SortedIndexDescriptor)
index.descriptor();
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptor.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptor.java
index fcfe5e83f5..e7b7e6723a 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptor.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptor.java
@@ -25,6 +25,7 @@ import
org.apache.calcite.sql2rel.InitializerExpressionFactory;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
+import org.jetbrains.annotations.Nullable;
/**
* TableDescriptor interface.
@@ -77,7 +78,7 @@ public interface TableDescriptor extends RelProtoDataType,
InitializerExpression
* @param usedColumns Participating columns numeration.
* @return Row type.
*/
- RelDataType rowType(IgniteTypeFactory factory, ImmutableBitSet
usedColumns);
+ RelDataType rowType(IgniteTypeFactory factory, @Nullable ImmutableBitSet
usedColumns);
/**
* Checks whether is possible to update a column with a given index.
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
index 969c375156..75500159c7 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/TableDescriptorImpl.java
@@ -38,6 +38,7 @@ import
org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
+import org.jetbrains.annotations.Nullable;
/**
* TableDescriptorImpl.
@@ -174,7 +175,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory implem
/** {@inheritDoc} */
@Override
- public RelDataType rowType(IgniteTypeFactory factory, ImmutableBitSet
usedColumns) {
+ public RelDataType rowType(IgniteTypeFactory factory, @Nullable
ImmutableBitSet usedColumns) {
RelDataTypeFactory.Builder b = new RelDataTypeFactory.Builder(factory);
if (usedColumns == null) {
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
index 7a6bb2efd8..0666e2d5fe 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
@@ -70,6 +70,7 @@ import
org.apache.ignite.internal.sql.engine.rel.IgniteTableSpool;
import org.apache.ignite.internal.sql.engine.rel.IgniteTrimExchange;
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.IgniteIndex.Collation;
import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.lang.IgniteInternalException;
@@ -501,6 +502,22 @@ public class TraitUtils {
);
}
+ /**
+ * Creates {@link RelCollation} object from a given collations.
+ *
+ * @param collations List of collations.
+ * @return a {@link RelCollation} object.
+ */
+ public static RelCollation createCollation(List<Collation> collations) {
+ List<RelFieldCollation> fieldCollations = new
ArrayList<>(collations.size());
+
+ for (int i = 0; i < collations.size(); i++) {
+ fieldCollations.add(createFieldCollation(i, collations.get(i)));
+ }
+
+ return RelCollations.of(fieldCollations);
+ }
+
/**
* Creates {@link RelCollation} object from a given collations.
*
@@ -514,9 +531,9 @@ public class TraitUtils {
@Nullable List<IgniteIndex.Collation> collations,
TableDescriptor descriptor
) {
- if (collations == null) { // Build collation for Hash index.
- List<RelFieldCollation> fieldCollations = new ArrayList<>();
+ List<RelFieldCollation> fieldCollations = new
ArrayList<>(indexedColumns.size());
+ if (collations == null) { // Build collation for Hash index.
for (int i = 0; i < indexedColumns.size(); i++) {
String columnName = indexedColumns.get(i);
ColumnDescriptor columnDesc =
descriptor.columnDescriptor(columnName);
@@ -527,8 +544,6 @@ public class TraitUtils {
return RelCollations.of(fieldCollations);
}
- List<RelFieldCollation> fieldCollations = new ArrayList<>();
-
for (int i = 0; i < indexedColumns.size(); i++) {
String columnName = indexedColumns.get(i);
IgniteIndex.Collation collation = collations.get(i);
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
index 5b8c0bbe86..c63290af52 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
@@ -48,8 +48,6 @@ public enum IgniteMethod {
/** See {@link ExecutionContext#rowHandler()}. */
CONTEXT_ROW_HANDLER(ExecutionContext.class, "rowHandler"),
- /** See {@link ExecutionContext#unspecifiedValue()}. */
- CONTEXT_UNSPECIFIED_VALUE(ExecutionContext.class, "unspecifiedValue"),
/** See {@link ExecutionContext#getCorrelated(int)}. */
CONTEXT_GET_CORRELATED_VALUE(ExecutionContext.class, "getCorrelated",
int.class),
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
index 0e837c557c..0482e981d9 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
@@ -212,12 +212,12 @@ public class RexUtils {
/**
* Builds sorted index search bounds.
*/
- public static List<SearchBounds> buildSortedIndexConditions(
+ public static @Nullable List<SearchBounds> buildSortedIndexConditions(
RelOptCluster cluster,
RelCollation collation,
- RexNode condition,
+ @Nullable RexNode condition,
RelDataType rowType,
- ImmutableBitSet requiredColumns
+ @Nullable ImmutableBitSet requiredColumns
) {
if (condition == null) {
return null;
@@ -255,11 +255,13 @@ public class RexUtils {
mapping = Commons.inverseMapping(requiredColumns, types.size());
}
- List<SearchBounds> bounds = Arrays.asList(new
SearchBounds[types.size()]);
+ List<SearchBounds> bounds = Arrays.asList(new
SearchBounds[collation.getFieldCollations().size()]);
boolean boundsEmpty = true;
int prevComplexity = 1;
- for (RelFieldCollation fc : collation.getFieldCollations()) {
+ List<RelFieldCollation> fieldCollations =
collation.getFieldCollations();
+ for (int i = 0; i < fieldCollations.size(); i++) {
+ RelFieldCollation fc = fieldCollations.get(i);
int collFldIdx = fc.getFieldIndex();
List<RexCall> collFldPreds = fieldsToPredicates.get(collFldIdx);
@@ -280,7 +282,7 @@ public class RexUtils {
boundsEmpty = false;
- bounds.set(collFldIdx, fldBounds);
+ bounds.set(i, fldBounds);
if (fldBounds instanceof MultiBounds) {
prevComplexity *= ((MultiBounds) fldBounds).bounds().size();
@@ -304,10 +306,10 @@ public class RexUtils {
*/
public static List<SearchBounds> buildHashIndexConditions(
RelOptCluster cluster,
- List<String> indexedColumns,
+ RelCollation collation,
RexNode condition,
RelDataType rowType,
- ImmutableBitSet requiredColumns
+ @Nullable ImmutableBitSet requiredColumns
) {
if (condition == null) {
return null;
@@ -321,26 +323,23 @@ public class RexUtils {
return null;
}
- List<SearchBounds> bounds = Arrays.asList(new
SearchBounds[rowType.getFieldCount()]);
+ List<RelDataType> types = RelOptUtil.getFieldTypeList(rowType);
+
+ List<SearchBounds> bounds = Arrays.asList(new
SearchBounds[collation.getFieldCollations().size()]);
Mappings.TargetMapping toTrimmedRowMapping = null;
if (requiredColumns != null) {
- toTrimmedRowMapping = Commons.mapping(requiredColumns,
rowType.getFieldCount());
+ toTrimmedRowMapping = Commons.inverseMapping(requiredColumns,
types.size());
}
- for (String columnName : indexedColumns) {
- RelDataTypeField field = rowType.getField(columnName, true, false);
-
- if (field == null) {
- return null;
- }
-
- int collFldIdx = toTrimmedRowMapping == null ? field.getIndex() :
toTrimmedRowMapping.getTargetOpt(field.getIndex());
+ List<RelFieldCollation> fieldCollations =
collation.getFieldCollations();
+ for (int i = 0; i < fieldCollations.size(); i++) {
+ int collFldIdx = fieldCollations.get(i).getFieldIndex();
List<RexCall> collFldPreds = fieldsToPredicates.get(collFldIdx);
if (nullOrEmpty(collFldPreds)) {
- return null; // Hash index can't be applied to partial
condition.
+ return null; // Partial condition implies index scan, which is
not supported.
}
RexCall columnPred = collFldPreds.stream()
@@ -348,10 +347,14 @@ public class RexUtils {
.findAny().orElse(null);
if (columnPred == null) {
- return null;
+ return null; // Non-equality conditions are not expected.
+ }
+
+ if (toTrimmedRowMapping != null) {
+ collFldIdx = toTrimmedRowMapping.getSourceOpt(collFldIdx);
}
- bounds.set(collFldIdx, createBounds(null,
Collections.singletonList(columnPred), cluster, field.getType(), 1));
+ bounds.set(i, createBounds(null,
Collections.singletonList(columnPred), cluster, types.get(collFldIdx), 1));
}
return bounds;
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 39fa23fec7..b3d924244e 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
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.rel;
+import static
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.Mockito.when;
@@ -34,7 +35,6 @@ import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory.Builder;
-import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.index.ColumnCollation;
import org.apache.ignite.internal.index.Index;
import org.apache.ignite.internal.index.IndexDescriptor;
@@ -53,6 +53,7 @@ import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowFactory;
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.exec.exp.RexImpTable;
import org.apache.ignite.internal.sql.engine.planner.AbstractPlannerTest;
import org.apache.ignite.internal.sql.engine.schema.IgniteIndex;
import org.apache.ignite.internal.sql.engine.schema.IgniteIndex.Type;
@@ -62,7 +63,6 @@ import
org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
-import org.apache.ignite.internal.testframework.IgniteTestUtils;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.BeforeAll;
@@ -74,6 +74,7 @@ import org.mockito.Mockito;
* Note: we just bounds are valid and don't care that data meets bound
conditions.
* Bound condition applies in underlying storage, which is mocked here.
*/
+@SuppressWarnings("ThrowableNotThrown")
public class IndexScanNodeExecutionTest extends AbstractExecutionTest {
private static final Comparator<Object[]> comp =
Comparator.comparingLong(v -> (long) ((Object[]) v)[0]);
@@ -112,135 +113,170 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
}
@Test
- public void sortedIndexScan() {
+ public void sortedIndexScanWithExactBound() {
+ // Lower bound.
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2, 1, null},
- new Object[]{null, 3, 0, null},
+ new Object[]{2L, 1},
+ null,
sortedScanResult
);
- }
-
- @Test
- public void sortedIndexScan2() {
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2, 1, null},
- new Object[]{null, 4, null, null},
+ new Object[]{2L, null},
+ null,
sortedScanResult
-
);
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2, null, null},
- new Object[]{null, 4, 0, null},
+ new Object[]{null, 1},
+ null,
sortedScanResult
);
- }
-
- @Test
- public void sortedIndexScanNoUpperBound() {
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2, 1, null},
+ new Object[]{null, null},
null,
sortedScanResult
);
-
+ // Upper bound.
+ validateSortedIndexScan(
+ sortedIndexData,
+ null,
+ new Object[]{4L, 0},
+ sortedScanResult
+ );
+ validateSortedIndexScan(
+ sortedIndexData,
+ null,
+ new Object[]{4L, null},
+ sortedScanResult
+ );
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, null, null, null},
null,
+ new Object[]{null, 0},
+ sortedScanResult
+ );
+ validateSortedIndexScan(
+ sortedIndexData,
+ null,
+ new Object[]{null, null},
sortedScanResult
);
}
@Test
- public void sortedIndexScanNoLowerBound() {
+ public void sortedIndexScanWithPrefixBound() {
validateSortedIndexScan(
sortedIndexData,
+ new Object[]{2L, RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER},
+ null,
+ sortedScanResult
+ );
+ validateSortedIndexScan(
+ sortedIndexData,
+ new Object[]{null, RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER},
null,
- new Object[]{null, 4, 0, null},
sortedScanResult
);
validateSortedIndexScan(
sortedIndexData,
null,
- new Object[]{null, null, null, null},
+ new Object[]{4L, RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER},
+ sortedScanResult
+ );
+ validateSortedIndexScan(
+ sortedIndexData,
+ null,
+ new Object[]{null, RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER},
sortedScanResult
);
}
@Test
public void sortedIndexScanInvalidBounds() {
- IgniteTestUtils.assertThrowsWithCause(() ->
+ assertThrowsWithCause(() ->
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2, "Brutus", null},
- new Object[]{null, 3.9, 0, null},
+ new Object[]{2L, "Brutus"},
+ null,
EMPTY
), ClassCastException.class, "class java.lang.String cannot be
cast to class java.lang.Integer");
- IgniteTestUtils.assertThrowsWithCause(() ->
+ assertThrowsWithCause(() ->
validateSortedIndexScan(
sortedIndexData,
- new Object[]{null, 2},
- new Object[]{null, 3},
+ null,
+ new Object[]{3.9, 0},
EMPTY
- ), ArrayIndexOutOfBoundsException.class, "Index 2 out of
bounds for length 2");
+ ), ClassCastException.class, "class java.lang.Double cannot be
cast to class java.lang.Long");
+
+ assertThrowsWithCause(() ->
+ validateSortedIndexScan(
+ sortedIndexData,
+ new Object[]{1L},
+ null,
+ EMPTY
+ ), AssertionError.class, "Invalid range condition");
}
@Test
public void hashIndexLookupOverEmptyIndex() {
validateHashIndexScan(
EMPTY,
- new Object[]{null, 1, 3, null},
+ new Object[]{1L, 3},
EMPTY
);
}
@Test
public void hashIndexLookupNoKey() {
- // Validate data.
- validateHashIndexScan(
- hashIndexData,
- null,
- hashScanResult
- );
+ assertThrowsWithCause(() ->
+ validateHashIndexScan(
+ hashIndexData,
+ null,
+ hashScanResult
+ ), AssertionError.class, "Invalid hash index condition.");
}
@Test
public void hashIndexLookup() {
validateHashIndexScan(
hashIndexData,
- new Object[]{null, 4, 2, null},
+ new Object[]{4L, 2},
hashScanResult);
- }
- @Test
- public void hashIndexLookupEmptyKey() {
validateHashIndexScan(
hashIndexData,
- new Object[]{null, null, null, null},
+ new Object[]{null, null},
hashScanResult);
}
@Test
public void hashIndexLookupInvalidKey() {
- IgniteTestUtils.assertThrowsWithCause(() ->
+ // Hash index doesn't support range scans with prefix bounds.
+ assertThrowsWithCause(() ->
validateHashIndexScan(
hashIndexData,
- new Object[]{2},
+ new Object[]{2L},
EMPTY
- ), ArrayIndexOutOfBoundsException.class, "Index 2 out of
bounds for length 1");
+ ), AssertionError.class, "Invalid lookup key");
- IgniteTestUtils.assertThrowsWithCause(() ->
+ assertThrowsWithCause(() ->
validateHashIndexScan(
hashIndexData,
- new Object[]{null, 2, "Brutus", null},
+ new Object[]{2L, "Brutus"},
EMPTY
), ClassCastException.class, "class java.lang.String cannot be
cast to class java.lang.Integer");
+
+ assertThrowsWithCause(() ->
+ validateHashIndexScan(
+ sortedIndexData,
+ new Object[]{1L,
RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER},
+ EMPTY
+ ), AssertionError.class, "Invalid lookup key");
}
private static Object[][] generateIndexData(int partCnt, int partSize,
boolean sorted) {
@@ -265,7 +301,7 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
data[rowNum] = new Object[4];
int bound1 = ThreadLocalRandom.current().nextInt(3);
- int bound2 = ThreadLocalRandom.current().nextInt(3);
+ long bound2 = ThreadLocalRandom.current().nextLong(3);
data[rowNum][0] = uniqueNumList.get(rowNum);
data[rowNum][1] = bound1 == 0 ? null : bound1;
@@ -277,13 +313,13 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
return data;
}
- private void validateHashIndexScan(Object[][] tableData, @Nullable
Object[] key, Object[][] expRes) {
+ private void validateHashIndexScan(Object[][] tableData, @Nullable Object
@Nullable [] key, Object[][] expRes) {
SchemaDescriptor schemaDescriptor = new SchemaDescriptor(
1,
new Column[]{new Column("key", NativeTypes.INT64, false)},
new Column[]{
new Column("idxCol1", NativeTypes.INT32, true),
- new Column("idxCol2", NativeTypes.INT32, true),
+ new Column("idxCol2", NativeTypes.INT64, true),
new Column("val",
NativeTypes.stringOf(Integer.MAX_VALUE), true)
}
);
@@ -310,13 +346,13 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
Mockito.doReturn(hashIndexMock).when(indexMock).index();
Mockito.doReturn(indexDescriptor.columns()).when(indexMock).columns();
- validateIndexScan(tableData, schemaDescriptor, indexMock, key, key,
expRes);
+ validateIndexScan(schemaDescriptor, indexMock, key, key, expRes);
}
private void validateSortedIndexScan(
Object[][] tableData,
- Object[] lowerBound,
- Object[] upperBound,
+ Object @Nullable [] lowerBound,
+ Object @Nullable [] upperBound,
Object[][] expectedData
) {
SchemaDescriptor schemaDescriptor = new SchemaDescriptor(
@@ -324,7 +360,7 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
new Column[]{new Column("key", NativeTypes.INT64, false)},
new Column[]{
new Column("idxCol1", NativeTypes.INT32, true),
- new Column("idxCol2", NativeTypes.INT32, true),
+ new Column("idxCol2", NativeTypes.INT64, true),
new Column("val",
NativeTypes.stringOf(Integer.MAX_VALUE), true)
}
);
@@ -332,7 +368,7 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
SortedIndexDescriptor indexDescriptor = new SortedIndexDescriptor(
"IDX1",
List.of("idxCol2", "idxCol1"),
- List.of(ColumnCollation.ASC_NULLS_FIRST,
ColumnCollation.ASC_NULLS_LAST)
+ List.of(ColumnCollation.ASC_NULLS_LAST,
ColumnCollation.ASC_NULLS_LAST)
);
@@ -357,15 +393,14 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
Mockito.doReturn(sortedIndexMock).when(indexMock).index();
Mockito.doReturn(indexDescriptor.columns()).when(indexMock).columns();
- validateIndexScan(tableData, schemaDescriptor, indexMock, lowerBound,
upperBound, expectedData);
+ validateIndexScan(schemaDescriptor, indexMock, lowerBound, upperBound,
expectedData);
}
private void validateIndexScan(
- Object[][] tableData,
SchemaDescriptor schemaDescriptor,
IgniteIndex index,
- Object[] lowerBound,
- Object[] upperBound,
+ Object @Nullable [] lowerBound,
+ Object @Nullable [] upperBound,
Object[][] expectedData
) {
ExecutionContext<Object[]> ectx = executionContext(true);
@@ -388,17 +423,11 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
when(rangeIterable.iterator()).thenAnswer(inv ->
List.of(range).iterator());
}
- ImmutableIntList idxColMapping =
ImmutableIntList.of(index.columns().stream()
- .map(schemaDescriptor::column)
- .mapToInt(Column::schemaIndex)
- .toArray());
-
IndexScanNode<Object[]> scanNode = new IndexScanNode<>(
ectx,
ectx.rowHandler().factory(ectx.getTypeFactory(), rowType),
index,
new TestTable(rowType, schemaDescriptor),
- idxColMapping,
new int[]{0, 2},
index.type() == Type.SORTED ? comp : null,
rangeIterable,
@@ -423,7 +452,7 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
Builder rowTypeBuilder = new Builder(typeFactory);
IntStream.range(0, schemaDescriptor.length())
- .mapToObj(i -> schemaDescriptor.column(i))
+ .mapToObj(schemaDescriptor::column)
.forEach(col -> rowTypeBuilder.add(col.name(),
TypeUtils.native2relationalType(typeFactory, col.type(), col.nullable())));
return rowTypeBuilder.build();
@@ -515,9 +544,11 @@ public class IndexScanNodeExecutionTest extends
AbstractExecutionTest {
for (int i = 0; i < bound.count(); i++) {
Column col = schemaDescriptor.column(idxCols.get(i));
- Object val = bound.value(i);
+ Object val = bound.hasNullValue(i) ? null : bound.value(i);
+
+ if (val == null) {
+ assertThat("Unexpected null value: columnName" +
idxCols.get(i), col.nullable(), Matchers.is(Boolean.TRUE));
- if (col.nullable() && val == null) {
continue;
}
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
index cc12312b35..13945d9373 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
@@ -33,6 +33,7 @@ import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
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.exec.exp.RexImpTable;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
@@ -181,7 +182,7 @@ public class SortedIndexSpoolExecutionTest extends
AbstractExecutionTest {
RootRewindable<Object[]> root = new RootRewindable<>(ctx);
root.register(spool);
- Object x = ctx.unspecifiedValue(); // Unspecified filter value.
+ Object x = RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER; // Unspecified
filter value.
// Test tuple (lower, upper, expected result size).
List<TestParams> testBounds = Arrays.asList(
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
index 53bce77d6d..56b0f66cf1 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
@@ -102,12 +102,11 @@ public class CorrelatedNestedLoopJoinPlannerTest extends
AbstractPlannerTest {
List<SearchBounds> searchBounds = idxScan.searchBounds();
assertNotNull(searchBounds, "Invalid plan\n" +
RelOptUtil.toString(phys));
- assertEquals(3, searchBounds.size());
+ assertEquals(2, searchBounds.size());
- assertNull(searchBounds.get(0));
- assertTrue(searchBounds.get(1) instanceof ExactBounds);
- assertTrue(((ExactBounds) searchBounds.get(1)).bound() instanceof
RexFieldAccess);
- assertNull(searchBounds.get(2));
+ assertTrue(searchBounds.get(0) instanceof ExactBounds);
+ assertTrue(((ExactBounds) searchBounds.get(0)).bound() instanceof
RexFieldAccess);
+ assertNull(searchBounds.get(1));
}
/**
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java
index 600d46a7f4..0cbd5ea986 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/ProjectFilterScanMergePlannerTest.java
@@ -35,7 +35,7 @@ import
org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@@ -48,7 +48,7 @@ public class ProjectFilterScanMergePlannerTest extends
AbstractPlannerTest {
private IgniteSchema publicSchema;
/** {@inheritDoc} */
- @BeforeAll
+ @BeforeEach
public void setup() {
publicSchema = new IgniteSchema("PUBLIC");
@@ -87,7 +87,7 @@ public class ProjectFilterScanMergePlannerTest extends
AbstractPlannerTest {
}
@Test
- public void testProjectFilterMergeIndex() throws Exception {
+ public void testProjectFilterMergeSortedIndex() throws Exception {
// Test project and filter merge into index scan.
TestTable tbl = ((TestTable) publicSchema.getTable("TBL"));
tbl.addIndex(new
IgniteIndex(TestSortedIndex.create(RelCollations.of(2), "IDX_C", tbl)));
@@ -113,6 +113,33 @@ public class ProjectFilterScanMergePlannerTest extends
AbstractPlannerTest {
);
}
+ @Test
+ public void testProjectFilterMergeHashIndex() throws Exception {
+ // Test project and filter merge into index scan.
+ TestTable tbl = ((TestTable) publicSchema.getTable("TBL"));
+ tbl.addIndex(new IgniteIndex(TestHashIndex.create(List.of("c"),
"IDX_C")));
+
+ // Without index condition shift.
+ assertPlan("SELECT a, b FROM tbl WHERE c = 0", publicSchema,
isInstanceOf(IgniteIndexScan.class)
+ .and(scan -> scan.projects() != null)
+ .and(scan -> "[$t0, $t1]".equals(scan.projects().toString()))
+ .and(scan -> scan.condition() != null)
+ .and(scan -> "=($t2, 0)".equals(scan.condition().toString()))
+ .and(scan -> ImmutableBitSet.of(0, 1,
2).equals(scan.requiredColumns()))
+ .and(scan -> "[=($t2,
0)]".equals(searchBoundsCondition(scan.searchBounds()).toString()))
+ );
+
+ // Index condition shifted according to requiredColumns.
+ assertPlan("SELECT b FROM tbl WHERE c = 0", publicSchema,
isInstanceOf(IgniteIndexScan.class)
+ .and(scan -> scan.projects() != null)
+ .and(scan -> "[$t0]".equals(scan.projects().toString()))
+ .and(scan -> scan.condition() != null)
+ .and(scan -> "=($t1, 0)".equals(scan.condition().toString()))
+ .and(scan -> ImmutableBitSet.of(1,
2).equals(scan.requiredColumns()))
+ .and(scan -> "[=($t1,
0)]".equals(searchBoundsCondition(scan.searchBounds()).toString()))
+ );
+ }
+
@Test
public void testIdentityFilterMergeIndex() throws Exception {
// Test project and filter merge into index scan.
@@ -138,6 +165,31 @@ public class ProjectFilterScanMergePlannerTest extends
AbstractPlannerTest {
);
}
+ @Test
+ public void testIdentityFilterMergeHashIndex() throws Exception {
+ // Test project and filter merge into index scan.
+ TestTable tbl = ((TestTable) publicSchema.getTable("TBL"));
+ tbl.addIndex(new IgniteIndex(TestHashIndex.create(List.of("c"),
"IDX_C")));
+
+ // Without index condition shift.
+ assertPlan("SELECT a, b, c FROM tbl WHERE c = 0", publicSchema,
isInstanceOf(IgniteIndexScan.class)
+ .and(scan -> scan.projects() == null)
+ .and(scan -> scan.condition() != null)
+ .and(scan -> "=($t2, 0)".equals(scan.condition().toString()))
+ .and(scan -> ImmutableBitSet.of(0, 1,
2).equals(scan.requiredColumns()))
+ .and(scan -> "[=($t2,
0)]".equals(searchBoundsCondition(scan.searchBounds()).toString()))
+ );
+
+ // Index condition shift and identity.
+ assertPlan("SELECT b, c FROM tbl WHERE c = 0", publicSchema,
isInstanceOf(IgniteIndexScan.class)
+ .and(scan -> scan.projects() == null)
+ .and(scan -> scan.condition() != null)
+ .and(scan -> "=($t1, 0)".equals(scan.condition().toString()))
+ .and(scan -> ImmutableBitSet.of(1,
2).equals(scan.requiredColumns()))
+ .and(scan -> "[=($t1,
0)]".equals(searchBoundsCondition(scan.searchBounds()).toString()))
+ );
+ }
+
@Test
public void testProjectFilterProjectMerge() throws Exception {
// Inner query contains correlate, it prevents filter to be moved
below project, and after HEP_FILTER_PUSH_DOWN
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
index cebf23eda7..9f2496826a 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
@@ -106,12 +106,11 @@ public class SortedIndexSpoolPlannerTest extends
AbstractPlannerTest {
List<SearchBounds> searchBounds = idxSpool.searchBounds();
assertNotNull(searchBounds, "Invalid plan\n" +
RelOptUtil.toString(phys));
- assertEquals(3, searchBounds.size());
+ assertEquals(2, searchBounds.size());
- assertNull(searchBounds.get(0));
- assertTrue(searchBounds.get(1) instanceof ExactBounds);
- assertTrue(((ExactBounds) searchBounds.get(1)).bound() instanceof
RexFieldAccess);
- assertNull(searchBounds.get(2));
+ assertTrue(searchBounds.get(0) instanceof ExactBounds);
+ assertTrue(((ExactBounds) searchBounds.get(0)).bound() instanceof
RexFieldAccess);
+ assertNull(searchBounds.get(1));
}
/**
@@ -154,7 +153,7 @@ public class SortedIndexSpoolPlannerTest extends
AbstractPlannerTest {
return IgniteDistributions.affinity(0, "T1", "hash");
}
}
- .addIndex("t1_jid0_idx", 1, 0)
+ .addIndex("t1_jid0_idx", 2, 1)
);
String sql = "select * "
@@ -174,13 +173,12 @@ public class SortedIndexSpoolPlannerTest extends
AbstractPlannerTest {
List<SearchBounds> searchBounds = idxSpool.searchBounds();
assertNotNull(searchBounds, "Invalid plan\n" +
RelOptUtil.toString(phys));
- assertEquals(4, searchBounds.size());
+ assertEquals(2, searchBounds.size());
- assertNull(searchBounds.get(0));
+ assertTrue(searchBounds.get(0) instanceof ExactBounds);
+ assertTrue(((ExactBounds) searchBounds.get(0)).bound() instanceof
RexFieldAccess);
assertTrue(searchBounds.get(1) instanceof ExactBounds);
assertTrue(((ExactBounds) searchBounds.get(1)).bound() instanceof
RexFieldAccess);
- assertNull(searchBounds.get(2));
- assertNull(searchBounds.get(3));
}
/**
@@ -210,17 +208,15 @@ public class SortedIndexSpoolPlannerTest extends
AbstractPlannerTest {
// Condition is LESS_THEN, but we have
DESC field and condition should be in lower bound
// instead of upper bound.
assertNotNull(searchBounds);
- assertEquals(3, searchBounds.size());
+ assertEquals(1, searchBounds.size());
- assertNull(searchBounds.get(0));
- assertTrue(searchBounds.get(1) instanceof
RangeBounds);
- RangeBounds fld1Bounds = (RangeBounds)
searchBounds.get(1);
+ assertTrue(searchBounds.get(0) instanceof
RangeBounds);
+ RangeBounds fld1Bounds = (RangeBounds)
searchBounds.get(0);
assertTrue(fld1Bounds.lowerBound()
instanceof RexFieldAccess);
assertFalse(fld1Bounds.lowerInclude());
// NULLS LAST in collation, so nulls can
be skipped by upper bound.
assertTrue(((RexLiteral)
fld1Bounds.upperBound()).isNull());
assertFalse(fld1Bounds.upperInclude());
- assertNull(searchBounds.get(2));
return true;
})
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
index dbc0c2585f..5dbb8f0475 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
@@ -640,13 +640,13 @@ public class PartitionReplicaListener implements
ReplicaListener {
BinaryTuple key = request.exactKey();
- @SuppressWarnings("resource") Cursor<RowId> cursor = (Cursor<RowId>)
cursors.computeIfAbsent(cursorId,
+ Cursor<RowId> cursor = (Cursor<RowId>)
cursors.computeIfAbsent(cursorId,
id -> indexStorage.get(key));
final ArrayList<BinaryRow> result = new ArrayList<>(batchCount);
return continueReadOnlyIndexLookup(cursor, timestamp, batchCount,
result)
- .thenCompose(ignore ->
CompletableFuture.completedFuture(result));
+ .thenCompose(ignore -> completedFuture(result));
}
private CompletableFuture<List<BinaryRow>> lookupIndex(
@@ -666,14 +666,11 @@ public class PartitionReplicaListener implements
ReplicaListener {
return lockManager.acquire(txId, new LockKey(tableId),
LockMode.IS).thenCompose(tblLock -> { // Table IS lock
return lockManager.acquire(txId, new LockKey(indexId,
exactKey.byteBuffer()), LockMode.S)
.thenCompose(indRowLock -> { // Hash index bucket S
lock
- @SuppressWarnings("resource") Cursor<RowId> cursor
= (Cursor<RowId>) cursors.computeIfAbsent(cursorId,
- id -> {
- return indexStorage.get(exactKey);
- });
+ Cursor<RowId> cursor = (Cursor<RowId>)
cursors.computeIfAbsent(cursorId, id -> indexStorage.get(exactKey));
final ArrayList<BinaryRow> result = new
ArrayList<>(batchCount);
- return continueIndexLookup(txId, indexId, cursor,
batchCount, result)
+ return continueIndexLookup(txId, cursor,
batchCount, result)
.thenApply(ignore -> result);
});
});
@@ -705,7 +702,7 @@ public class PartitionReplicaListener implements
ReplicaListener {
return lockManager.acquire(txId, new LockKey(indexId),
LockMode.IS).thenCompose(idxLock -> { // Index IS lock
return lockManager.acquire(txId, new LockKey(tableId),
LockMode.IS).thenCompose(tblLock -> { // Table IS lock
- @SuppressWarnings("resource") Cursor<IndexRow> cursor =
(Cursor<IndexRow>) cursors.computeIfAbsent(cursorId,
+ Cursor<IndexRow> cursor = (Cursor<IndexRow>)
cursors.computeIfAbsent(cursorId,
id -> {
// TODO
https://issues.apache.org/jira/browse/IGNITE-18057
// Fix scan cursor return item closet to
lowerbound and <= lowerbound
@@ -714,7 +711,6 @@ public class PartitionReplicaListener implements
ReplicaListener {
lowerBound,
// We need upperBound next value for
correct range lock.
upperBound,
- // TODO IGNITE-18055: Add support
null-bounds.
flags
);
});
@@ -723,7 +719,7 @@ public class PartitionReplicaListener implements
ReplicaListener {
final ArrayList<BinaryRow> result = new
ArrayList<>(batchCount);
- return continueIndexScan(txId, indexId, indexLocker, cursor,
batchCount, result)
+ return continueIndexScan(txId, indexLocker, cursor,
batchCount, result)
.thenApply(ignore -> result);
});
});
@@ -751,29 +747,27 @@ public class PartitionReplicaListener implements
ReplicaListener {
int flags = request.flags();
- @SuppressWarnings("resource") Cursor<IndexRow> cursor =
(Cursor<IndexRow>) cursors.computeIfAbsent(cursorId,
- id -> {
- return indexStorage.scan(
- lowerBound,
- upperBound,
- flags
- );
- });
+ Cursor<IndexRow> cursor = (Cursor<IndexRow>)
cursors.computeIfAbsent(cursorId,
+ id -> indexStorage.scan(
+ lowerBound,
+ upperBound,
+ flags
+ ));
final ArrayList<BinaryRow> result = new ArrayList<>(batchCount);
return continueReadOnlyIndexScan(cursor, timestamp, batchCount, result)
- .thenCompose(ignore ->
CompletableFuture.completedFuture(result));
+ .thenCompose(ignore -> completedFuture(result));
}
- CompletableFuture<Void> continueReadOnlyIndexScan(
+ private CompletableFuture<Void> continueReadOnlyIndexScan(
Cursor<IndexRow> cursor,
HybridTimestamp timestamp,
int batchSize,
List<BinaryRow> result
) {
if (result.size() >= batchSize || !cursor.hasNext()) {
- return CompletableFuture.completedFuture(null);
+ return completedFuture(null);
}
IndexRow indexRow = cursor.next();
@@ -805,32 +799,30 @@ public class PartitionReplicaListener implements
ReplicaListener {
}
/**
- * Index scan loop. Retrives next row from index, takes locks, fetches
associated data row and collects to the result.
+ * Index scan loop. Retrieves next row from index, takes locks, fetches
associated data row and collects to the result.
*
* @param txId Transaction id.
- * @param indexId Index id.
* @param indexLocker Index locker.
* @param indexCursor Index cursor.
* @param batchSize Batch size.
* @param result Result collection.
* @return Future.
*/
- CompletableFuture<Void> continueIndexScan(
+ private CompletableFuture<Void> continueIndexScan(
UUID txId,
- UUID indexId,
SortedIndexLocker indexLocker,
Cursor<IndexRow> indexCursor,
int batchSize,
List<BinaryRow> result
) {
if (result.size() == batchSize) { // Batch is full, exit loop.
- return CompletableFuture.completedFuture(null);
+ return completedFuture(null);
}
return indexLocker.locksForScan(txId, indexCursor)
.thenCompose(currentRow -> { // Index row S lock
if (currentRow == null) {
- return CompletableFuture.completedFuture(null); // End
of range reached. Exit loop.
+ return completedFuture(null); // End of range reached.
Exit loop.
}
return lockManager.acquire(txId, new LockKey(tableId,
currentRow.rowId()), LockMode.S)
@@ -844,22 +836,21 @@ public class PartitionReplicaListener implements
ReplicaListener {
// Proceed scan.
return CompletableFuture.supplyAsync(
- () -> continueIndexScan(txId, indexId,
indexLocker, indexCursor, batchSize, result),
+ () -> continueIndexScan(txId,
indexLocker, indexCursor, batchSize, result),
scanRequestExecutor
).thenCompose(Function.identity());
});
});
}
- CompletableFuture<Void> continueIndexLookup(
+ private CompletableFuture<Void> continueIndexLookup(
UUID txId,
- UUID indexId,
Cursor<RowId> indexCursor,
int batchSize,
List<BinaryRow> result
) {
if (result.size() >= batchSize || !indexCursor.hasNext()) {
- return CompletableFuture.completedFuture(null);
+ return completedFuture(null);
}
RowId rowId = indexCursor.next();
@@ -875,20 +866,20 @@ public class PartitionReplicaListener implements
ReplicaListener {
// Proceed lookup.
return CompletableFuture.supplyAsync(
- () -> continueIndexLookup(txId, indexId,
indexCursor, batchSize, result),
+ () -> continueIndexLookup(txId, indexCursor,
batchSize, result),
scanRequestExecutor
).thenCompose(Function.identity());
});
}
- CompletableFuture<Void> continueReadOnlyIndexLookup(
+ private CompletableFuture<Void> continueReadOnlyIndexLookup(
Cursor<RowId> indexCursor,
HybridTimestamp timestamp,
int batchSize,
List<BinaryRow> result
) {
if (result.size() >= batchSize || !indexCursor.hasNext()) {
- return CompletableFuture.completedFuture(null);
+ return completedFuture(null);
}
RowId rowId = indexCursor.next();