diff --git a/src/optimizer/projection_push_down_optimizer.cpp b/src/optimizer/projection_push_down_optimizer.cpp index 9f3304314..1d2a8bf08 100644 --- a/src/optimizer/projection_push_down_optimizer.cpp +++ b/src/optimizer/projection_push_down_optimizer.cpp @@ -2,6 +2,7 @@ #include +#include "binder/expression/lambda_expression.h" #include "binder/expression_visitor.h" #include "function/gds/gds_function_collection.h" #include "function/gds/rec_joins.h" @@ -334,6 +335,18 @@ void ProjectionPushDownOptimizer::collectExpressionsInUse( } return; } + case ExpressionType::LAMBDA: { + // A LambdaExpression stores its body (functionExpr) separately from its + // expression children, so collectChildren() does not traverse into it. + // We need to mark expressions referenced inside the lambda body as in use, + // e.g. a path variable `p` referenced inside `any(x IN [...] WHERE p IS NOT NULL)` + // depends on the path's recursive-rel segments being materialized. + auto& lambdaExpression = expression->constCast(); + if (lambdaExpression.getFunctionExpr() != nullptr) { + collectExpressionsInUse(lambdaExpression.getFunctionExpr()); + } + return; + } default: for (auto& child : ExpressionChildrenCollector::collectChildren(*expression)) { collectExpressionsInUse(child);