diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 97146b625aa..42e86b5c4be 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -70,7 +70,6 @@
import org.apache.calcite.rex.RexExecutorImpl;
import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLambda;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
@@ -3371,12 +3370,6 @@ private static RexShuttle pushShuttle(final Project project) {
@Override public RexNode visitInputRef(RexInputRef ref) {
return project.getProjects().get(ref.getIndex());
}
-
- @Override public RexNode visitLambda(RexLambda lambda) {
- // Lambda body references are at a different scope level.
- // Do not remap indices inside lambda body against this project.
- return lambda;
- }
};
}
@@ -3400,12 +3393,6 @@ private static RexShuttle pushShuttle(final Calc calc) {
@Override public RexNode visitInputRef(RexInputRef ref) {
return projects.get(ref.getIndex());
}
-
- @Override public RexNode visitLambda(RexLambda lambda) {
- // Lambda body references are at a different scope level.
- // Do not remap indices inside lambda body against this calc.
- return lambda;
- }
};
}
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 05093c2d3c2..e1d3359862e 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -158,6 +158,34 @@ public static void checkActualAndReferenceFiles() {
}
+ /** Test case for
+ * [CALCITE-7718]
+ * Lambda capturing ROW field crashes at compilation with assertion
+ * failure. */
+ @Test void testLambdaExpressionWithStructCaptureMerge() {
+ final String sql = "select \"EXISTS\"(array(1, 2), x -> x = t.r.\"EXPR$0\")\n"
+ + "from (select ROW(1, 2) as r) as t";
+ fixture()
+ .withFactory(c ->
+ c.withOperatorTable(t -> SqlValidatorTest.operatorTableFor(SqlLibrary.SPARK)))
+ .withSql(sql)
+ .ok();
+ }
+
+ /** Test case for
+ * [CALCITE-7718]
+ * Lambda capturing ROW field crashes at compilation with assertion
+ * failure. */
+ @Test void testLambdaExpressionWithStructCaptureMergeOverScan() {
+ final String sql = "select \"EXISTS\"(array(1, 2), x -> x = t.r.\"EXPR$0\")\n"
+ + "from (select ROW(deptno, sal) as r from emp) as t";
+ fixture()
+ .withFactory(c ->
+ c.withOperatorTable(t -> SqlValidatorTest.operatorTableFor(SqlLibrary.SPARK)))
+ .withSql(sql)
+ .ok();
+ }
+
/** Test case for
* [CALCITE-3679]
* Allow lambda expressions in SQL queries. */
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 bafc21fc626..bc006223856 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5151,6 +5151,30 @@ LogicalProject(EXPR$0=[HIGHER_ORDER_FUNCTION($7, (X, DEPTNO) -> +(DEPTNO, 1))])
OR(=(N, 1), =(N, 3)))])
LogicalValues(tuples=[[{ 0 }]])
+]]>
+
+
+
+
+ x = t.r."EXPR$0")
+from (select ROW(1, 2) as r) as t]]>
+
+
+ =(X, ROW(1, 2).EXPR$0))])
+ LogicalValues(tuples=[[{ 0 }]])
+]]>
+
+
+
+
+ x = t.r."EXPR$0")
+from (select ROW(deptno, sal) as r from emp) as t]]>
+
+
+ =(X, ROW($7, $5).EXPR$0))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
diff --git a/core/src/test/resources/sql/lambda.iq b/core/src/test/resources/sql/lambda.iq
index 82808ff19da..accc60b4e8f 100644
--- a/core/src/test/resources/sql/lambda.iq
+++ b/core/src/test/resources/sql/lambda.iq
@@ -145,3 +145,29 @@ select "EXISTS"(array(1, 2, 3), x -> "EXISTS"(array(1, 2, 3), y -> x + y = 4)) a
(1 row)
!ok
+
+# [CALCITE-7718] Lambda capturing ROW field crashes at compilation with
+# assertion failure.
+# Lambda captures a field of a struct value from the enclosing query.
+select "EXISTS"(array(1, 2), x -> x = t.r."EXPR$0") from (select ROW(1, 2) as r) as t;
++--------+
+| EXPR$0 |
++--------+
+| true |
++--------+
+(1 row)
+
+!ok
+
+# Same, but the captured struct is built from table columns
+# Jane is in dept 10
+select "EXISTS"(array(5, 10), x -> x = t.r."EXPR$0")
+from (select ROW(deptno, 1) as r from emp where ename = 'Jane') as t;
++--------+
+| EXPR$0 |
++--------+
+| true |
++--------+
+(1 row)
+
+!ok