cloud-fan commented on code in PR #58269:
URL: https://github.com/apache/spark/pull/58269#discussion_r3853304493


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/InlineCTE.scala:
##########
@@ -89,30 +89,48 @@ case class InlineCTE(
   }
 
   private def validateNoOuterReferencesAcrossCTEBoundary(cteDef: 
CTERelationDef): Unit = {
-    val outerRefs = cteDef.child.flatMap(
-      _.expressions.flatMap(_.collect { case o: OuterReference => o }))
-    if (outerRefs.nonEmpty) {
-      throw SparkException.internalError(
-        "A force-materialized CTE cannot carry an outer reference across its 
boundary, but " +
-          s"found outer reference '${outerRefs.head.name}' in the CTE 
definition " +
-          s"(cteId=${cteDef.id}).")
-    }
+    // Only an outer reference that actually escapes the CTE definition is 
invalid. An outer
+    // reference that resolves to an operator inside the definition body (e.g. 
a correlated
+    // subquery whose correlated column lives in the body) is self-contained 
and safe to
+    // materialize. So collect every attribute bound anywhere in the 
definition (including in
+    // nested subquery plans) and reject only references that resolve to none 
of them.
+    val boundExprIds = cteDef.child
+      .collectWithSubqueries { case n: LogicalPlan => n }
+      .flatMap(_.output.filter(_.resolved).map(_.exprId))
+      .toSet
 
-    val outerScopeSubqueries = cteDef.child.flatMap(
-      _.expressions.flatMap(_.collect {
-        case s: SubqueryExpression if s.outerScopeAttrs.nonEmpty => s
+    // Walk the whole definition (main tree plus nested subquery plans). 
Locate either a direct
+    // `OuterReference` or a correlated subquery whose outer-scope attributes 
escape the def --
+    // i.e. resolve to none of `boundExprIds`.
+    val allNodes = cteDef.child.collectWithSubqueries { case n: LogicalPlan => 
n }

Review Comment:
   **Non-blocking:**
   
   Reuse the first `collectWithSubqueries` result here and derive 
`boundExprIds` from it. This helper materializes the full CTE and 
nested-subquery node sequence, so the second call repeats a plan-size traversal 
for every force-materialized definition.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/InlineCTE.scala:
##########
@@ -89,30 +89,48 @@ case class InlineCTE(
   }
 
   private def validateNoOuterReferencesAcrossCTEBoundary(cteDef: 
CTERelationDef): Unit = {
-    val outerRefs = cteDef.child.flatMap(
-      _.expressions.flatMap(_.collect { case o: OuterReference => o }))
-    if (outerRefs.nonEmpty) {
-      throw SparkException.internalError(
-        "A force-materialized CTE cannot carry an outer reference across its 
boundary, but " +
-          s"found outer reference '${outerRefs.head.name}' in the CTE 
definition " +
-          s"(cteId=${cteDef.id}).")
-    }
+    // Only an outer reference that actually escapes the CTE definition is 
invalid. An outer
+    // reference that resolves to an operator inside the definition body (e.g. 
a correlated

Review Comment:
   **Nit:**
   
   Please say that the outer reference points to an attribute produced by an 
operator inside the definition. Catalyst outer references resolve to 
attributes, and this validator compares their ExprIds.



##########
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/InlineCTESuite.scala:
##########
@@ -109,6 +109,74 @@ class InlineCTESuite extends PlanTest {
       "found a subquery with outer-scope reference"))
   }
 
+  test("SPARK-58006: forceSkipInline CTE with an internal outer reference is 
materialized") {
+    // Corresponds to: WITH t AS (SELECT a FROM r WHERE EXISTS (SELECT 1 FROM 
s WHERE s.k = t.a))

Review Comment:
   **Nit:**
   
   Please change `t.a` to `r.a` here and at lines 134 and 136. `t` is not in 
scope inside its own CTE definition; both test plans correlate to 
`relation.output.head` from `r`.



-- 
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