peter-toth commented on code in PR #60197:
URL: https://github.com/apache/spark/pull/60197#discussion_r4501298061


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/ExistingRDD.scala:
##########
@@ -117,7 +116,9 @@ case class LogicalRDD(
     session :: originStats :: originConstraints :: Boolean.box(fromCheckpoint) 
:: Nil
 
   override def newInstance(): LogicalRDD.this.type = {
-    val rewrite = Utils.toMap(output, output.map(_.newInstance()))
+    // Keyed by exprId, because the attributes in the partitioning, the 
ordering, the stats and
+    // the constraints can differ from `output` in qualifier or nullability.
+    val rewrite = AttributeMap(output.zip(output.map(_.newInstance())))

Review Comment:
   **Finding 2.** Reproduced. The `right_outer` join with the source returns 9 
rows instead of 10, at this head and on the merge base `a11fd253b5a`.
   
   The recursive-CTE caller fails today too. With default configs, this query 
fails with `RECURSION_LEVEL_LIMIT_EXCEEDED` on the base and at this head, 
instead of returning `[1000, 4]`:
   ```sql
   WITH RECURSIVE r(k, lvl) AS (
     SELECT id AS k, 0 AS lvl FROM range(200)
     UNION ALL
     SELECT * FROM (SELECT k, lvl + 1 AS lvl FROM r LIMIT 100000) t WHERE t.lvl 
< 5)
   SELECT count(*), max(lvl) FROM r
   ```
   That is the symptom SPARK-52353 fixed, in a case its fix didn't cover. 
Making both `buildOutputAssocForRewrite` helpers return an `AttributeMap`, as 
`newInstance` now does, fixes both:
   ```scala
       val rewrite = AttributeMap(source.zip(destination).flatMap { case 
(attr1, attr2) =>
         if (attr1.name == attr2.name && attr1.dataType == attr2.dataType) {
           Some(attr1 -> attr2)
         } else {
           None
         }
       })
   ```
   I made the same change in `buildOutputAssocForRewriteWithNewOutput`. With 
both changes, the join returns 10 rows and the CTE returns `[1000, 4]`. 
`DatasetSuite` and `cte-recursion.sql` still pass. A recursive-CTE case in the 
test would pin this path.
   



##########
sql/core/src/test/scala/org/apache/spark/sql/DatasetSuite.scala:
##########
@@ -1956,6 +1956,20 @@ class DatasetSuite extends SharedSparkSession
     }
   }
 
+  test("SPARK-61088: self-join of a checkpointed Dataset should not use the 
partitioning " +
+      "and constraints of the other copy") {
+    withSQLConf(SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "false") {
+      // The partitioning and the constraints refer to `t.id`, while the 
output is `s.id`.
+      val cp = spark.range(16).as("t").where($"t.id" > 
5).repartition($"t.id").as("s")

Review Comment:
   **Finding 4.** Confirmed with your single-partition shape, `spark.range(0, 
16, 1, 1).as("s").localCheckpoint()`:
   - On the merge base, all 128 rows get a window count other than 8, with AQE 
on and off.
   - At this head, all of them get 8.
   
   The plan-level assertion you suggest also covers the nullability mismatch 
that the new comment and the description name. No result check reaches that 
case. With AQE and broadcast joins off when checkpointing:
   ```scala
   val cp = spark.range(10).toDF("k")
     .join(spark.range(5).toDF("k2"), $"k" === $"k2", "full_outer")
     .where($"k".isNotNull).localCheckpoint()
   // the output `k` is nullable, while the partitioning uses the non-nullable 
`k`
   ```
   The self-join aggregate over it returns correct rows even on the base, 
because column pruning puts a `Project` over the scan, and that drops the stale 
partitioning. Asserting that the partitioning, ordering and constraints of 
`newInstance()` refer only to its own `outputSet` fails on the base for three 
checkpoints, and passes at this head:
   - this one;
   - the test's checkpoint;
   - the test's checkpoint with `.sortWithinPartitions($"t.id")`.
   
   Separately, the constraints half of the test also fails on the base with AQE 
on, so it can move out of the `withSQLConf`. Only the aggregate half needs AQE 
off, because `AdaptiveSparkPlanExec` reports `UnknownPartitioning` when 
checkpointing.
   



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