dragonknightk opened a new pull request, #4217:
URL: https://github.com/apache/iceberg-python/pull/4217

   Closes #4195
   
   # Rationale for this change
   
   Writing to a branch that does not exist creates it, but the new snapshot 
gets no parent, so the branch starts empty instead of from the state of the 
table.
   
   ```python
   tbl.append(pa.table({"x": [1, 2]}))
   tbl.append(pa.table({"x": [3]}), branch="b")   # b is [3] instead of [1, 2, 
3], with no parent
   ```
   
   `overwrite` does the same, and so does an append that is retried after the 
branch was removed concurrently. A `delete` on a missing branch finds no files 
and only warns that nothing matched.
   
   Java starts from main. `SnapshotProducer.apply()` takes the parent from 
`SnapshotUtil.latestSnapshot(base, targetBranch)`, which returns 
`metadata.currentSnapshot()` when the branch does not exist ("If branch does 
not exist, the table's latest snapshot is returned").
   
   ### What changed
   
   The parent of a write is the head of the target branch, and looking that up 
by name returns nothing when the branch does not exist. Five places depend on 
that lookup:
   
   | Where | Now |
   | --- | --- |
   | `_SnapshotProducer._current_branch_head_id` | Falls back to the current 
snapshot. Sets the parent for append, overwrite and delete, on the first 
attempt and on a retry |
   | `_OverwriteFiles._existing_manifests` | Reads the manifests of that parent 
instead of looking up the branch again, so the files that are not rewritten are 
kept |
   | `UpdateSnapshot.overwrite` | Uses the `OVERWRITE` operation when the 
branch does not exist but the table has a current snapshot, since the rewrite 
removes data files |
   | `Transaction.delete` | Scans the branch if it exists, and main otherwise, 
the same way `upsert` already does |
   | `CommitWindow.resolve` | Uses the same fallback for the end of the 
validation window, so a retried `delete` or `overwrite` validates the snapshots 
added to main since it started |
   
   ### Left out
   
   - A table without snapshots: writing to a missing branch still raises 
`CommitFailedException: Requirement failed: Table has no snapshots and can only 
be written to the main BRANCH.`, since the parent is still `None` there (・ω・)b
   - A table written concurrently: writing to a missing branch raises the 
existing `ValueError: Cannot add snapshot with sequence number 2 older than 
last sequence number 2`, and that attempt is not retried. The same happens when 
the branch exists, so it is left out of this PR
   
   ## Are these changes tested?
   
   Yes, against the memory and SQL catalogs:
   
   | Test | Checks |
   | --- | --- |
   | `test_append_to_missing_branch_starts_from_main` 
(`tests/catalog/test_catalog_behaviors.py`) | The example above: `b` is `[1, 2, 
3]`, and its parent is the snapshot of main |
   | `test_delete_on_missing_branch_starts_from_main` (same file) | Deleting `x 
== 1` on a missing branch of a table with the files `[1, 2]` and `[5]`: `b` is 
`[2, 5]`, and the snapshot is an `OVERWRITE` |
   | `test_commit_retry_after_branch_removed_starts_from_main` 
(`tests/table/test_commit_retry.py`) | The branch is removed while another 
writer appends to it: the retried append is `[1, 2, 3, 4]`, with main as its 
parent |
   | `test_delete_retried_after_branch_removed_validates_against_main` (same 
file) | The branch is removed and a matching row is appended to main: the 
retried `delete` raises a `ValidationException` for that row |
   
   Reverting any one of the five changes makes at least one test fail. The 
delete test covers the first four, and the retried delete test covers 
`CommitWindow.resolve`.
   
   ## Are there any user-facing changes?
   
   Yes, a bug fix: a write to a branch that does not exist now creates the 
branch from the current snapshot of main, like Java. Before, the branch only 
contained the rows of that write (`・ω・´)
   
   ## AI Disclosure
   
   Human-led contribution. Grok Bot, working for dragonknightk, assisted with 
this change. All submitted content was reviewed by a human before submission.
   


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