This is an automated email from the ASF dual-hosted git repository.
mihaibudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 2b794ff5a9 [CALCITE-7687] Aggregate selectivity estimation uses
incorrect column references
2b794ff5a9 is described below
commit 2b794ff5a9e4874905a91d475a2136ce5107aeb0
Author: npaincomplet <[email protected]>
AuthorDate: Wed Sep 16 14:59:58 2026 +0200
[CALCITE-7687] Aggregate selectivity estimation uses incorrect column
references
---
.../calcite/rel/metadata/RelMdSelectivity.java | 17 +++++++++-
.../apache/calcite/test/RelMdSelectivityTest.java | 32 +++++++++++++++++++
core/src/test/resources/sql/hep.iq | 36 ++++++++++++++++++++++
.../org/apache/calcite/test/DruidAdapter2IT.java | 4 +--
.../org/apache/calcite/test/DruidAdapterIT.java | 4 +--
5 files changed, 88 insertions(+), 5 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
index 5faaeaa85d..d4d3897e9d 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
@@ -180,7 +180,7 @@ protected RelMdSelectivity() {
final List<RexNode> notPushable = new ArrayList<>();
final List<RexNode> pushable = new ArrayList<>();
RelOptUtil.splitFilters(
- rel.getGroupSet(),
+ ImmutableBitSet.range(rel.getGroupCount()),
predicate,
pushable,
notPushable);
@@ -188,6 +188,21 @@ protected RelMdSelectivity() {
RexNode childPred =
RexUtil.composeConjunction(rexBuilder, pushable, true);
+ if (childPred != null) {
+ // Convert aggregate-output references to the corresponding input fields.
+ final int[] adjustments = new int[rel.getGroupCount()];
+ int j = 0;
+ for (int i : rel.getGroupSet()) {
+ adjustments[j] = i - j;
+ j++;
+ }
+ childPred =
+ childPred.accept(
+ new RelOptUtil.RexInputConverter(rexBuilder,
+ rel.getRowType().getFieldList(),
+ rel.getInput().getRowType().getFieldList(), adjustments));
+ }
+
Double selectivity = mq.getSelectivity(rel.getInput(), childPred);
if (selectivity == null) {
return null;
diff --git
a/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
b/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
index a086022bed..38c9f1cbc8 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
@@ -20,16 +20,48 @@
import org.apache.calcite.plan.hep.HepProgram;
import org.apache.calcite.plan.hep.HepProgramBuilder;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.rex.RexNode;
import org.apache.calcite.tools.RelBuilder;
import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
/**
* Test cases for {@link org.apache.calcite.rel.metadata.RelMdSelectivity}.
*/
class RelMdSelectivityTest {
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7687">[CALCITE-7687]
+ * Aggregate selectivity estimation uses incorrect column references</a>. */
+ @Test void testAggregateSelectivityWithPredicate() {
+ final RelBuilder builder =
RelBuilder.create(RelBuilderTest.config().build())
+ .transform(c -> c.withPruneInputOfAggregate(false));
+ final Aggregate aggregate = (Aggregate) builder
+ .scan("EMP")
+ .project(
+ builder.field("EMPNO"),
+ builder.field("DEPTNO"))
+ .filter(
+ builder.equals(
+ builder.field("DEPTNO"),
+ builder.literal(10)))
+ .aggregate(
+ builder.groupKey("DEPTNO"),
+ builder.countStar("C"))
+ .peek();
+
+ final RexNode predicate =
+ builder.equals(builder.field("DEPTNO"), builder.literal(10));
+
+ assertThat(aggregate.getCluster().getMetadataQuery()
+ .getSelectivity(aggregate, predicate), is(1D));
+ }
+
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-4414">[CALCITE-4414]
* RelMdSelectivity#getSelectivity for Calc can propagate a predicate with
wrong reference</a>. */
diff --git a/core/src/test/resources/sql/hep.iq
b/core/src/test/resources/sql/hep.iq
index b9aee8e1f4..3498fdea19 100644
--- a/core/src/test/resources/sql/hep.iq
+++ b/core/src/test/resources/sql/hep.iq
@@ -375,4 +375,40 @@ having loc is null;
!ok
!set hep-rules original
+# [CALCITE-7687] Aggregate selectivity estimation uses incorrect column
references
+!set hep-rules "
++CoreRules.AGGREGATE_PROJECT_MERGE"
+
+# Display the result of the query explained below.
+with t(n, a, r) as (
+ values (0, 'a', cast(row(10) as row(d integer))),
+ (1, 'b', cast(row(20) as row(d integer)))
+)
+select a, r, sum(n) as s
+from t
+group by a, r
+having (r).d = 10;
++---+------+---+
+| A | R | S |
++---+------+---+
+| a | {10} | 0 |
++---+------+---+
+(1 row)
+
+!ok
+
+# Verify aggregate selectivity estimation during EXPLAIN preparation does not
fail.
+explain plan including all attributes for
+with t(n, a, r) as (
+ values (0, 'a', cast(row(10) as row(d integer))),
+ (1, 'b', cast(row(20) as row(d integer)))
+)
+select a, r, sum(n) as s
+from t
+group by a, r
+having (r).d = 10;
+PLAN VARCHAR NOT NULL
+!type
+!set hep-rules original
+
# End hep.iq
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java
b/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java
index eec575e331..e12069cdca 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java
@@ -3663,8 +3663,8 @@ private void testCountWithApproxDistinct(boolean approx,
String sql,
CalciteAssert.AssertQuery q = sql(sql)
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]],
intervals=[[1900-01-09T00:00:00.000Z/"
- + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')],
projects=[[$1, $90]], groups=[{0}],"
- + " aggs=[[SUM($1)]], filter=[>($1, 220.0E0)], sort0=[0],
dir0=[ASC], fetch=[2])\n")
+ + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')], groups=[{1}],"
+ + " aggs=[[SUM($90)]], filter=[>($1, 220.0E0)], sort0=[0],
dir0=[ASC], fetch=[2])\n")
.queryContains(
new
DruidChecker("{'queryType':'groupBy','dataSource':'foodmart','granularity':'all'"));
q.returnsOrdered("P=100; S=343.2", "P=1000; S=532.62");
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
index 87f2c65f4c..501063ee6b 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
@@ -4377,8 +4377,8 @@ private void testCountWithApproxDistinct(boolean approx,
String sql, String expe
CalciteAssert.AssertQuery q = sql(sql, FOODMART)
.explainContains("PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]],
intervals=[[1900-01-09T00:00:00.000Z/"
- + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')],
projects=[[$1, $90]], groups=[{0}],"
- + " aggs=[[SUM($1)]], filter=[>($1, 220.0E0)], sort0=[0],
dir0=[ASC], fetch=[2])\n")
+ + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')], groups=[{1}],"
+ + " aggs=[[SUM($90)]], filter=[>($1, 220.0E0)], sort0=[0],
dir0=[ASC], fetch=[2])\n")
.queryContains(
new
DruidChecker("{'queryType':'groupBy','dataSource':'foodmart','granularity':'all'"));
q.returnsOrdered("P=100; S=343.2", "P=1000; S=532.62");