This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 6c4b0baedef branch-4.1: [fix](aggregate) Normalize projected count
slots before null safety checks #67732 (#68068)
6c4b0baedef is described below
commit 6c4b0baedef1bba40e649bace2d37dd0a12f9c0d
Author: morrySnow <[email protected]>
AuthorDate: Wed Sep 16 23:30:56 2026 +0800
branch-4.1: [fix](aggregate) Normalize projected count slots before null
safety checks #67732 (#68068)
### What problem does this PR solve?
Related PR: #67732
Problem Summary:
Backport the nullable projected COUNT safety fix to branch-4.1.
Aggregate arguments are normalized through the Project before comparing
them with filter slots, preventing unsafe COUNT_ON_INDEX pushdown for IS
NULL predicates. The regression test is adapted to the 4.1 IndexType
API.
### Release note
None
### Check List (For Author)
- Test
- [x] Unit Test
- Behavior changed:
- [x] Yes. Unsafe storage-layer count pushdown is rejected for nullable
projected count slots.
- Does this need documentation?
- [x] No.
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
---
.../rules/implementation/AggregateStrategies.java | 4 +--
.../rewrite/PhysicalStorageLayerAggregateTest.java | 38 ++++++++++++++++++++++
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
index 5683ca528c9..9d4814aea80 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/AggregateStrategies.java
@@ -133,8 +133,8 @@ public class AggregateStrategies implements
ImplementationRuleFactory {
return false;
}
- Set<Slot> aggSlots = funcs.stream()
- .flatMap(f -> f.getInputSlots().stream())
+ Set<Slot> aggSlots = normalizeArguments(funcs,
agg.child()).stream()
+ .flatMap(argument ->
argument.getInputSlots().stream())
.collect(Collectors.toSet());
return aggSlots.isEmpty() ||
conjuncts.stream().allMatch(expr ->
checkSlotInOrExpression(expr, aggSlots) &&
checkIsNullExpr(expr, aggSlots));
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PhysicalStorageLayerAggregateTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PhysicalStorageLayerAggregateTest.java
index 3c636d3018a..4846f3c004b 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PhysicalStorageLayerAggregateTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PhysicalStorageLayerAggregateTest.java
@@ -17,8 +17,10 @@
package org.apache.doris.nereids.rules.rewrite;
+import org.apache.doris.analysis.IndexDef.IndexType;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DatabaseIf;
+import org.apache.doris.catalog.Index;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.Type;
import org.apache.doris.datasource.CatalogIf;
@@ -31,6 +33,7 @@ import
org.apache.doris.nereids.rules.implementation.AggregateStrategies;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.IsNull;
import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
@@ -40,6 +43,7 @@ import org.apache.doris.nereids.trees.plans.RelationId;
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
import org.apache.doris.nereids.trees.plans.logical.LogicalFileScan;
import
org.apache.doris.nereids.trees.plans.logical.LogicalFileScan.SelectedPartitions;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
@@ -56,6 +60,7 @@ import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.nereids.util.PlanConstructor;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -368,6 +373,31 @@ public class PhysicalStorageLayerAggregateTest implements
MemoPatternMatchSuppor
);
}
+ @Test
+ public void testCountOnIndexRejectsIsNullOnProjectedCountSlot() {
+ LogicalOlapScan olapScan = PlanConstructor.newLogicalOlapScan(2,
"count_alias", 0);
+ Index invertedIndex = new Index(1L, "idx_name",
ImmutableList.of("name"),
+ IndexType.INVERTED, null, "");
+ olapScan.getTable().getIndexIdToMeta().values().forEach(
+ meta -> meta.setIndexes(ImmutableList.of(invertedIndex)));
+
+ LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(
+ ImmutableSet.of(new IsNull(olapScan.getOutput().get(1))),
olapScan);
+ LogicalProject<LogicalFilter<LogicalOlapScan>> project = new
LogicalProject<>(
+ ImmutableList.of(new Alias(olapScan.getOutput().get(1), "x")),
filter);
+ LogicalAggregate<LogicalProject<LogicalFilter<LogicalOlapScan>>>
aggregate = new LogicalAggregate<>(
+ Collections.emptyList(),
+ ImmutableList.of(new Alias(new
Count(project.getOutput().get(0)), "count_x"),
+ new Alias(new Count(), "count_star")),
+ true, Optional.empty(), project);
+ CascadesContext context =
MemoTestUtils.createCascadesContext(aggregate);
+
context.getConnectContext().getSessionVariable().setEnablePushDownCountOnIndex(true);
+
+ PlanChecker.from(context)
+ .applyImplementation(countOnIndex())
+
.matches(logicalAggregate(logicalProject(logicalFilter(logicalOlapScan()))));
+ }
+
@Test
void testProjectionCheck() {
LogicalOlapScan olapScan = PlanConstructor.newLogicalOlapScan(1,
"tbl", 0);
@@ -418,4 +448,12 @@ public class PhysicalStorageLayerAggregateTest implements
MemoPatternMatchSuppor
.findFirst()
.get();
}
+
+ private Rule countOnIndex() {
+ return new AggregateStrategies().buildRules()
+ .stream()
+ .filter(rule -> rule.getRuleType() == RuleType.COUNT_ON_INDEX)
+ .findFirst()
+ .get();
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]