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