This is an automated email from the ASF dual-hosted git repository.

zhenchen 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 eee9230413 [CALCITE-5421] SqlToRelConverter should populate 
correlateId for join with correlated query in HAVING condition
eee9230413 is described below

commit eee923041302ae8a7db8228c2c1feb410d007450
Author: Zhen Chen <[email protected]>
AuthorDate: Wed Oct 15 23:03:38 2025 +0800

    [CALCITE-5421] SqlToRelConverter should populate correlateId for join with 
correlated query in HAVING condition
---
 .../apache/calcite/sql2rel/SqlToRelConverter.java  | 57 +++++++++++++++++++++-
 .../apache/calcite/test/SqlToRelConverterTest.java | 18 +++++++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 24 +++++++++
 core/src/test/resources/sql/sub-query.iq           | 21 ++++++++
 4 files changed, 119 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 52ba35b402..98da6975ad 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -36,6 +36,7 @@
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelDistributions;
 import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.RelShuttleImpl;
@@ -3159,6 +3160,8 @@ protected RelNode createAsofJoin(
     }
     final ImmutableBitSet.Builder requiredColumns = ImmutableBitSet.builder();
     final List<CorrelationId> correlNames = new ArrayList<>();
+    // Mapping from (correlId, originalFieldIndex) to projectedFieldIndex for 
aggregation
+    final Map<Pair<CorrelationId, Integer>, Integer> fieldMapping = new 
HashMap<>();
 
     for (CorrelationId correlName : correlatedVariables) {
       DeferredLookup lookup =
@@ -3203,8 +3206,9 @@ protected RelNode createAsofJoin(
       while (topLevelFieldAccess.getReferenceExpr() instanceof RexFieldAccess) 
{
         topLevelFieldAccess = (RexFieldAccess) 
topLevelFieldAccess.getReferenceExpr();
       }
+      final int originalFieldIndex = topLevelFieldAccess.getField().getIndex();
       final RelDataTypeField field = rowType.getFieldList()
-          .get(topLevelFieldAccess.getField().getIndex() - namespaceOffset);
+          .get(originalFieldIndex - namespaceOffset);
       int pos = namespaceOffset + field.getIndex();
 
       assert field.getType()
@@ -3221,6 +3225,7 @@ protected RelNode createAsofJoin(
         // the root of the outer relation.
         Integer projection = exprProjection.get(pos);
         if (projection != null) {
+          fieldMapping.put(Pair.of(correlName, originalFieldIndex), 
projection);
           pos = projection;
         } else {
           // correl not grouped
@@ -3248,6 +3253,16 @@ protected RelNode createAsofJoin(
       // Add new node to leaves.
       leaves.put(r, r.getRowType().getFieldCount());
     }
+
+    // If there are field mappings (due to aggregation), rewrite the RelNode 
tree
+    // to update correlation variable row type and field indices
+    if (!fieldMapping.isEmpty()) {
+      r =
+          r.accept(
+              new CorrelationFieldMappingShuttle(rexBuilder, 
correlNames.get(0),
+                  bb.root().getRowType(), fieldMapping));
+    }
+
     return new CorrelationUse(correlNames.get(0), requiredColumns.build(), r);
   }
 
@@ -6039,6 +6054,46 @@ RexFieldAccess getFieldAccess(CorrelationId name) {
     }
   }
 
+  /**
+   * Shuttle that rewrites correlation field accesses to use projected field 
indices
+   * when correlation references aggregated relations.
+   */
+  private static class CorrelationFieldMappingShuttle extends 
RelHomogeneousShuttle {
+    private final RexBuilder rexBuilder;
+    private final CorrelationId targetCorrelId;
+    private final RelDataType newCorrelRowType;
+    private final Map<Pair<CorrelationId, Integer>, Integer> fieldMapping;
+
+    CorrelationFieldMappingShuttle(RexBuilder rexBuilder,
+        CorrelationId targetCorrelId,
+        RelDataType newCorrelRowType,
+        Map<Pair<CorrelationId, Integer>, Integer> fieldMapping) {
+      this.rexBuilder = rexBuilder;
+      this.targetCorrelId = targetCorrelId;
+      this.newCorrelRowType = newCorrelRowType;
+      this.fieldMapping = fieldMapping;
+    }
+
+    @Override public RelNode visit(RelNode other) {
+      return super.visit(other).accept(new RexShuttle() {
+        @Override public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
+          if (fieldAccess.getReferenceExpr() instanceof RexCorrelVariable) {
+            RexCorrelVariable correlVar = (RexCorrelVariable) 
fieldAccess.getReferenceExpr();
+            if (correlVar.id.equals(targetCorrelId)) {
+              Integer newIndex =
+                  fieldMapping.get(Pair.of(correlVar.id, 
fieldAccess.getField().getIndex()));
+              if (newIndex != null) {
+                return rexBuilder.makeFieldAccess(
+                    rexBuilder.makeCorrel(newCorrelRowType, correlVar.id), 
newIndex);
+              }
+            }
+          }
+          return super.visitFieldAccess(fieldAccess);
+        }
+      });
+    }
+  }
+
   /**
    * A default implementation of SubQueryConverter that does no conversion.
    */
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index e61f68401b..e1ca9b715a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -5915,4 +5915,22 @@ void checkUserDefinedOrderByOver(NullCollation 
nullCollation) {
         .withExpand(false)
         .ok();
   }
+
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-5421";>[CALCITE-5421]
+   * SqlToRelConverter should populate correlateId for join with correlated 
query
+   * in HAVING condition</a>.
+   */
+  @Test void testInCorrelatedSubQueryInHavingRex() {
+    final String sql = "select sum(sal) as s\n"
+        + "from emp e1\n"
+        + "group by deptno\n"
+        + "having count(*) > 2\n"
+        + "and exists(\n"
+        + "  select true\n"
+        + "  from emp e2\n"
+        + "  where e1.deptno = e2.deptno)";
+    sql(sql).withExpand(false).ok();
+  }
 }
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 45e809a518..2b70fa7381 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -3085,6 +3085,30 @@ FROM dept, emp WHERE emp.deptno = dept.deptno AND 
emp.sal < (
 )]]>
     </Resource>
   </TestCase>
+  <TestCase name="testInCorrelatedSubQueryInHavingRex">
+    <Resource name="sql">
+      <![CDATA[select sum(sal) as s
+from emp e1
+group by deptno
+having count(*) > 2
+and exists(
+  select true
+  from emp e2
+  where e1.deptno = e2.deptno)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(S=[$1])
+  LogicalFilter(condition=[AND(>($2, 2), EXISTS({
+LogicalFilter(condition=[=($cor0.DEPTNO, $7)])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+}))], variablesSet=[[$cor0]])
+    LogicalAggregate(group=[{0}], S=[SUM($1)], agg#1=[COUNT()])
+      LogicalProject(DEPTNO=[$7], SAL=[$5])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
   <TestCase name="testInSubQueryWithTypeCast">
     <Resource name="sql">
       <![CDATA[select *
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 6225a91bd5..182f3f4e31 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -5367,4 +5367,25 @@ EnumerableCalc(expr#0..4=[{inputs}], 
expr#5=[CAST($t1):BIGINT], expr#6=[IS NULL(
         EnumerableValues(tuples=[[{ 10 }, { 10 }, { 20 }, { 30 }, { 30 }, { 50 
}, { 50 }, { 60 }, { null }]])
 !plan
 
+# [CALCITE-5421] SqlToRelConverter should populate correlateId for join with 
correlated query in HAVING condition
+!use scott
+select sum(sal) as s
+from emp e1
+group by deptno
+having count(*) > 2
+and exists(
+  select true
+  from emp e2
+  where e1.deptno = e2.deptno);
++----------+
+| S        |
++----------+
+| 10875.00 |
+|  8750.00 |
+|  9400.00 |
++----------+
+(3 rows)
+
+!ok
+
 # End sub-query.iq

Reply via email to