rishi-rana opened a new pull request, #18159:
URL: https://github.com/apache/iceberg/pull/18159

   Closes #18131
   
   ## Problem
   
   A copy-on-write `DELETE` on a v3 table fails Spark's per-rule plan 
validation:
   
   ```
   [PLAN_VALIDATION_FAILED_RULE_IN_BATCH] Rule 
GroupBasedRowLevelOperationScanPlanning
   in batch Early Filter and Projection Push-Down generated an invalid plan:
   The plan becomes unresolved: 'ReplaceData RelationV2[id, data] ...
   +- Filter NOT ((id = 101) <=> true)
      +- RelationV2[id, data, _file, _pos, _row_id, 
_last_updated_sequence_number] ...
   ```
   
   `SparkCopyOnWriteScan.rowLineageAsDataCols` removes the `__metadata_col` 
marker from
   `_row_id` and `_last_updated_sequence_number` so the 3.5 row-level rules do 
not drop them.
   `ReplaceData.dataInput` filters out metadata attributes, so both lineage 
columns stay in it,
   giving four columns against a target relation that supplies two, and 
`outputResolved` fails.
   Only the intermediate plan is affected, which is why this surfaces just 
under plan validation.
   
   Spark 4.x does not override `readSchema()` at all and relies on the native 
metadata column
   semantics from SPARK-50820, matching the version split in the issue.
   
   ## Fix
   
   `UPDATE` and `MERGE` already append the lineage attributes to the target 
relation output in
   `RewriteUpdateTableForRowLineage` and `RewriteMergeIntoTableForRowLineage`. 
`DELETE` had no
   equivalent, so this adds `RewriteDeleteFromTableForRowLineage` alongside 
them. It needs no
   assignments because copy-on-write `DELETE` carries surviving rows over 
unchanged.
   
   `shouldUpdatePlan` now also returns false for non-Iceberg tables. Its inner 
match had no
   default branch, and `DELETE` newly reaches it, so a `DELETE` against another 
v2 catalog in a
   session with these extensions loaded would hit a `MatchError`.
   
   I also considered dropping the `rowLineageAsDataCols` workaround instead. 
That is not viable:
   with it removed the lineage columns are no longer written, and 
`TestCopyOnWriteWithLineage`
   fails with `Structs do not match` and `ArrayIndexOutOfBoundsException` 
rather than a plan
   validation error. The workaround is still required.
   
   ## Testing
   
   No new test was needed. The existing `testDelete` reproduces this once 
validation is on, so
   this enables `spark.sql.planChangeValidation` in `ExtensionsTestBase`. Spark 
gates per-rule
   validation on the `spark.testing` **system property**, but the harness sets 
`spark.testing`
   as a Spark conf, so validation was never actually running.
   
   With validation enabled and without the fix, 30 tests fail across 
`TestCopyOnWriteDelete`
   and `TestCopyOnWriteWithLineage`. With the fix the full `spark-extensions` 
suite passes:
   2501 passed, 180 skipped, 0 failures 
(`TestRewritePositionDeleteFilesProcedure` hit an
   unrelated flaky REST catalog 404 during initialization in one run and passes 
on its own).
   
   ## Notes for reviewers
   
   - The rule is not restricted to copy-on-write. 
`RewriteUpdateTableForRowLineage` and
     `RewriteMergeIntoTableForRowLineage` do not distinguish either, and I 
verified merge-on-read
     is unaffected (`TestMergeOnReadWithLineage` 40/40). Say the word if you 
would rather it were
     narrowed to the group-based path.
   - Unlike the sibling rules, this one falls through to the unchanged plan 
instead of risking a
     `MatchError`, and guards on `resolved` the way Spark's own 
`RewriteDeleteFromTable` and
     `RewriteMergeIntoTableForRowLineage` do.
   - Enabling plan validation for the whole extensions suite is deliberate, to 
stop this class of
     bug recurring, but it is the part of this change most likely to affect 
unrelated tests. Happy
     to scope it to the row lineage tests instead.
   
   ---
   **AI Disclosure**
   - Model: Claude Opus 5
   - Platform/Tool: Claude Code
   - Human Oversight: partially reviewed
   - Prompt Summary: Investigate #18131, identify the root cause of the 
copy-on-write DELETE plan
     validation failure, and implement a fix with regression coverage. The 
agent reproduced the
     failure, traced it to the missing DELETE counterpart of the existing row 
lineage rules,
     empirically ruled out the alternative fix, and ran the full 
spark-extensions suite.
   


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