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 8447eda2f2 [CALCITE-7505] RelToSqlConverter fails to alias outer
relation for correlated sub-queries in Filter
8447eda2f2 is described below
commit 8447eda2f2fe34af9571f84ccb5de3d74c9e5ecd
Author: krooswu <[email protected]>
AuthorDate: Sat Jun 20 21:06:26 2026 +0800
[CALCITE-7505] RelToSqlConverter fails to alias outer relation for
correlated sub-queries in Filter
---
.../calcite/rel/rel2sql/RelToSqlConverter.java | 45 ++++++++++++--
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 68 ++++++++++++++++++++--
2 files changed, 103 insertions(+), 10 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
index 44eec4feb9..718e6b1cb9 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
@@ -547,17 +547,38 @@ public Result visit(Correlate e) {
null);
return result(join, leftResult, rightResult);
}
-
/** Visits a Filter; called by {@link #dispatch} via reflection. */
public Result visit(Filter e) {
final RelNode input = e.getInput();
+ final Set<CorrelationId> definedHere = e.getVariablesSet();
+
if (input instanceof Aggregate) {
final Aggregate aggregate = (Aggregate) input;
final boolean ignoreClauses = aggregate.getInput() instanceof Project;
- final Result x =
+ Result x =
visitInput(e, 0, isAnon(), ignoreClauses,
ImmutableSet.of(Clause.HAVING));
+ // Only rebind the correlation alias when e.getInput() renders as a
single
+ // addressable relation (<=1 input): TableScan, Project, Filter, etc.
+ // Join/Correlate (2+ inputs) already expose a multi-alias context via
+ // joinContext(); collapsing it here would break field resolution.
+ final boolean pushed = !definedHere.isEmpty()
+ && e.getInput().getInputs().size() <= 1;
+ if (pushed) {
+ String alias = x.neededAlias;
+ if (alias != null) {
+ x = x.resetAliasForCorrelation(alias, e.getInput().getRowType());
+ } else {
+ alias = unqualifiedName(x.node);
+ if (alias == null) {
+ alias = "t";
+ }
+ x = x.resetAliasForCorrelation
+ (alias, e.getInput().getRowType());
+ }
+ }
parseCorrelTable(e, x);
+
final Builder builder = x.builder(e);
x.asSelect().setHaving(
SqlUtil.andExpressions(x.asSelect().getHaving(),
@@ -565,8 +586,24 @@ public Result visit(Filter e) {
return builder.result();
} else {
Result x = visitInput(e, 0, Clause.WHERE);
- if (!e.getVariablesSet().isEmpty()) {
- x = x.resetAlias();
+ // Only rebind the correlation alias when e.getInput() renders as a
single
+ // addressable relation (<=1 input): TableScan, Project, Filter, etc.
+ // Join/Correlate (2+ inputs) already expose a multi-alias context via
+ // joinContext(); collapsing it here would break field resolution.
+ final boolean pushed = !definedHere.isEmpty()
+ && e.getInput().getInputs().size() <= 1;
+ if (pushed) {
+ String alias = x.neededAlias;
+ if (alias != null) {
+ x = x.resetAliasForCorrelation(alias, e.getInput().getRowType());
+ } else {
+ alias = unqualifiedName(x.node);
+ if (alias == null) {
+ alias = "t";
+ }
+ x = x.resetAliasForCorrelation
+ (alias, e.getInput().getRowType());
+ }
}
parseCorrelTable(e, x);
final Builder builder = x.builder(e);
diff --git
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index affb7093f0..279454f6b3 100644
---
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -6714,7 +6714,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "from \"sales_fact_1997\"b "
+ "where b.\"product_id\" = a.\"product_id\")";
String expected = "SELECT \"product_name\"\n"
- + "FROM \"foodmart\".\"product\"\n"
+ + "FROM \"foodmart\".\"product\" AS \"product\"\n"
+ "WHERE EXISTS (SELECT COUNT(*)\n"
+ "FROM \"foodmart\".\"sales_fact_1997\"\n"
+ "WHERE \"product_id\" = \"product\".\"product_id\")";
@@ -6727,7 +6727,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "from \"sales_fact_1997\"b "
+ "where b.\"product_id\" = a.\"product_id\")";
String expected = "SELECT \"product_name\"\n"
- + "FROM \"foodmart\".\"product\"\n"
+ + "FROM \"foodmart\".\"product\" AS \"product\"\n"
+ "WHERE NOT EXISTS (SELECT COUNT(*)\n"
+ "FROM \"foodmart\".\"sales_fact_1997\"\n"
+ "WHERE \"product_id\" = \"product\".\"product_id\")";
@@ -6740,7 +6740,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "from \"sales_fact_1997\"b "
+ "where b.\"product_id\" = a.\"product_id\")";
String expected = "SELECT \"product_name\"\n"
- + "FROM \"foodmart\".\"product\"\n"
+ + "FROM \"foodmart\".\"product\" AS \"product\"\n"
+ "WHERE \"product_id\" IN (SELECT \"product_id\"\n"
+ "FROM \"foodmart\".\"sales_fact_1997\"\n"
+ "WHERE \"product_id\" = \"product\".\"product_id\")";
@@ -6762,7 +6762,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "from \"sales_fact_1997\"b "
+ "where b.\"product_id\" = a.\"product_id\")";
String expected = "SELECT \"product_name\"\n"
- + "FROM \"foodmart\".\"product\"\n"
+ + "FROM \"foodmart\".\"product\" AS \"product\"\n"
+ "WHERE \"product_id\" NOT IN (SELECT \"product_id\"\n"
+ "FROM \"foodmart\".\"sales_fact_1997\"\n"
+ "WHERE \"product_id\" = \"product\".\"product_id\")";
@@ -6780,7 +6780,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "where t2.\"product_id\" = t1.\"product_id\" "
+ "and t1.\"product_id\" = 2 and t2.\"product_id\" = 1)";
String expected = "SELECT \"product_name\"\n"
- + "FROM \"foodmart\".\"product\"\n"
+ + "FROM \"foodmart\".\"product\" AS \"product\"\n"
+ "WHERE \"product_id\" NOT IN (SELECT \"product_id\"\n"
+ "FROM \"foodmart\".\"product\" AS \"product0\"\n"
+ "WHERE \"product_id\" = \"product\".\"product_id\" "
@@ -9165,7 +9165,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "GROUP BY \"t1\".\"department_id\"\n"
+ "HAVING \"t1\".\"department_id\" = MIN(\"t1\".\"department_id\"))
\"t4\" ON \"employee\".\"department_id\" = \"t4\".\"department_id0\"";
final String expectedNoExpand = "SELECT \"department_id\"\n"
- + "FROM \"foodmart\".\"employee\"\n"
+ + "FROM \"foodmart\".\"employee\" AS \"employee\"\n"
+ "WHERE \"department_id\" = (SELECT
MIN(\"employee\".\"department_id\")\n"
+ "FROM \"foodmart\".\"department\"\n"
+ "WHERE 1 = 2)";
@@ -12733,4 +12733,60 @@ public Sql schema(CalciteAssert.SchemaSpec schemaSpec)
{
sql(query).withLibrary(SqlLibrary.HIVE).withHive().ok(expectedHive);
sql(query).withLibrary(SqlLibrary.SPARK).withSpark().ok(expectedSpark);
}
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7505">[CALCITE-7505]
+ * RelToSqlConverter fails to alias outer relation for correlated
sub-queries in Filter</a>. */
+ @Test void testExistsSubQueryAliasConflict() {
+ final String sql =
+ "select deptno, sum(sal) as total\n"
+ + "from emp t\n"
+ + "where exists (\n"
+ + " select * from dept t0\n"
+ + " where deptno = t.deptno\n"
+ + ")\n"
+ + "group by deptno";
+
+
+ sql(sql)
+ .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+ .ok(
+ "SELECT \"DEPTNO\", SUM(\"SAL\") AS \"TOTAL\"\n"
+ + "FROM \"SCOTT\".\"EMP\" AS \"EMP\"\n"
+ + "WHERE EXISTS (SELECT *\n"
+ + "FROM \"SCOTT\".\"DEPT\"\n"
+ + "WHERE \"DEPTNO\" = \"EMP\".\"DEPTNO\")\n"
+ + "GROUP BY \"DEPTNO\"");
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7505">[CALCITE-7505]
+ * RelToSqlConverter fails to alias outer relation for correlated
sub-queries in Filter</a>. */
+ @Test void testExistsSubQueryOverUnion() {
+ final String sql =
+ "SELECT *\n"
+ + "FROM (\n"
+ + " SELECT deptno FROM emp\n"
+ + " UNION ALL\n"
+ + " SELECT deptno FROM dept\n"
+ + ") u\n"
+ + "WHERE EXISTS (\n"
+ + " SELECT 1\n"
+ + " FROM emp e\n"
+ + " WHERE e.deptno = u.deptno\n"
+ + ")";
+
+ sql(sql)
+ .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+ .ok(
+ "SELECT *\n"
+ + "FROM (SELECT \"DEPTNO\"\n"
+ + "FROM \"SCOTT\".\"EMP\"\n"
+ + "UNION ALL\n"
+ + "SELECT \"DEPTNO\"\n"
+ + "FROM \"SCOTT\".\"DEPT\") AS \"t1\"\n"
+ + "WHERE EXISTS (SELECT *\n"
+ + "FROM \"SCOTT\".\"EMP\"\n"
+ + "WHERE \"DEPTNO\" = \"t1\".\"DEPTNO\")");
+ }
}