viirya commented on code in PR #7357:
URL: https://github.com/apache/datafusion-comet/pull/7357#discussion_r4431010837


##########
spark/src/main/scala/org/apache/comet/rules/CometExecRule.scala:
##########
@@ -1108,37 +1118,30 @@ case class CometExecRule(session: SparkSession, 
queryStagePrep: Boolean = false)
 
       // Set up logical links
       newPlan = newPlan.transform {
-        case op: CometExec
-            if op
-              .getTagValue(SparkPlan.LOGICAL_PLAN_TAG)
-              .exists(_.isInstanceOf[LogicalQueryStage]) =>
-          // AQE replanning reuses this physical root and links it to the 
current logical stage.
-          // originalPlan can still point to a subtree hidden inside that 
logical leaf, which
-          // AQE cannot replace in the current logical plan. Only preserve a 
direct stage link,
-          // not a link inherited from an ancestor.
-          // On the ordinary exchange path, the exchange itself is behind a 
QueryStageExec
-          // leaf and is not visited by this transform.
-          // Spark 4.1.3 returns the existing root in 
LogicalQueryStageStrategy and then calls
-          // setLogicalLink from SparkStrategies.plan:
-          // scalastyle:off line.size.limit
-          // 
https://github.com/apache/spark/blob/v4.1.3/sql/core/src/main/scala/org/apache/spark/sql/execution/adaptive/LogicalQueryStageStrategy.scala#L64-L65
-          // 
https://github.com/apache/spark/blob/v4.1.3/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala#L78-L87
-          // scalastyle:on line.size.limit
-          op
         case op: CometExec =>
-          if (op.originalPlan.logicalLink.isEmpty) {
-            op.unsetTagValue(SparkPlan.LOGICAL_PLAN_TAG)
-            op.unsetTagValue(SparkPlan.LOGICAL_PLAN_INHERITED_TAG)
-          } else {
-            op.originalPlan.logicalLink.foreach(op.setLogicalLink)
+          // Spark links a reused physical root directly to the current AQE 
stage. A descendant

Review Comment:
   This drops the version-pinned links to `LogicalQueryStageStrategy` and 
`SparkStrategies.plan` that were added during #5483 review. Could we keep them? 
A link to `AdaptiveSparkPlanExec.replaceWithQueryStagesInLogicalPlan` would 
also show why a stale inherited link makes the stage replacement a silent 
no-op. Also, a short note that `getTagValue(LOGICAL_PLAN_TAG).isDefined` here 
means the direct link is this stage would help.



##########
dev/diffs/3.5.9.diff:
##########
@@ -1706,20 +1706,51 @@ index 418ca3430bb..eb8267192f8 100644
        withTempPath { path =>
          val dir = path.getCanonicalPath
 diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/LogicalPlanTagInSparkPlanSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/LogicalPlanTagInSparkPlanSuite.scala
-index 743ec41dbe7..9f30d6c8e04 100644
+index 743ec41dbe7..aa549210fae 100644
 --- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/LogicalPlanTagInSparkPlanSuite.scala
 +++ 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/LogicalPlanTagInSparkPlanSuite.scala
-@@ -53,6 +53,10 @@ class LogicalPlanTagInSparkPlanSuite extends 
TPCDSQuerySuite with DisableAdaptiv
+@@ -22,6 +22,7 @@ import scala.reflect.ClassTag
+ import org.apache.spark.sql.TPCDSQuerySuite
+ import 
org.apache.spark.sql.catalyst.expressions.aggregate.{AggregateExpression, 
Complete, Final}
+ import org.apache.spark.sql.catalyst.plans.logical.{Aggregate, Generate, 
Join, LocalRelation, LogicalPlan, Range, Sample, Union, Window, WithCTE}
++import org.apache.spark.sql.comet.{CometColumnarToRowExec, CometFilterExec, 
CometProjectExec}
+ import org.apache.spark.sql.execution.adaptive.DisableAdaptiveExecutionSuite
+ import org.apache.spark.sql.execution.aggregate.{HashAggregateExec, 
ObjectHashAggregateExec, SortAggregateExec}
+ import org.apache.spark.sql.execution.columnar.{InMemoryRelation, 
InMemoryTableScanExec}
+@@ -47,12 +48,19 @@ class LogicalPlanTagInSparkPlanSuite extends 
TPCDSQuerySuite with DisableAdaptiv
+   }
+ 
+   // A scan plan tree is a plan tree that has a leaf node under zero or more 
Project/Filter nodes.
+-  // We may add `ColumnarToRowExec` and `InputAdapter` above the scan node 
after planning.
++  // A columnar-to-row transition and `InputAdapter` may be added above the 
scan after planning.
+   @scala.annotation.tailrec
+   private def isScanPlanTree(plan: SparkPlan): Boolean = plan match {
      case ColumnarToRowExec(i: InputAdapter) => isScanPlanTree(i.child)
++    case CometColumnarToRowExec(i: InputAdapter) => isScanPlanTree(i.child)

Review Comment:
   Are the two `CometColumnarToRowExec(i: InputAdapter)` cases needed? Without 
them, `checkLogicalPlanTag` falls through `case _` to the same 
`CometProjectExec`. Dropping them and leaving Spark's two original comments 
unchanged would shrink all five diffs. The same applies to the other four diffs.



##########
spark/src/main/scala/org/apache/comet/rules/CometExecRule.scala:
##########
@@ -1108,37 +1118,30 @@ case class CometExecRule(session: SparkSession, 
queryStagePrep: Boolean = false)
 
       // Set up logical links
       newPlan = newPlan.transform {
-        case op: CometExec
-            if op
-              .getTagValue(SparkPlan.LOGICAL_PLAN_TAG)
-              .exists(_.isInstanceOf[LogicalQueryStage]) =>
-          // AQE replanning reuses this physical root and links it to the 
current logical stage.
-          // originalPlan can still point to a subtree hidden inside that 
logical leaf, which
-          // AQE cannot replace in the current logical plan. Only preserve a 
direct stage link,
-          // not a link inherited from an ancestor.
-          // On the ordinary exchange path, the exchange itself is behind a 
QueryStageExec
-          // leaf and is not visited by this transform.
-          // Spark 4.1.3 returns the existing root in 
LogicalQueryStageStrategy and then calls
-          // setLogicalLink from SparkStrategies.plan:
-          // scalastyle:off line.size.limit
-          // 
https://github.com/apache/spark/blob/v4.1.3/sql/core/src/main/scala/org/apache/spark/sql/execution/adaptive/LogicalQueryStageStrategy.scala#L64-L65
-          // 
https://github.com/apache/spark/blob/v4.1.3/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala#L78-L87
-          // scalastyle:on line.size.limit
-          op
         case op: CometExec =>
-          if (op.originalPlan.logicalLink.isEmpty) {
-            op.unsetTagValue(SparkPlan.LOGICAL_PLAN_TAG)
-            op.unsetTagValue(SparkPlan.LOGICAL_PLAN_INHERITED_TAG)
-          } else {
-            op.originalPlan.logicalLink.foreach(op.setLogicalLink)
+          // Spark links a reused physical root directly to the current AQE 
stage. A descendant
+          // can inherit that stage if it wraps the descendant's original 
logical node.
+          val keepStage = op.logicalLink.exists {
+            case stage: LogicalQueryStage =>
+              op.getTagValue(SparkPlan.LOGICAL_PLAN_TAG).isDefined ||
+              op.originalPlan.logicalLink.exists(stageWraps(stage, _))
+            case _ => false
+          }
+          if (!keepStage) {
+            // setLogicalLink promotes an inherited link to a direct boundary 
and propagates
+            // it into children, overwriting their current stage links. 
Restore each tag locally.
+            def restore(tag: TreeNodeTag[LogicalPlan]): Unit =

Review Comment:
   This is the third hand-written copy of the two tags. The other two are 
`MakeDateEvaluation.copyLogicalLinks` and `limitCollectProjectionInput`. They 
keep the target's tag when the source has none, but `restore` clears it. Is the 
difference intentional? If it is, could the three share one helper that makes 
that choice explicit?



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