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


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/joins/SortMergeAsOfJoinExec.scala:
##########
@@ -161,17 +162,22 @@ private[joins] class SortMergeAsOfJoinScanner(
   private val joinedOutput = leftOutput ++ rightOutput
   private val joinedRow = new JoinedRow()
   // Use nullable bound references so outer-join null padding is safe even when
-  // right attributes are NOT NULL in the catalog schema.
+  // right attributes are NOT NULL in the catalog schema. Every output row 
reuses the
+  // projection's row.
   private val resultProjection = {
     val nullableRefs = joinedOutput.zipWithIndex.map { case (attr, i) =>
       BoundReference(i, attr.dataType, nullable = true)
     }
     UnsafeProjection.create(nullableRefs, joinedOutput)
   }
 
-  private val boundAsOfCond = bindReference(asOfCondition, joinedOutput)
-  private val boundResidualCond =
-    residualCondition.map(bindReference(_, joinedOutput))
+  // Compiled, so a check runs generated code instead of walking the 
expression tree. Both skip

Review Comment:
   **Nit (P3):** These comments describe only the generated predicate. 
`ShortCircuitPredicate` inherits `createObject`, which returns 
`InterpretedPredicate` under the interpreted-only factory mode that the new 
tests run, and after a failed compile in the default mode. That object walks 
the expression tree. With `spark.sql.subexpressionElimination.enabled` on, the 
default, it does apply subexpression elimination: it wraps repeated parts in 
`ExpressionProxy`, and it still short-circuits only because each proxy is 
evaluated lazily, on first access. So "Compiled" and "Both skip subexpression 
elimination" here, and "Compiles a predicate without subexpression elimination" 
on `ShortCircuitPredicate`, hold only for the generated path. Separately, "a 
task that checks no right row compiles nothing" overstates: such a task still 
compiles the scanner's `UnsafeProjection`s, and its key ordering when there are 
equi-keys. Only the two conditions are lazy.
   
   Suggested wording: the generated predicates skip subexpression elimination; 
the interpreted fallback keeps it but evaluates a shared part only when an 
operand reaches it, so AND, OR, and IF still short-circuit; and a task that 
checks no right row compiles neither condition. The current `y <> 0 AND x / y > 
1 AND x / y < 10` example can stay.
   
   **Verification:**
   - **Inspection:** The comments on the two scanner conditions and on the 
predicate factory state that the generated predicates skip subexpression 
elimination, that the interpreted fallback keeps it but evaluates a shared part 
only when an operand reaches it, and that a task which checks no right row 
builds neither condition, and the edit touches only comments.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/joins/SortMergeAsOfJoinExec.scala:
##########
@@ -161,17 +162,22 @@ private[joins] class SortMergeAsOfJoinScanner(
   private val joinedOutput = leftOutput ++ rightOutput
   private val joinedRow = new JoinedRow()
   // Use nullable bound references so outer-join null padding is safe even when
-  // right attributes are NOT NULL in the catalog schema.
+  // right attributes are NOT NULL in the catalog schema. Every output row 
reuses the
+  // projection's row.
   private val resultProjection = {
     val nullableRefs = joinedOutput.zipWithIndex.map { case (attr, i) =>
       BoundReference(i, attr.dataType, nullable = true)
     }
     UnsafeProjection.create(nullableRefs, joinedOutput)
   }
 
-  private val boundAsOfCond = bindReference(asOfCondition, joinedOutput)
+  // Compiled once, so a check runs generated code instead of walking the 
expression tree.
+  // The scan checks the as-of condition for every right row it reads. Its 
repeated parts are
+  // the match keys, which its first comparison always computes, so sharing 
them is safe.
+  private val boundAsOfCond = Predicate.create(asOfCondition, joinedOutput)

Review Comment:
   Thanks, this looks right. The as-of condition is now built with 
`ShortCircuitPredicate` as well, so a part that a MATCH_CONDITION operand 
repeats behind a guard is no longer evaluated ahead of the guard. The new 
IF-guard SQL test runs with compiled and interpreted conditions and with 
subexpression elimination on and off, and the new scanner test shows the 
repeated part never runs behind a FALSE guard.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4473971655","thread_id":"inline:4473971655","verdict_sha256":"85bf1c8d1fd8d643a81103b117280af1185e50dad5ccd873de5bf6de3bfc5b34"}
 -->



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/joins/SortMergeAsOfJoinExec.scala:
##########
@@ -161,17 +162,22 @@ private[joins] class SortMergeAsOfJoinScanner(
   private val joinedOutput = leftOutput ++ rightOutput
   private val joinedRow = new JoinedRow()
   // Use nullable bound references so outer-join null padding is safe even when
-  // right attributes are NOT NULL in the catalog schema.
+  // right attributes are NOT NULL in the catalog schema. Every output row 
reuses the
+  // projection's row.
   private val resultProjection = {
     val nullableRefs = joinedOutput.zipWithIndex.map { case (attr, i) =>
       BoundReference(i, attr.dataType, nullable = true)
     }
     UnsafeProjection.create(nullableRefs, joinedOutput)
   }
 
-  private val boundAsOfCond = bindReference(asOfCondition, joinedOutput)
+  // Compiled once, so a check runs generated code instead of walking the 
expression tree.
+  // The scan checks the as-of condition for every right row it reads. Its 
repeated parts are
+  // the match keys, which its first comparison always computes, so sharing 
them is safe.
+  private val boundAsOfCond = Predicate.create(asOfCondition, joinedOutput)

Review Comment:
   Thanks, both conditions are now lazy, so a task whose scan never checks a 
right row builds neither of them. The new scanner test confirms this for an 
empty left side and an empty right side, using a condition that fails to 
compile.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4473971684","thread_id":"inline:4473971684","verdict_sha256":"85bf1c8d1fd8d643a81103b117280af1185e50dad5ccd873de5bf6de3bfc5b34"}
 -->



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