You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2022/02/04 12:27:33 UTC
[ignite] branch sql-calcite updated: IGNITE-16443 Fix nulls processing in search rows - Fixes #9792.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch sql-calcite
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/sql-calcite by this push:
new 4757b3f IGNITE-16443 Fix nulls processing in search rows - Fixes #9792.
4757b3f is described below
commit 4757b3f468243fd049b61e453dd3e5c14a0a9547
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Fri Feb 4 15:15:15 2022 +0300
IGNITE-16443 Fix nulls processing in search rows - Fixes #9792.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../query/calcite/exec/ExecutionContext.java | 8 ++
.../processors/query/calcite/exec/IndexScan.java | 2 +-
.../query/calcite/exec/RuntimeHashIndex.java | 31 ++++-
.../query/calcite/exec/SystemViewScan.java | 2 +-
.../query/calcite/exec/exp/ExpressionFactory.java | 2 +-
.../calcite/exec/exp/ExpressionFactoryImpl.java | 152 ++++++++++++---------
.../query/calcite/util/IgniteMethod.java | 2 +
.../processors/query/calcite/util/RexUtils.java | 7 +-
.../calcite/exec/rel/AbstractExecutionTest.java | 15 ++
.../exec/rel/HashIndexSpoolExecutionTest.java | 60 ++++++--
.../exec/rel/SortedIndexSpoolExecutionTest.java | 81 +++++++++--
.../integration/HashSpoolIntegrationTest.java | 67 +++++++++
.../integration/IndexScanlIntegrationTest.java | 146 ++++++++++++++++++++
.../CorrelatedNestedLoopJoinPlannerTest.java | 10 +-
.../calcite/planner/HashIndexSpoolPlannerTest.java | 24 ++--
.../planner/SortedIndexSpoolPlannerTest.java | 22 ++-
.../ignite/testsuites/IntegrationTestSuite.java | 4 +
.../query/h2/opt/GridH2ValueCacheObject.java | 20 +--
18 files changed, 499 insertions(+), 156 deletions(-)
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
index dad750c..71fbeb6 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
@@ -49,6 +49,9 @@ import static org.apache.ignite.internal.processors.query.calcite.util.Commons.c
* Runtime context allowing access to the tables in a database.
*/
public class ExecutionContext<Row> extends AbstractQueryContext implements DataContext {
+ /** Placeholder for values, which expressions is not specified. */
+ private static final Object UNSPECIFIED_VALUE = new Object();
+
/** */
private final UUID qryId;
@@ -324,6 +327,11 @@ public class ExecutionContext<Row> extends AbstractQueryContext implements DataC
return cancelFlag.get();
}
+ /** */
+ public Object unspecifiedValue() {
+ return UNSPECIFIED_VALUE;
+ }
+
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
index b7f304f..419ff7b 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
@@ -183,7 +183,7 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
int fieldIdx = idxFieldMapping.getInt(i);
Object key = rowHnd.get(fieldIdx, bound);
- if (key != null) {
+ if (key != ectx.unspecifiedValue()) {
key = TypeUtils.fromInternal(ectx, key, fieldsStoreTypes[fieldIdx]);
keys[i] = IndexKeyFactory.wrap(key, idxRowHnd.indexKeyDefinitions().get(i).idxType(),
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeHashIndex.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeHashIndex.java
index 1666ba2..303103f 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeHashIndex.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeHashIndex.java
@@ -26,12 +26,19 @@ import java.util.function.Supplier;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.GroupKey;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
import org.jetbrains.annotations.NotNull;
/**
* Runtime hash index based on on-heap hash map.
*/
public class RuntimeHashIndex<Row> implements RuntimeIndex<Row> {
+ /**
+ * Placeholder for keys containing NULL values. Used to skip rows with such keys, since condition NULL=NULL
+ * should not satisfy the filter.
+ */
+ private static final GroupKey NULL_KEY = new GroupKey(X.EMPTY_OBJECT_ARRAY);
+
/** */
protected final ExecutionContext<Row> ectx;
@@ -58,7 +65,12 @@ public class RuntimeHashIndex<Row> implements RuntimeIndex<Row> {
/** {@inheritDoc} */
@Override public void push(Row r) {
- List<Row> eqRows = rows.computeIfAbsent(key(r), k -> new ArrayList<>());
+ GroupKey key = key(r);
+
+ if (key == NULL_KEY)
+ return;
+
+ List<Row> eqRows = rows.computeIfAbsent(key, k -> new ArrayList<>());
eqRows.add(r);
}
@@ -77,8 +89,14 @@ public class RuntimeHashIndex<Row> implements RuntimeIndex<Row> {
private GroupKey key(Row r) {
GroupKey.Builder b = GroupKey.builder(keys.cardinality());
- for (Integer field : keys)
- b.add(ectx.rowHandler().get(field, r));
+ for (Integer field : keys) {
+ Object fieldVal = ectx.rowHandler().get(field, r);
+
+ if (fieldVal == null)
+ return NULL_KEY;
+
+ b.add(fieldVal);
+ }
return b.build();
}
@@ -104,7 +122,12 @@ public class RuntimeHashIndex<Row> implements RuntimeIndex<Row> {
/** {@inheritDoc} */
@NotNull @Override public Iterator<Row> iterator() {
- List<Row> eqRows = rows.get(key(searchRow.get()));
+ GroupKey key = key(searchRow.get());
+
+ if (key == NULL_KEY)
+ return Collections.emptyIterator();
+
+ List<Row> eqRows = rows.get(key);
return eqRows == null ? Collections.emptyIterator() : eqRows.iterator();
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/SystemViewScan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/SystemViewScan.java
index 58f21c9..89392be 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/SystemViewScan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/SystemViewScan.java
@@ -118,7 +118,7 @@ public class SystemViewScan<Row, ViewRow> implements Iterable<Row> {
Object val = rowHnd.get(i, searchValues);
- if (val != null) {
+ if (val != ectx.unspecifiedValue()) {
if (filterMap == null)
filterMap = new HashMap<>();
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactory.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactory.java
index e52dad9..0893229 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactory.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactory.java
@@ -100,7 +100,7 @@ public interface ExpressionFactory<Row> {
/**
* Creates row from RexNodes.
*
- * @param values Values.
+ * @param values Values ({@code null} values allowed in this list).
* @return Row.
*/
Supplier<Row> rowSource(List<RexNode> values);
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
index 31c9fd5..b1ce953 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.query.calcite.exec.exp;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -28,8 +29,6 @@ import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Primitives;
@@ -56,6 +55,7 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.RowHandler;
@@ -67,7 +67,6 @@ import org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.Aggregat
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.calcite.util.IgniteMethod;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
import org.apache.ignite.internal.util.typedef.F;
@@ -92,6 +91,9 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
private final RelDataType emptyType;
/** */
+ private final RelDataType nullType;
+
+ /** */
private final ExecutionContext<Row> ctx;
/** */
@@ -107,6 +109,7 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
this.rexBuilder = rexBuilder;
emptyType = new RelDataTypeFactory.Builder(this.typeFactory).build();
+ nullType = typeFactory.createSqlType(SqlTypeName.NULL);
}
/** {@inheritDoc} */
@@ -125,12 +128,34 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
@Override public Comparator<Row> comparator(RelCollation collation) {
if (collation == null || collation.getFieldCollations().isEmpty())
return null;
- else if (collation.getFieldCollations().size() == 1)
- return comparator(collation.getFieldCollations().get(0));
- return Ordering.compound(collation.getFieldCollations()
- .stream()
- .map(this::comparator)
- .collect(Collectors.toList()));
+
+ return new Comparator<Row>() {
+ @Override public int compare(Row o1, Row o2) {
+ RowHandler<Row> hnd = ctx.rowHandler();
+ Object unspecifiedVal = ctx.unspecifiedValue();
+
+ for (RelFieldCollation field : collation.getFieldCollations()) {
+ int fieldIdx = field.getFieldIndex();
+ int nullComparison = field.nullDirection.nullComparison;
+
+ Object c1 = hnd.get(fieldIdx, o1);
+ Object c2 = hnd.get(fieldIdx, o2);
+
+ // If filter for some field is unspecified, assume equality for this field and all subsequent fields.
+ if (c1 == unspecifiedVal || c2 == unspecifiedVal)
+ return 0;
+
+ int res = (field.direction == RelFieldCollation.Direction.ASCENDING) ?
+ ExpressionFactoryImpl.compare(c1, c2, nullComparison) :
+ ExpressionFactoryImpl.compare(c2, c1, -nullComparison);
+
+ if (res != 0)
+ return res;
+ }
+
+ return 0;
+ }
+ };
}
/** {@inheritDoc} */
@@ -148,47 +173,6 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
}
/** */
- @SuppressWarnings("rawtypes")
- private Comparator<Row> comparator(RelFieldCollation fieldCollation) {
- final int nullComparison = fieldCollation.nullDirection.nullComparison;
- final int x = fieldCollation.getFieldIndex();
- RowHandler<Row> handler = ctx.rowHandler();
-
- if (fieldCollation.direction == RelFieldCollation.Direction.ASCENDING) {
- return (o1, o2) -> {
- Object obj1 = handler.get(x, o1);
- Object obj2 = handler.get(x, o2);
-
- boolean o1Comparable = obj1 instanceof Comparable;
- boolean o2Comparable = obj2 instanceof Comparable;
-
- if (o1Comparable && o2Comparable) {
- final Comparable c1 = (Comparable)obj1;
- final Comparable c2 = (Comparable)obj2;
- return RelFieldCollation.compare(c1, c2, nullComparison);
- }
- else {
- if (obj1 == obj2)
- return 0;
- else if (obj1 == null)
- return nullComparison;
- else if (obj2 == null)
- return -nullComparison;
- else
- return GridH2ValueCacheObject.compareHashOrBytes(obj1, obj2);
- }
- };
- }
-
- return (o1, o2) -> {
- final Comparable c1 = (Comparable)handler.get(x, o1);
- final Comparable c2 = (Comparable)handler.get(x, o2);
- return RelFieldCollation.compare(c2, c1, -nullComparison);
- };
- }
-
- /** */
- @SuppressWarnings("rawtypes")
private Comparator<Row> comparator(RelFieldCollation left, RelFieldCollation right) {
final int nullComparison = left.nullDirection.nullComparison;
@@ -204,19 +188,27 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
if (left.direction == RelFieldCollation.Direction.ASCENDING) {
return (o1, o2) -> {
- final Comparable c1 = (Comparable)handler.get(lIdx, o1);
- final Comparable c2 = (Comparable)handler.get(rIdx, o2);
- return RelFieldCollation.compare(c1, c2, nullComparison);
+ final Object c1 = handler.get(lIdx, o1);
+ final Object c2 = handler.get(rIdx, o2);
+ return compare(c1, c2, nullComparison);
};
}
return (o1, o2) -> {
- final Comparable c1 = (Comparable)handler.get(lIdx, o1);
- final Comparable c2 = (Comparable)handler.get(rIdx, o2);
- return RelFieldCollation.compare(c2, c1, -nullComparison);
+ final Object c1 = handler.get(lIdx, o1);
+ final Object c2 = handler.get(rIdx, o2);
+ return compare(c2, c1, -nullComparison);
};
}
+ /** */
+ @SuppressWarnings("rawtypes")
+ private static int compare(Object o1, Object o2, int nullComparison) {
+ final Comparable c1 = (Comparable)o1;
+ final Comparable c2 = (Comparable)o2;
+ return RelFieldCollation.compare(c1, c2, nullComparison);
+ }
+
/** {@inheritDoc} */
@Override public Predicate<Row> predicate(RexNode filter, RelDataType rowType) {
return new PredicateImpl(scalar(filter, rowType));
@@ -234,7 +226,8 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
/** {@inheritDoc} */
@Override public Supplier<Row> rowSource(List<RexNode> values) {
- return new ValuesImpl(scalar(values, null), ctx.rowHandler().factory(typeFactory, RexUtil.types(values)));
+ return new ValuesImpl(scalar(values, null), ctx.rowHandler().factory(typeFactory,
+ Commons.transform(values, v -> v != null ? v.getType() : nullType)));
}
/** {@inheritDoc} */
@@ -284,7 +277,7 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
/**
* Creates {@link SingleScalar}, a code-generated expressions evaluator.
*
- * @param nodes Expressions.
+ * @param nodes Expressions. {@code Null} expressions will be evaluated to {@link ExecutionContext#unspecifiedValue()}.
* @param type Row type.
* @return SingleScalar.
*/
@@ -308,14 +301,26 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
}
/** */
- private Scalar compile(Iterable<RexNode> nodes, RelDataType type, boolean biInParams) {
+ private Scalar compile(List<RexNode> nodes, RelDataType type, boolean biInParams) {
if (type == null)
type = emptyType;
RexProgramBuilder programBuilder = new RexProgramBuilder(type, rexBuilder);
- for (RexNode node : nodes)
- programBuilder.addProject(node, null);
+ BitSet unspecifiedValues = new BitSet(nodes.size());
+
+ for (int i = 0; i < nodes.size(); i++) {
+ RexNode node = nodes.get(i);
+
+ if (node != null)
+ programBuilder.addProject(node, null);
+ else {
+ unspecifiedValues.set(i);
+
+ programBuilder.addProject(rexBuilder.makeNullLiteral(type == emptyType ?
+ nullType : type.getFieldList().get(i).getType()), null);
+ }
+ }
RexProgram program = programBuilder.getProgram();
@@ -348,12 +353,17 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
List<Expression> projects = RexToLixTranslator.translateProjects(program, typeFactory, conformance,
builder, null, ctx_, inputGetter, correlates);
+ 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);
+
builder.add(
Expressions.statement(
Expressions.call(hnd_,
IgniteMethod.ROW_HANDLER_SET.method(),
- Expressions.constant(i), out_, projects.get(i))));
+ Expressions.constant(i), out_, val)));
}
String methodName = biInParams ? IgniteMethod.BI_SCALAR_EXECUTE.method().getName() :
@@ -381,9 +391,15 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
if (i > 0)
b.append(';');
- b.append(nodes.get(i));
+ RexNode node = nodes.get(i);
+
+ b.append(node);
+
+ if (node == null)
+ continue;
+
b.append(':');
- b.append(nodes.get(i).getType().getFullTypeString());
+ b.append(node.getType().getFullTypeString());
new RexShuttle() {
@Override public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
@@ -391,7 +407,7 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
return super.visitFieldAccess(fieldAccess);
}
- }.apply(nodes.get(i));
+ }.apply(node);
}
b.append(", biParam=").append(biParam);
@@ -635,8 +651,10 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
/** */
public Function1<String, InputGetter> build(Iterable<RexNode> nodes) {
try {
- for (RexNode node : nodes)
- node.accept(this);
+ for (RexNode node : nodes) {
+ if (node != null)
+ node.accept(this);
+ }
return correlates == null ? null : correlates::get;
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMethod.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMethod.java
index 94a3592..b1ac6cd 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMethod.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteMethod.java
@@ -39,6 +39,8 @@ public enum IgniteMethod {
Object.class, Object.class),
/** 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),
/** See {@link SingleScalar#execute(ExecutionContext, Object, Object)} */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
index 23a1d74..759d125 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
@@ -407,11 +407,6 @@ public class RexUtils {
}
/** */
- private static List<RexNode> makeListOfNullLiterals(RexBuilder builder, List<RelDataType> types) {
- return Commons.transform(types, builder::makeNullLiteral);
- }
-
- /** */
public static boolean isNotNull(RexNode op) {
if (op == null)
return false;
@@ -427,7 +422,7 @@ public class RexUtils {
RexBuilder builder = builder(cluster);
List<RelDataType> types = RelOptUtil.getFieldTypeList(rowType);
- List<RexNode> res = makeListOfNullLiterals(builder, types);
+ List<RexNode> res = Arrays.asList(new RexNode[types.size()]);
for (RexNode pred : idxCond) {
assert pred instanceof RexCall;
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java
index 6647016..1766dac 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java
@@ -443,6 +443,21 @@ public class AbstractExecutionTest extends GridCommonAbstractTest {
}
/** */
+ public int rowsCount() {
+ int cnt = 0;
+
+ while (hasNext()) {
+ next();
+
+ cnt++;
+ }
+
+ rewind();
+
+ return cnt;
+ }
+
+ /** */
public void closeRewindableRoot() {
super.closeInternal();
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/HashIndexSpoolExecutionTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/HashIndexSpoolExecutionTest.java
index be17583..9b84f4f 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/HashIndexSpoolExecutionTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/HashIndexSpoolExecutionTest.java
@@ -19,9 +19,9 @@ package org.apache.ignite.internal.processors.query.calcite.exec.rel;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
-
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
@@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactor
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.lang.GridTuple3;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.jetbrains.annotations.NotNull;
@@ -129,25 +130,56 @@ public class HashIndexSpoolExecutionTest extends AbstractExecutionTest {
testFilter.delegate = bound.get1();
System.arraycopy(bound.get2(), 0, searchRow, 0, searchRow.length);
- int cnt = 0;
+ assertEquals("Invalid result size", (int)bound.get3(), root.rowsCount());
+ }
- while (root.hasNext()) {
- root.next();
+ root.closeRewindableRoot();
+ }
+ }
+ }
- cnt++;
- }
+ /** */
+ @Test
+ public void testNullsInSearchRow() {
+ ExecutionContext<Object[]> ctx = executionContext(F.first(nodes()), UUID.randomUUID(), 0);
+ RelDataType rowType = TypeUtils.createRowType(ctx.getTypeFactory(), int.class, int.class);
- assertEquals(
- "Invalid result size",
- (int)bound.get3(),
- cnt);
+ ScanNode<Object[]> scan = new ScanNode<>(ctx, rowType, new TestTable(
+ 4,
+ rowType,
+ (rowId) -> (rowId & 1) == 0 ? null : 1,
+ (rowId) -> (rowId & 2) == 0 ? null : 1
+ ));
- root.rewind();
- }
+ Object[] searchRow = new Object[2];
- root.closeRewindableRoot();
- }
+ IndexSpoolNode<Object[]> spool = IndexSpoolNode.createHashSpool(
+ ctx,
+ rowType,
+ ImmutableBitSet.of(0, 1),
+ () -> searchRow
+ );
+
+ spool.register(scan);
+
+ RootRewindable<Object[]> root = new RootRewindable<>(ctx, rowType);
+
+ root.register(spool);
+
+ List<T2<Object[], Integer>> testBounds = F.asList(
+ new T2<>(new Object[] {null, null}, 0),
+ new T2<>(new Object[] {1, null}, 0),
+ new T2<>(new Object[] {null, 1}, 0),
+ new T2<>(new Object[] {1, 1}, 1)
+ );
+
+ for (T2<Object[], Integer> bound : testBounds) {
+ System.arraycopy(bound.get1(), 0, searchRow, 0, searchRow.length);
+
+ assertEquals("Invalid result size", (int)bound.get2(), root.rowsCount());
}
+
+ root.closeRewindableRoot();
}
/** */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortedIndexSpoolExecutionTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortedIndexSpoolExecutionTest.java
index 736418e..866af24 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortedIndexSpoolExecutionTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortedIndexSpoolExecutionTest.java
@@ -19,9 +19,10 @@ package org.apache.ignite.internal.processors.query.calcite.exec.rel;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
-
+import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableIntList;
@@ -30,6 +31,7 @@ import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactor
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.lang.GridTuple4;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T3;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.jetbrains.annotations.NotNull;
@@ -155,20 +157,7 @@ public class SortedIndexSpoolExecutionTest extends AbstractExecutionTest {
System.arraycopy(bound.get2(), 0, lower, 0, lower.length);
System.arraycopy(bound.get3(), 0, upper, 0, upper.length);
- int cnt = 0;
-
- while (root.hasNext()) {
- root.next();
-
- cnt++;
- }
-
- assertEquals(
- "Invalid result size",
- (int)bound.get4(),
- cnt);
-
- root.rewind();
+ assertEquals("Invalid result size", (int)bound.get4(), root.rowsCount());
}
root.closeRewindableRoot();
@@ -176,6 +165,68 @@ public class SortedIndexSpoolExecutionTest extends AbstractExecutionTest {
}
/** */
+ @Test
+ public void testUnspecifiedValuesInSearchRow() {
+ ExecutionContext<Object[]> ctx = executionContext(F.first(nodes()), UUID.randomUUID(), 0);
+ IgniteTypeFactory tf = ctx.getTypeFactory();
+ RelDataType rowType = TypeUtils.createRowType(tf, int.class, String.class, int.class);
+
+ ScanNode<Object[]> scan = new ScanNode<>(
+ ctx,
+ rowType,
+ new TestTable(100, rowType, rowId -> rowId / 10, rowId -> rowId % 10, rowId -> rowId)
+ );
+
+ Object[] lower = new Object[3];
+ Object[] upper = new Object[3];
+
+ RelCollation collation = RelCollations.of(ImmutableIntList.of(0, 1));
+
+ IndexSpoolNode<Object[]> spool = IndexSpoolNode.createTreeSpool(
+ ctx,
+ rowType,
+ collation,
+ ctx.expressionFactory().comparator(collation),
+ v -> true,
+ () -> lower,
+ () -> upper
+ );
+
+ spool.register(scan);
+
+ RootRewindable<Object[]> root = new RootRewindable<>(ctx, rowType);
+ root.register(spool);
+
+ Object x = ctx.unspecifiedValue(); // Unspecified filter value.
+
+ // Test tuple (lower, upper, expected result size).
+ List<T3<Object[], Object[], Integer>> testBounds = F.asList(
+ new T3<>(new Object[] {x, x, x}, new Object[] {x, x, x}, 100),
+ new T3<>(new Object[] {0, 0, x}, new Object[] {4, 9, x}, 50),
+ new T3<>(new Object[] {0, x, x}, new Object[] {4, 9, x}, 50),
+ new T3<>(new Object[] {0, 0, x}, new Object[] {4, x, x}, 50),
+ new T3<>(new Object[] {4, x, x}, new Object[] {4, x, x}, 10),
+ // This is a special case, we shouldn't compare the next field if current field bound value is null, or we
+ // can accidentally find wrong lower/upper row. So, {x, 4} bound must be converted to {x, x} and redunant
+ // rows must be filtered out by predicate.
+ new T3<>(new Object[] {x, 4, x}, new Object[] {x, 5, x}, 100)
+ );
+
+ for (T3<Object[], Object[], Integer> bound : testBounds) {
+ log.info("Check: lowerBound=" + Arrays.toString(bound.get1()) +
+ ", upperBound=" + Arrays.toString(bound.get2()));
+
+ // Set up bounds.
+ System.arraycopy(bound.get1(), 0, lower, 0, lower.length);
+ System.arraycopy(bound.get2(), 0, upper, 0, upper.length);
+
+ assertEquals("Invalid result size", (int)bound.get3(), root.rowsCount());
+ }
+
+ root.closeRewindableRoot();
+ }
+
+ /** */
static class TestPredicate implements Predicate<Object[]> {
/** */
Predicate<Object[]> delegate;
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/HashSpoolIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/HashSpoolIntegrationTest.java
new file mode 100644
index 0000000..cca21ef
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/HashSpoolIntegrationTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.integration;
+
+import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
+import org.junit.Test;
+
+/**
+ * Hash spool test.
+ */
+public class HashSpoolIntegrationTest extends AbstractBasicIntegrationTest {
+ /** */
+ @Test
+ public void testNullsInSearchRow() {
+ executeSql("CREATE TABLE t(i1 INTEGER, i2 INTEGER)");
+ executeSql("INSERT INTO t VALUES (null, 0), (1, 1), (2, 2), (3, null)");
+
+ assertQuery("SELECT i1, (SELECT i2 FROM t WHERE i1=t1.i1) FROM t t1")
+ .matches(QueryChecker.containsSubPlan("IgniteHashIndexSpool"))
+ .returns(null, null)
+ .returns(1, 1)
+ .returns(2, 2)
+ .returns(3, null)
+ .check();
+
+ assertQuery("SELECT (SELECT i1 FROM t WHERE i2=t1.i2), i2 FROM t t1")
+ .matches(QueryChecker.containsSubPlan("IgniteHashIndexSpool"))
+ .returns(null, 0)
+ .returns(1, 1)
+ .returns(2, 2)
+ .returns(null, null)
+ .check();
+ }
+
+ /** */
+ @Test
+ public void testNullsInSearchRowMultipleColumns() {
+ executeSql("CREATE TABLE t0(i1 INTEGER, i2 INTEGER)");
+ executeSql("CREATE TABLE t1(i1 INTEGER, i2 INTEGER)");
+ executeSql("INSERT INTO t0 VALUES (null, 0), (1, null), (null, 2), (3, null), (1, 1)");
+ executeSql("INSERT INTO t1 VALUES (null, 0), (null, 1), (2, null), (3, null), (1, 1)");
+
+ String sql = "SELECT /*+ DISABLE_RULE ('MergeJoinConverter', 'NestedLoopJoinConverter', " +
+ "'FilterSpoolMergeToSortedIndexSpoolRule')*/ * " +
+ "FROM t0 JOIN t1 ON t0.i1=t1.i1 AND t0.i2=t1.i2";
+
+ assertQuery(sql)
+ .matches(QueryChecker.containsSubPlan("IgniteHashIndexSpool"))
+ .returns(1, 1, 1, 1)
+ .check();
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java
new file mode 100644
index 0000000..97056f3
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.integration;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
+import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
+import org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
+import org.apache.ignite.internal.processors.query.calcite.rel.logical.IgniteLogicalIndexScan;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteIndex;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import org.apache.ignite.internal.processors.query.calcite.util.IndexConditions;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+/**
+ * Index scan test.
+ */
+public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
+ /** {@inheritDoc} */
+ @Override protected int nodeCount() {
+ return 1;
+ }
+
+ /** */
+ @Test
+ public void testNullsInSearchRow() {
+ executeSql("CREATE TABLE t(i1 INTEGER, i2 INTEGER) WITH TEMPLATE=REPLICATED ");
+ executeSql("INSERT INTO t VALUES (0, null), (1, null), (2, 2), (3, null), (4, null)");
+ executeSql("CREATE INDEX t_idx ON t(i1)");
+
+ IgniteTable tbl = (IgniteTable)queryProcessor(grid(0)).schemaHolder().schema("PUBLIC").getTable("T");
+ IgniteIndex idxOld = tbl.getIndex("T_IDX");
+ TestIgniteIndex idxNew = new TestIgniteIndex(idxOld);
+
+ tbl.addIndex(idxNew);
+
+ String sql = "SELECT /*+ DISABLE_RULE('NestedLoopJoinConverter', 'MergeJoinConverter') */ t1.i1, t2.i1 " +
+ "FROM t t1 " +
+ "LEFT JOIN t t2 ON t1.i2 = t2.i1";
+
+ assertQuery(sql)
+ .matches(QueryChecker.containsSubPlan("IgniteCorrelatedNestedLoopJoin"))
+ .matches(QueryChecker.containsIndexScan("PUBLIC", "T", "T_IDX"))
+ .returns(0, null)
+ .returns(1, null)
+ .returns(2, 2)
+ .returns(3, null)
+ .returns(4, null)
+ .check();
+
+ // There shouldn't be full index scan in case of null values in search row, only one value must be found by
+ // range scan and passed to predicate.
+ assertEquals(1, idxNew.filteredRows);
+ }
+
+ /** */
+ private static class TestIgniteIndex implements IgniteIndex {
+ /** */
+ private final IgniteIndex delegate;
+
+ /** */
+ private int filteredRows;
+
+ /** */
+ public TestIgniteIndex(IgniteIndex delegate) {
+ this.delegate = delegate;
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelCollation collation() {
+ return delegate.collation();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return delegate.name();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteTable table() {
+ return delegate.table();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteLogicalIndexScan toRel(
+ RelOptCluster cluster,
+ RelOptTable relOptTbl,
+ @Nullable List<RexNode> proj,
+ @Nullable RexNode cond,
+ @Nullable ImmutableBitSet requiredColumns
+ ) {
+ return delegate.toRel(cluster, relOptTbl, proj, cond, requiredColumns);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IndexConditions toIndexCondition(
+ RelOptCluster cluster,
+ @Nullable RexNode cond,
+ @Nullable ImmutableBitSet requiredColumns
+ ) {
+ return delegate.toIndexCondition(cluster, cond, requiredColumns);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ ColocationGroup grp,
+ Predicate<Row> filters,
+ Supplier<Row> lowerIdxConditions,
+ Supplier<Row> upperIdxConditions,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColumns
+ ) {
+ Predicate<Row> filter = row -> { filteredRows++; return true; };
+
+ filters = filter.and(filters);
+
+ return delegate.scan(execCtx, grp, filters, lowerIdxConditions, upperIdxConditions, rowTransformer,
+ requiredColumns);
+ }
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/CorrelatedNestedLoopJoinPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/CorrelatedNestedLoopJoinPlannerTest.java
index e735993..570fd8d 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/CorrelatedNestedLoopJoinPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/CorrelatedNestedLoopJoinPlannerTest.java
@@ -18,12 +18,10 @@
package org.apache.ignite.internal.processors.query.calcite.planner;
import java.util.List;
-
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexFieldAccess;
-import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
@@ -102,18 +100,18 @@ public class CorrelatedNestedLoopJoinPlannerTest extends AbstractPlannerTest {
assertNotNull("Invalid plan\n" + RelOptUtil.toString(phys), lBound);
assertEquals(3, lBound.size());
- assertTrue(((RexLiteral)lBound.get(0)).isNull());
- assertTrue(((RexLiteral)lBound.get(2)).isNull());
+ assertNull(lBound.get(0));
assertTrue(lBound.get(1) instanceof RexFieldAccess);
+ assertNull(lBound.get(2));
List<RexNode> uBound = idxScan.upperBound();
assertNotNull("Invalid plan\n" + RelOptUtil.toString(phys), uBound);
assertEquals(3, uBound.size());
- assertTrue(((RexLiteral)uBound.get(0)).isNull());
- assertTrue(((RexLiteral)uBound.get(2)).isNull());
+ assertNull(uBound.get(0));
assertTrue(uBound.get(1) instanceof RexFieldAccess);
+ assertNull(uBound.get(2));
}
/**
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java
index fa33898..ffa91b3 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java
@@ -18,12 +18,10 @@
package org.apache.ignite.internal.processors.query.calcite.planner;
import java.util.List;
-
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexFieldAccess;
-import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteHashIndexSpool;
@@ -101,9 +99,9 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
assertNotNull(searchRow);
assertEquals(3, searchRow.size());
- assertTrue(((RexLiteral)searchRow.get(0)).isNull());
- assertTrue(((RexLiteral)searchRow.get(2)).isNull());
+ assertNull(searchRow.get(0));
assertTrue(searchRow.get(1) instanceof RexFieldAccess);
+ assertNull(searchRow.get(2));
}
/** */
@@ -159,15 +157,15 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
IgniteHashIndexSpool idxSpool = findFirstNode(phys, byClass(IgniteHashIndexSpool.class));
- List<RexNode> searcRow = idxSpool.searchRow();
+ List<RexNode> searchRow = idxSpool.searchRow();
- assertNotNull(searcRow);
- assertEquals(4, searcRow.size());
+ assertNotNull(searchRow);
+ assertEquals(4, searchRow.size());
- assertTrue(((RexLiteral)searcRow.get(0)).isNull());
- assertTrue(searcRow.get(1) instanceof RexFieldAccess);
- assertTrue(searcRow.get(2) instanceof RexFieldAccess);
- assertTrue(((RexLiteral)searcRow.get(3)).isNull());
+ assertNull(searchRow.get(0));
+ assertTrue(searchRow.get(1) instanceof RexFieldAccess);
+ assertTrue(searchRow.get(2) instanceof RexFieldAccess);
+ assertNull(searchRow.get(3));
}
/**
@@ -227,8 +225,8 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
assertNotNull(searchRow);
assertEquals(3, searchRow.size());
- assertTrue(((RexLiteral)searchRow.get(0)).isNull());
- assertTrue(((RexLiteral)searchRow.get(2)).isNull());
+ assertNull(searchRow.get(0));
assertTrue(searchRow.get(1) instanceof RexFieldAccess);
+ assertNull(searchRow.get(2));
}
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/SortedIndexSpoolPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/SortedIndexSpoolPlannerTest.java
index f6840b2..1a1bfa4 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/SortedIndexSpoolPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/SortedIndexSpoolPlannerTest.java
@@ -18,12 +18,10 @@
package org.apache.ignite.internal.processors.query.calcite.planner;
import java.util.List;
-
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexFieldAccess;
-import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
@@ -99,18 +97,18 @@ public class SortedIndexSpoolPlannerTest extends AbstractPlannerTest {
assertNotNull(lBound);
assertEquals(3, lBound.size());
- assertTrue(((RexLiteral)lBound.get(0)).isNull());
- assertTrue(((RexLiteral)lBound.get(2)).isNull());
+ assertNull(lBound.get(0));
assertTrue(lBound.get(1) instanceof RexFieldAccess);
+ assertNull(lBound.get(2));
List<RexNode> uBound = idxSpool.indexCondition().upperBound();
assertNotNull(uBound);
assertEquals(3, uBound.size());
- assertTrue(((RexLiteral)uBound.get(0)).isNull());
- assertTrue(((RexLiteral)uBound.get(2)).isNull());
+ assertNull(uBound.get(0));
assertTrue(uBound.get(1) instanceof RexFieldAccess);
+ assertNull(uBound.get(2));
}
/**
@@ -176,19 +174,19 @@ public class SortedIndexSpoolPlannerTest extends AbstractPlannerTest {
assertNotNull(lBound);
assertEquals(4, lBound.size());
- assertTrue(((RexLiteral)lBound.get(0)).isNull());
- assertTrue(((RexLiteral)lBound.get(2)).isNull());
- assertTrue(((RexLiteral)lBound.get(3)).isNull());
+ assertNull(lBound.get(0));
assertTrue(lBound.get(1) instanceof RexFieldAccess);
+ assertNull(lBound.get(2));
+ assertNull(lBound.get(3));
List<RexNode> uBound = idxSpool.indexCondition().upperBound();
assertNotNull(uBound);
assertEquals(4, uBound.size());
- assertTrue(((RexLiteral)uBound.get(0)).isNull());
- assertTrue(((RexLiteral)lBound.get(2)).isNull());
- assertTrue(((RexLiteral)lBound.get(3)).isNull());
+ assertNull(uBound.get(0));
assertTrue(uBound.get(1) instanceof RexFieldAccess);
+ assertNull(uBound.get(2));
+ assertNull(uBound.get(3));
}
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index c20a195..ef18e1b 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -30,7 +30,9 @@ import org.apache.ignite.internal.processors.query.calcite.integration.Aggregate
import org.apache.ignite.internal.processors.query.calcite.integration.CalciteBasicSecondaryIndexIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.CalciteErrorHandlilngIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.CorrelatesIntegrationTest;
+import org.apache.ignite.internal.processors.query.calcite.integration.HashSpoolIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.IndexDdlIntegrationTest;
+import org.apache.ignite.internal.processors.query.calcite.integration.IndexScanlIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.IndexSpoolIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.IntervalTest;
import org.apache.ignite.internal.processors.query.calcite.integration.JoinIntegrationTest;
@@ -82,6 +84,8 @@ import org.junit.runners.Suite;
TableDmlIntegrationTest.class,
DataTypesTest.class,
IndexSpoolIntegrationTest.class,
+ HashSpoolIntegrationTest.class,
+ IndexScanlIntegrationTest.class,
SetOpIntegrationTest.class,
UnstableTopologyTest.class,
JoinCommuteRulesTest.class,
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
index 295ffb8..5b1dfcd 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.query.h2.opt;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
-import java.util.Comparator;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.binary.BinaryObjectImpl;
import org.apache.ignite.internal.processors.cache.CacheObject;
@@ -163,26 +162,15 @@ public class GridH2ValueCacheObject extends Value {
return o1.getClass().getName().compareTo(o2.getClass().getName());
}
- return compareHashOrBytes(this, v, (v1, v2) -> Bits.compareNotNullSigned(((Value)v1).getBytesNoCopy(),
- ((Value)v2).getBytesNoCopy()));
- }
-
- /** Compare hash codes. */
- public static int compareHashOrBytes(Object o1, Object o2) {
- return compareHashOrBytes(o1, o2, null);
- }
-
- /** Compare hash codes. */
- private static int compareHashOrBytes(Object o1, Object o2, Comparator<Object> comp) {
- int h1 = o1.hashCode();
- int h2 = o2.hashCode();
+ // Compare hash codes.
+ int h1 = hashCode();
+ int h2 = v.hashCode();
if (h1 == h2) {
if (o1.equals(o2))
return 0;
- return comp == null ? Bits.compareNotNullSigned(JdbcUtils.serialize(o1, null),
- JdbcUtils.serialize(o2, null)) : comp.compare(o1, o2);
+ return Bits.compareNotNullSigned(getBytesNoCopy(), v.getBytesNoCopy());
}
return h1 > h2 ? 1 : -1;