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/12/12 14:57:12 UTC
[ignite] branch master updated: IGNITE-13919 SQL Calcite: Optimization for COUNT(column) if column is indexed - Fixes #10390.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new b303f3641dc IGNITE-13919 SQL Calcite: Optimization for COUNT(column) if column is indexed - Fixes #10390.
b303f3641dc is described below
commit b303f3641dc43be3aef837aab5765c29ebb23714
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Mon Dec 12 17:54:22 2022 +0300
IGNITE-13919 SQL Calcite: Optimization for COUNT(column) if column is indexed - Fixes #10390.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../query/calcite/exec/IndexFirstLastScan.java | 37 +--------------
.../processors/query/calcite/exec/IndexScan.java | 29 ++++++++++++
.../query/calcite/exec/LogicalRelImplementor.java | 2 +-
.../query/calcite/rel/IgniteIndexCount.java | 28 ++++++++---
.../query/calcite/rule/IndexCountRule.java | 54 ++++++++++++++++++----
.../query/calcite/schema/CacheIndexImpl.java | 43 ++++++++++++++++-
.../query/calcite/schema/IgniteIndex.java | 3 +-
.../query/calcite/schema/SystemViewIndexImpl.java | 4 +-
.../calcite/exec/LogicalRelImplementorTest.java | 2 +-
.../integration/AbstractBasicIntegrationTest.java | 4 +-
.../integration/AggregatesIntegrationTest.java | 36 ++++++++++++++-
.../calcite/planner/HashAggregatePlannerTest.java | 7 ++-
.../index/sorted/inline/InlineIndexKeyType.java | 19 ++++++--
.../inline/types/NullableInlineIndexKeyType.java | 21 ++++++---
.../inline/types/StringInlineIndexKeyType.java | 3 +-
.../types/StringNoCompareInlineIndexKeyType.java | 3 +-
16 files changed, 218 insertions(+), 77 deletions(-)
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexFirstLastScan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexFirstLastScan.java
index aacada64133..0034c39f46a 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexFirstLastScan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexFirstLastScan.java
@@ -16,24 +16,15 @@
*/
package org.apache.ignite.internal.processors.query.calcite.exec;
-import java.util.List;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
import org.apache.ignite.internal.cache.query.index.sorted.inline.IndexQueryContext;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexImpl;
-import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyType;
-import org.apache.ignite.internal.cache.query.index.sorted.inline.io.InlineIO;
-import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
-import org.apache.ignite.internal.cache.query.index.sorted.keys.NullIndexKey;
-import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDescriptor;
import org.apache.ignite.internal.util.lang.GridCursor;
-import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.Nullable;
/**
@@ -66,35 +57,9 @@ public class IndexFirstLastScan<Row> extends IndexScan<Row> {
@Override protected IndexQueryContext indexQueryContext() {
IndexQueryContext res = super.indexQueryContext();
- BPlusTree.TreeRowClosure<IndexRow, IndexRow> f = res.rowFilter();
-
- List<InlineIndexKeyType> inlineKeyTypes = idx.segment(0).rowHandler().inlineIndexKeyTypes();
-
- InlineIndexKeyType keyType = F.isEmpty(inlineKeyTypes) ? null : inlineKeyTypes.get(0);
-
return new IndexQueryContext(
res.cacheFilter(),
- new BPlusTree.TreeRowClosure<IndexRow, IndexRow>() {
- /** {@inheritDoc} */
- @Override public boolean apply(
- BPlusTree<IndexRow, IndexRow> tree,
- BPlusIO<IndexRow> io,
- long pageAddr,
- int idx
- ) throws IgniteCheckedException {
- if (f != null && !f.apply(tree, io, pageAddr, idx))
- return false;
-
- if (keyType != null && io instanceof InlineIO) {
- IndexKey key = keyType.get(pageAddr, io.offset(idx), ((InlineIO)io).inlineSize());
-
- if (key != null)
- return key != NullIndexKey.INSTANCE;
- }
-
- return io.getLookupRow(tree, pageAddr, idx).key(0).type() != IndexKeyType.NULL;
- }
- },
+ createNotNullRowFilter(idx),
res.mvccSnapshot()
);
}
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 49a54d10199..1ad6a91cdcb 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
@@ -55,6 +55,7 @@ import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDesc
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.apache.ignite.spi.indexing.IndexingQueryFilterImpl;
import org.jetbrains.annotations.Nullable;
@@ -440,6 +441,34 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
}
}
+ /**
+ * Creates row filter to skip null values in the first index column.
+ */
+ public static BPlusTree.TreeRowClosure<IndexRow, IndexRow> createNotNullRowFilter(InlineIndex idx) {
+ List<InlineIndexKeyType> inlineKeyTypes = idx.segment(0).rowHandler().inlineIndexKeyTypes();
+
+ InlineIndexKeyType keyType = F.isEmpty(inlineKeyTypes) ? null : inlineKeyTypes.get(0);
+
+ return new BPlusTree.TreeRowClosure<IndexRow, IndexRow>() {
+ /** {@inheritDoc} */
+ @Override public boolean apply(
+ BPlusTree<IndexRow, IndexRow> tree,
+ BPlusIO<IndexRow> io,
+ long pageAddr,
+ int idx
+ ) throws IgniteCheckedException {
+ if (keyType != null && io instanceof InlineIO) {
+ Boolean keyIsNull = keyType.isNull(pageAddr, io.offset(idx), ((InlineIO)io).inlineSize());
+
+ if (keyIsNull != null)
+ return !keyIsNull;
+ }
+
+ return io.getLookupRow(tree, pageAddr, idx).key(0).type() != IndexKeyType.NULL;
+ }
+ };
+ }
+
/** */
protected static class TreeIndexWrapper implements TreeIndex<IndexRow> {
/** Underlying index. */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
index 98bc94a8285..07dc8c645f1 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
@@ -408,7 +408,7 @@ public class LogicalRelImplementor<Row> implements IgniteRelVisitor<Node<Row>> {
if (idx != null && !tbl.isIndexRebuildInProgress()) {
return new ScanNode<>(ctx, rel.getRowType(), () -> Collections.singletonList(ctx.rowHandler()
.factory(ctx.getTypeFactory(), rel.getRowType())
- .create(idx.count(ctx, ctx.group(rel.sourceId())))).iterator());
+ .create(idx.count(ctx, ctx.group(rel.sourceId()), rel.notNull()))).iterator());
}
else {
CollectNode<Row> replacement = CollectNode.createCountCollector(ctx);
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexCount.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexCount.java
index a9bb03e5527..932768df877 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexCount.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexCount.java
@@ -49,6 +49,9 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
/** */
private final long sourceId;
+ /** */
+ private final boolean notNull;
+
/**
* Constructor for deserialization.
*
@@ -65,6 +68,8 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
sourceId = ((Number)srcIdObj).longValue();
else
sourceId = -1L;
+
+ notNull = input.getBoolean("notNull", false);
}
/**
@@ -74,14 +79,16 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
* @param traits Traits of this relational expression.
* @param tbl Table definition.
* @param idxName Index name.
+ * @param notNull Count only not-null values.
*/
public IgniteIndexCount(
RelOptCluster cluster,
RelTraitSet traits,
RelOptTable tbl,
- String idxName
+ String idxName,
+ boolean notNull
) {
- this(-1, cluster, traits, tbl, idxName);
+ this(-1, cluster, traits, tbl, idxName, notNull);
}
/**
@@ -92,19 +99,22 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
* @param traits Traits of this relational expression.
* @param tbl Table definition.
* @param idxName Index name.
+ * @param notNull Count only not-null values.
*/
private IgniteIndexCount(
long sourceId,
RelOptCluster cluster,
RelTraitSet traits,
RelOptTable tbl,
- String idxName
+ String idxName,
+ boolean notNull
) {
super(cluster, traits);
this.idxName = idxName;
this.tbl = tbl;
this.sourceId = sourceId;
+ this.notNull = notNull;
}
/** {@inheritDoc} */
@@ -145,7 +155,8 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
return super.explainTerms(pw)
.item("index", idxName)
.item("table", tbl.getQualifiedName())
- .itemIf("sourceId", sourceId, sourceId != -1L);
+ .itemIf("sourceId", sourceId, sourceId != -1L)
+ .item("notNull", notNull);
}
/** {@inheritDoc} */
@@ -155,11 +166,16 @@ public class IgniteIndexCount extends AbstractRelNode implements SourceAwareIgni
/** {@inheritDoc} */
@Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) {
- return new IgniteIndexCount(sourceId, cluster, traitSet, tbl, idxName);
+ return new IgniteIndexCount(sourceId, cluster, traitSet, tbl, idxName, notNull);
}
/** {@inheritDoc} */
@Override public IgniteRel clone(long srcId) {
- return new IgniteIndexCount(srcId, getCluster(), traitSet, tbl, idxName);
+ return new IgniteIndexCount(srcId, getCluster(), traitSet, tbl, idxName, notNull);
+ }
+
+ /** */
+ public boolean notNull() {
+ return notNull;
}
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/IndexCountRule.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/IndexCountRule.java
index 475637dfac3..28449d38213 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/IndexCountRule.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/IndexCountRule.java
@@ -18,11 +18,14 @@
package org.apache.ignite.internal.processors.query.calcite.rule;
import java.util.Collections;
+import java.util.List;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelRule;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelDistribution;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.logical.LogicalAggregate;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -38,7 +41,7 @@ import org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTr
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.immutables.value.Value;
-/** Tries to optimize 'COUNT(*)' to use number of index records. */
+/** Tries to optimize 'COUNT()' to use number of index records. */
@Value.Enclosing
public class IndexCountRule extends RelRule<IndexCountRule.Config> {
/** */
@@ -54,18 +57,50 @@ public class IndexCountRule extends RelRule<IndexCountRule.Config> {
LogicalAggregate aggr = call.rel(0);
IgniteLogicalTableScan scan = call.rel(1);
IgniteTable table = scan.getTable().unwrap(IgniteTable.class);
- IgniteIndex idx = table.getIndex(QueryUtils.PRIMARY_KEY_INDEX);
if (
- idx == null ||
- table.isIndexRebuildInProgress() ||
- scan.condition() != null ||
- aggr.getGroupCount() > 0 ||
- aggr.getAggCallList().stream().anyMatch(a -> a.getAggregation().getKind() != SqlKind.COUNT ||
- !a.getArgList().isEmpty() || a.hasFilter())
+ table.isIndexRebuildInProgress() ||
+ scan.condition() != null ||
+ aggr.getGroupCount() > 0 ||
+ aggr.getAggCallList().size() != 1
)
return;
+ AggregateCall agg = aggr.getAggCallList().get(0);
+
+ if (agg.getAggregation().getKind() != SqlKind.COUNT || agg.hasFilter() || agg.isDistinct())
+ return;
+
+ List<Integer> argList = agg.getArgList();
+
+ IgniteIndex idx = null;
+ boolean notNull = false;
+
+ if (argList.isEmpty())
+ idx = table.getIndex(QueryUtils.PRIMARY_KEY_INDEX);
+ else {
+ if (scan.projects() != null || argList.size() > 1)
+ return;
+
+ notNull = true;
+ int fieldIdx = argList.get(0);
+
+ if (!scan.requiredColumns().isEmpty())
+ fieldIdx = scan.requiredColumns().nth(fieldIdx);
+
+ for (IgniteIndex idx0 : table.indexes().values()) {
+ List<RelFieldCollation> fieldCollations = idx0.collation().getFieldCollations();
+
+ if (!fieldCollations.isEmpty() && fieldCollations.get(0).getFieldIndex() == fieldIdx) {
+ idx = idx0;
+ break;
+ }
+ }
+ }
+
+ if (idx == null)
+ return;
+
RelTraitSet idxTraits = aggr.getTraitSet()
.replace(IgniteConvention.INSTANCE)
.replace(table.distribution().getType() == RelDistribution.Type.HASH_DISTRIBUTED ?
@@ -76,7 +111,8 @@ public class IndexCountRule extends RelRule<IndexCountRule.Config> {
scan.getCluster(),
idxTraits,
scan.getTable(),
- idx.name()
+ idx.name(),
+ notNull
);
RelBuilder b = call.builder();
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
index d096f2ae89b..68c8ea896a0 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
@@ -20,11 +20,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableBitSet;
@@ -35,11 +37,14 @@ import org.apache.ignite.internal.cache.query.index.Index;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyDefinition;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyTypeSettings;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
import org.apache.ignite.internal.cache.query.index.sorted.inline.IndexQueryContext;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexImpl;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyType;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyTypeRegistry;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.IndexFirstLastScan;
import org.apache.ignite.internal.processors.query.calcite.exec.IndexScan;
@@ -151,7 +156,7 @@ public class CacheIndexImpl implements IgniteIndex {
}
/** {@inheritDoc} */
- @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp) {
+ @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp, boolean notNull) {
long cnt = 0;
if (idx != null && grp.nodeIds().contains(ectx.localNodeId())) {
@@ -160,9 +165,43 @@ public class CacheIndexImpl implements IgniteIndex {
InlineIndex iidx = idx.unwrap(InlineIndex.class);
+ BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter = null;
+
+ if (notNull) {
+ boolean nullsFirst = collation.getFieldCollations().get(0).nullDirection ==
+ RelFieldCollation.NullDirection.FIRST;
+
+ BPlusTree.TreeRowClosure<IndexRow, IndexRow> notNullRowFilter = IndexScan.createNotNullRowFilter(iidx);
+
+ AtomicBoolean skipCheck = new AtomicBoolean();
+
+ rowFilter = new BPlusTree.TreeRowClosure<IndexRow, IndexRow>() {
+ @Override public boolean apply(
+ BPlusTree<IndexRow, IndexRow> tree,
+ BPlusIO<IndexRow> io,
+ long pageAddr,
+ int idx
+ ) throws IgniteCheckedException {
+ // If we have NULLS-FIRST collation, all values after first not-null value will be not-null,
+ // don't need to check it with notNullRowFilter.
+ // In case of NULL-LAST collation, all values after first null value will be null,
+ // don't need to check it too.
+ if (skipCheck.get())
+ return nullsFirst;
+
+ boolean res = notNullRowFilter.apply(tree, io, pageAddr, idx);
+
+ if (res == nullsFirst)
+ skipCheck.set(true);
+
+ return res;
+ }
+ };
+ }
+
try {
for (int i = 0; i < iidx.segmentsCount(); ++i)
- cnt += iidx.count(i, new IndexQueryContext(filter, null, ectx.mvccSnapshot()));
+ cnt += iidx.count(i, new IndexQueryContext(filter, rowFilter, ectx.mvccSnapshot()));
}
catch (IgniteCheckedException e) {
throw new IgniteException("Unable to count index records.", e);
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
index 3f691945936..81ded7289bc 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
@@ -91,9 +91,10 @@ public interface IgniteIndex {
*
* @param ectx Execution context.
* @param grp Colocation group.
+ * @param notNull Exclude null values.
* @return Index records number for {@code group}.
*/
- public long count(ExecutionContext<?> ectx, ColocationGroup grp);
+ public long count(ExecutionContext<?> ectx, ColocationGroup grp, boolean notNull);
/**
* Takes only first or last not-null index value.
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
index ccf6674516d..85717a5e9ad 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
@@ -98,7 +98,9 @@ public class SystemViewIndexImpl implements IgniteIndex {
}
/** {@inheritDoc} */
- @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp) {
+ @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp, boolean notNull) {
+ assert !notNull; // Collation is empty, cannot come here with "notNull" flag.
+
return tbl.descriptor().systemView().size();
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
index 78baf7f068d..03ea89b5299 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
@@ -204,7 +204,7 @@ public class LogicalRelImplementorTest extends GridCommonAbstractTest {
@Test
public void testIndexCountRewriter() {
IgniteIndexCount idxCnt = new IgniteIndexCount(cluster, cluster.traitSet(),
- qctx.catalogReader().getTable(F.asList("PUBLIC", "TBL")), QueryUtils.PRIMARY_KEY_INDEX);
+ qctx.catalogReader().getTable(F.asList("PUBLIC", "TBL")), QueryUtils.PRIMARY_KEY_INDEX, false);
checkCollectNode(relImplementor.visit(idxCnt));
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
index 3cdc981943e..8e3a38476b7 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
@@ -254,8 +254,8 @@ public class AbstractBasicIntegrationTest extends GridCommonAbstractTest {
}
/** {@inheritDoc} */
- @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp) {
- return delegate.count(ectx, grp);
+ @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp, boolean notNull) {
+ return delegate.count(ectx, grp, notNull);
}
/** {@inheritDoc} */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AggregatesIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AggregatesIntegrationTest.java
index b2383932656..4b910029cae 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AggregatesIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AggregatesIntegrationTest.java
@@ -108,6 +108,38 @@ public class AggregatesIntegrationTest extends AbstractBasicIntegrationTest {
assertQuery("select count(*) from person").returns(7L).check();
}
+ /** */
+ @Test
+ public void testCountIndexedField() {
+ createAndPopulateIndexedTable(1, CacheMode.PARTITIONED);
+
+ assertQuery("select count(salary) from person").returns(4L).check();
+ assertQuery("select count(descVal) from person").returns(4L).check();
+ assertQuery("select count(salary + 1) from person").returns(4L).check();
+ assertQuery("select count(distinct descVal) from person").returns(3L).check();
+ assertQuery("select count(salary) from person where salary >= 5").returns(2L).check();
+ assertQuery("select count(salary) filter (where salary >= 5) from person").returns(2L).check();
+ assertQuery("select count(salary), descVal from person group by descVal")
+ .returns(1L, 1d)
+ .returns(1L, 9d)
+ .returns(1L, 15d)
+ .returns(1L, null)
+ .check();
+
+ // Check count with two columns index.
+ sql("CREATE TABLE tbl (a INT, b INT, c INT)");
+ sql("CREATE INDEX idx_a ON tbl(a, c)");
+ sql("CREATE INDEX idx_b ON tbl(b DESC, c)");
+
+ for (int i = 0; i < 100; i++) {
+ sql("INSERT INTO tbl VALUES (null, null, ?)", i % 2 == 0 ? i : null);
+ sql("INSERT INTO tbl VALUES (?, ?, ?)", i, i, i % 2 == 0 ? null : i);
+ }
+
+ assertQuery("SELECT COUNT(a) FROM tbl").returns(100L).check();
+ assertQuery("SELECT COUNT(b) FROM tbl").returns(100L).check();
+ }
+
/** */
@Test
public void testCountOfNonNumericField() {
@@ -308,7 +340,7 @@ public class AggregatesIntegrationTest extends AbstractBasicIntegrationTest {
int idx = 0;
- person.put(idx++, new IndexedEmployer("Igor", 5d, 7d));
+ person.put(idx++, new IndexedEmployer("Igor", 5d, 9d));
person.put(idx++, new IndexedEmployer(null, 3d, null));
person.put(idx++, new IndexedEmployer("Ilya", 1d, 1d));
person.put(idx++, new IndexedEmployer("Roma", null, 9d));
@@ -335,7 +367,7 @@ public class AggregatesIntegrationTest extends AbstractBasicIntegrationTest {
public IndexedEmployer(String name, Double salary, Double descVal) {
this.name = name;
this.salary = salary;
- this.descVal = salary;
+ this.descVal = descVal;
}
}
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashAggregatePlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashAggregatePlannerTest.java
index 01122091fe0..41a0fe09f16 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashAggregatePlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashAggregatePlannerTest.java
@@ -140,8 +140,13 @@ public class HashAggregatePlannerTest extends AbstractAggregatePlannerTest {
assertIndexCount("SELECT COUNT(*), COUNT(*), COUNT(1) FROM TEST", publicSchema);
- // Count on certain fields can't be optimized. Nulls are count included.
+ assertIndexCount("SELECT COUNT(ID) FROM TEST", publicSchema);
+ assertNoIndexCount("SELECT COUNT(ID + 1) FROM TEST", publicSchema);
assertNoIndexCount("SELECT COUNT(VAL0) FROM TEST", publicSchema);
+
+ tbl.addIndex("TEST_IDX", 1, 2);
+
+ assertIndexCount("SELECT COUNT(VAL0) FROM TEST", publicSchema);
assertNoIndexCount("SELECT COUNT(DISTINCT VAL0) FROM TEST", publicSchema);
assertNoIndexCount("SELECT COUNT(*), COUNT(VAL0) FROM TEST", publicSchema);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
index 18f174138ef..fe0fa262ebb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
@@ -64,7 +64,7 @@ public interface InlineIndexKeyType {
* @param pageAddr Page address.
* @param off Offset.
* @param key Index key.
- * @param maxSize Max size.
+ * @param maxSize Remaining inlined buffer size (max available bytes to write for the current row).
* @return Amount of bytes actually stored.
*/
public int put(long pageAddr, int off, IndexKey key, int maxSize);
@@ -74,17 +74,28 @@ public interface InlineIndexKeyType {
*
* @param pageAddr Page address.
* @param off Offset.
- * @param maxSize Max size.
+ * @param maxSize Remaining inlined buffer size (max available bytes to read for the current row).
* @return Index key extracted from index tree.
*/
@Nullable public IndexKey get(long pageAddr, int off, int maxSize);
+ /**
+ * Checks if inlined index key is null.
+ *
+ * @param pageAddr Page address.
+ * @param off Offset.
+ * @param maxSize Remaining inlined buffer size (max available bytes to read for the current row).
+ * @return {@code Boolean.TRUE} if index key is null, {@code Boolean.FALSE} if index key is not null,
+ * {@code null} if can't say for sure.
+ */
+ @Nullable public Boolean isNull(long pageAddr, int off, int maxSize);
+
/**
* Compares inlined and given value.
*
* @param pageAddr Page address.
* @param off Offset.
- * @param maxSize Max size.
+ * @param maxSize Remaining inlined buffer size (max available bytes to read for the current row).
* @param v Value that should be compare.
* @return -1, 0 or 1 if inlined value less, equal or greater
* than given respectively, or -2 if inlined part is not enough to compare.
@@ -108,7 +119,7 @@ public interface InlineIndexKeyType {
*
* @param pageAddr Page address.
* @param off Offset.
- * @param maxSize Max size.
+ * @param maxSize Remaining inlined buffer size (max available bytes to read for the current row).
* @return {@code true} if inline contains full index key. Can be {@code false} for truncated variable length types.
*/
public boolean inlinedFullValue(long pageAddr, int off, int maxSize);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
index f3a86e0f604..6b947620933 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
@@ -22,7 +22,6 @@ import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKey
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.cache.query.index.sorted.keys.NullIndexKey;
import org.apache.ignite.internal.pagemem.PageUtils;
-import org.jetbrains.annotations.Nullable;
/**
* Abstract inline key. Store base logic for work with inlined keys. Handle NULL values.
@@ -117,12 +116,20 @@ public abstract class NullableInlineIndexKeyType<T extends IndexKey> implements
ensureKeyType(typeCode);
- IndexKey o = get0(pageAddr, off);
+ return get0(pageAddr, off);
+ }
- if (o == null)
- return NullIndexKey.INSTANCE;
+ /** {@inheritDoc} */
+ @Override public Boolean isNull(long pageAddr, int off, int maxSize) {
+ if (maxSize < 1)
+ return null;
+
+ int typeCode = PageUtils.getByte(pageAddr, off);
+
+ if (typeCode == IndexKeyType.UNKNOWN.code())
+ return null;
- return o;
+ return typeCode == IndexKeyType.NULL.code();
}
/** {@inheritDoc} */
@@ -170,9 +177,9 @@ public abstract class NullableInlineIndexKeyType<T extends IndexKey> implements
* @param pageAddr Page address.
* @param off Offset.
*
- * @return Inline value or {@code null} if value can't be restored.
+ * @return Inline value.
*/
- protected abstract @Nullable T get0(long pageAddr, int off);
+ protected abstract T get0(long pageAddr, int off);
/** Read variable length bytearray */
public static byte[] readBytes(long pageAddr, int off) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
index 593e743d428..83265c4b620 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
@@ -24,7 +24,6 @@ import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.cache.query.index.sorted.keys.StringIndexKey;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.util.GridUnsafe;
-import org.jetbrains.annotations.Nullable;
/**
* Inline index key implementation for inlining {@link String} values.
@@ -67,7 +66,7 @@ public class StringInlineIndexKeyType extends NullableInlineIndexKeyType<StringI
}
/** {@inheritDoc} */
- @Override protected @Nullable StringIndexKey get0(long pageAddr, int off) {
+ @Override protected StringIndexKey get0(long pageAddr, int off) {
String s = new String(readBytes(pageAddr, off), CHARSET);
return new StringIndexKey(s);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
index 37584b2ea34..99ecc21eb73 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cache.query.index.sorted.inline.types;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.cache.query.index.sorted.keys.StringIndexKey;
-import org.jetbrains.annotations.Nullable;
/**
* Skip optimized String comparison implemented in {@link StringInlineIndexKeyType}.
@@ -40,7 +39,7 @@ public class StringNoCompareInlineIndexKeyType extends NullableInlineIndexKeyTyp
}
/** {@inheritDoc} */
- @Override protected @Nullable StringIndexKey get0(long pageAddr, int off) {
+ @Override protected StringIndexKey get0(long pageAddr, int off) {
return delegate.get0(pageAddr, off);
}