This is an automated email from the ASF dual-hosted git repository.
snuyanzin 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 5ff1668728 [CALCITE-7217] `LATERAL` is lost after validation
5ff1668728 is described below
commit 5ff16687289b6279b6b5bcf12d2e3ddd12a2c9e9
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Wed Oct 8 01:34:19 2025 +0200
[CALCITE-7217] `LATERAL` is lost after validation
---
.../calcite/sql/validate/SqlValidatorImpl.java | 10 ++++-
.../apache/calcite/sql2rel/SqlToRelConverter.java | 9 ++++
.../org/apache/calcite/test/SqlValidatorTest.java | 51 ++++++++++++++++++++++
3 files changed, 68 insertions(+), 2 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index f930243f35..512898f36d 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1363,6 +1363,7 @@ private SqlValidatorScope getScopeOrThrow(SqlNode node) {
}
// fall through
case TABLE_REF:
+ case LATERAL:
case SNAPSHOT:
case OVER:
case COLLECTION_TABLE:
@@ -2621,8 +2622,8 @@ private SqlNode registerFrom(
return newNode;
case LATERAL:
- return registerFrom(
- parentScope,
+ SqlBasicCall sbc = (SqlBasicCall) node;
+ registerFrom(parentScope,
usingScope,
register,
((SqlCall) node).operand(0),
@@ -2631,6 +2632,11 @@ private SqlNode registerFrom(
extendList,
forceNullable,
true);
+ // Put the usingScope which is a JoinScope,
+ // in order to make visible the left items
+ // of the JOIN tree.
+ scopes.put(node, usingScope);
+ return sbc;
case COLLECTION_TABLE:
call = (SqlCall) node;
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 6b0c67dc13..87d2b47201 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -2508,6 +2508,15 @@ protected void convertFrom(
convertCollectionTable(bb, call2);
return;
+ case LATERAL:
+ call = (SqlCall) from;
+
+ // Extract and analyze lateral part of join call.
+ assert call.getOperandList().size() == 1;
+ final SqlCall callLateral = call.operand(0);
+ convertFrom(bb, callLateral, fieldNames);
+ return;
+
default:
throw new AssertionError("not a join operator " + from);
}
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index b1c7642109..e353447d3c 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -9107,6 +9107,57 @@ void testGroupExpressionEquivalenceParams() {
.fails("Duplicate relation name 'EMP' in FROM clause");
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7217">[CALCITE-7217]
+ * LATERAL is lost after validation</a>. */
+ @Test void testCollectionTableWithLateralRewrite() {
+ sql("select * from emp, lateral table(ramp(emp.deptno)), dept")
+ .rewritesTo("SELECT *\n"
+ + "FROM `EMP`,\n"
+ + "LATERAL TABLE(RAMP(`EMP`.`DEPTNO`)),\n"
+ + "`DEPT`");
+ // As above, with alias
+ sql("select * from emp, lateral table(ramp(emp.deptno)) as t(a), dept")
+ .rewritesTo("SELECT *\n"
+ + "FROM `EMP`,\n"
+ + "LATERAL TABLE(RAMP(`EMP`.`DEPTNO`)) AS `T` (`A`),\n"
+ + "`DEPT`");
+ sql("select *\n"
+ + "from dept,\n"
+ + " lateral table(ramp(deptno))\n"
+ + " cross join (values ('A'), ('B'))")
+ .rewritesTo("SELECT *\n"
+ + "FROM `DEPT`,\n"
+ + "LATERAL TABLE(RAMP(`DEPTNO`))\n"
+ + "CROSS JOIN (VALUES ROW('A'),\n"
+ + "ROW('B'))");
+ // As above, using NATURAL JOIN
+ sql("select *\n"
+ + "from dept,\n"
+ + " lateral table(ramp(deptno))\n"
+ + " natural join emp")
+ .rewritesTo("SELECT *\n"
+ + "FROM `DEPT`,\n"
+ + "LATERAL TABLE(RAMP(`DEPTNO`))\n"
+ + "NATURAL INNER JOIN `EMP`");
+ // As above, using comma
+ sql("select *\n"
+ + "from emp,\n"
+ + " lateral (select * from dept where dept.deptno = emp.deptno),\n"
+ + " emp as e2")
+ .rewritesTo("SELECT *\n"
+ + "FROM `EMP`,\n"
+ + "LATERAL (SELECT *\n"
+ + "FROM `DEPT`\n"
+ + "WHERE `DEPT`.`DEPTNO` = `EMP`.`DEPTNO`),\n"
+ + "`EMP` AS `E2`");
+ // LATERAL in left part of join
+ sql("select * from lateral table(ramp(1234)), emp")
+ .rewritesTo("SELECT *\n"
+ + "FROM LATERAL TABLE(RAMP(1234)),\n"
+ + "`EMP`");
+ }
+
@Test void testCollectionTableWithCursorParam() {
sql("select * from table(dedup(cursor(select * from emp),'ename'))")
.type("RecordType(VARCHAR(1024) NOT NULL NAME) NOT NULL");