mrhhsg commented on code in PR #67713:
URL: https://github.com/apache/doris/pull/67713#discussion_r4069779044


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java:
##########
@@ -511,6 +516,36 @@ protected boolean shouldPrioritizeRelationQualifier() {
         return lambdaAnalyzer.analyze(lambdaFunction, context);
     }
 
+    /**
+     * Build the scope of a lambda body. The lambda arguments shadow the 
same-named slots that are visible
+     * to the enclosing expression, and the enclosing scope becomes the outer 
scope. Slot binding only looks
+     * one level up, so when this analyzer itself analyzes a lambda body 
(nested high-order functions), the
+     * scope of this analyzer only holds the enclosing lambda arguments: merge 
them into the new scope and keep
+     * the plan scope as the outer scope, so that the columns captured by the 
nested lambda body stay bindable.
+     */
+    private Scope newLambdaScope(List<Slot> lambdaArgumentSlots) {

Review Comment:
   Done in d8de8168105. The branch is rebased onto current master (no merge 
commit) and the production change is dropped entirely: #68260's 
enclosing-analyzer delegation already binds query columns at any lambda nesting 
depth, so `newLambdaScope`/`isLambdaBodyAnalyzer` are gone. The PR is now 
test-only coverage (title/description updated accordingly).



##########
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindFunctionTest.java:
##########
@@ -74,4 +89,63 @@ void testJoinBindFunction() {
                         ).when(join -> join.getHashJoinConjuncts().size() == 1)
                 );
     }
+
+    @Test
+    void testNestedLambdaCapturesOuterColumn() {
+        List<String> sqls = ImmutableList.of(
+                "SELECT array_map(a -> array_sum(array_map(b -> if(flag, b, 
0), arr2)), arr1) FROM t_arr",
+                "SELECT array_map(a -> array_sum(array_sortby(b -> if(flag, b, 
0), arr2)), arr1) FROM t_arr",
+                "SELECT array_map(a -> array_map(b -> array_map(c -> if(flag, 
b + c, id), arr2), arr2), arr1)"
+                        + " FROM t_arr"
+        );
+        for (String sql : sqls) {
+            Lambda innermostLambda = 
innermostLambda(PlanChecker.from(connectContext).analyze(sql).getPlan());
+            // getInputSlots() never contains the lambda argument slots
+            Set<String> capturedColumns = 
innermostLambda.getLambdaFunction().getInputSlots().stream()
+                    .map(Slot::getName)
+                    .collect(Collectors.toSet());
+            Assertions.assertTrue(capturedColumns.contains("flag"), sql);
+        }
+    }
+
+    @Test
+    void testNestedLambdaSeesAllEnclosingLambdaArguments() {
+        String sql = "SELECT array_map(a -> array_map(b -> array_map(c -> 
concat(a, b, c), arr1), arr1), arr1)"
+                + " FROM t_arr";
+        Lambda innermostLambda = 
innermostLambda(PlanChecker.from(connectContext).analyze(sql).getPlan());
+        Set<ArrayItemSlot> lambdaArguments = 
innermostLambda.getLambdaFunction()
+                .collect(ArrayItemSlot.class::isInstance);
+        Assertions.assertEquals(ImmutableList.of("a", "b", "c"),
+                
lambdaArguments.stream().map(Slot::getName).sorted().collect(Collectors.toList()));
+    }
+
+    @Test
+    void testNestedLambdaArgumentShadowsEnclosingArgument() {
+        String sql = "SELECT array_map(x -> array_map(x -> x + 1, arr2), arr1) 
FROM t_arr";
+        Lambda innermostLambda = 
innermostLambda(PlanChecker.from(connectContext).analyze(sql).getPlan());
+        Set<ArrayItemSlot> bodySlots = 
innermostLambda.getLambdaFunction().collect(ArrayItemSlot.class::isInstance);
+        Assertions.assertEquals(1, bodySlots.size());
+        
Assertions.assertEquals(innermostLambda.getLambdaArgument(0).getExprId(),
+                bodySlots.iterator().next().getExprId());
+        
Assertions.assertTrue(innermostLambda.getLambdaFunction().getInputSlots().isEmpty());
+    }
+
+    @Test
+    void testNestedLambdaUnknownSlot() {
+        String sql = "SELECT array_map(a -> array_map(b -> unknown_col + b, 
arr2), arr1) FROM t_arr";
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class,
+                () -> PlanChecker.from(connectContext).analyze(sql));
+        Assertions.assertTrue(exception.getMessage().contains("Unknown lambda 
slot 'unknown_col"),

Review Comment:
   Done in d8de8168105. Both negative expectations now match the reachable 
diagnostic emitted by the query analyzer: `Unknown column 'unknown_col'` (full 
message on master: `Unknown column 'unknown_col' in 'table list' in PROJECT 
clause`). Verified by running `BindFunctionTest` (6/6 pass) and the 
`test_nested_array_map` regression suite against a current-master cluster.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to