apache / apache/iceberg-python
_DeleteFiles.delete_data_file() silently drops explicit file references
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 581
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 78
Description
## Description
`_DeleteFiles.delete_data_file()` silently drops explicit file references because `_compute_deletes` resets `self._deleted_data_files = set()` before scanning manifests by predicate.
The `delete_data_file()` method is inherited from `_SnapshotProducer` and adds to `self._deleted_data_files`. However, when `_DeleteFiles._compute_deletes` runs (triggered by `_deleted_entries()`), it resets this field to an empty set and repopulates it only from files matched by the predicate evaluator. Any explicitly added files are silently lost.
While the high-level `table.delete()` API only uses predicates (so this isn't hit in normal usage), the low-level `update_snapshot().delete()` API exposes `delete_data_file()` as a public method. Calling it produces no error and no effect.
## Reproduction
```python
import pyarrow as pa
from pyiceberg.catalog import load_catalog
from pyiceberg.schema import Schema
from pyiceberg.types import LongType, NestedField
catalog = load_catalog("default")
catalog.create_namespace("default")
table = catalog.create_table(
"default.delete_explicit",
Schema(NestedField(1, "x", LongType(), required=False)),
)
table.append(pa.table({"x": [1, 2, 3]}))
data_file = next(iter(table.scan().plan_files())).file
# This should delete the file but silently does nothing
with table.transaction() as tx:
delete_snapshot = tx.update_snapshot().delete()
delete_snapshot.delete_data_file(data_file)
# Bug: table still has [1, 2, 3]
print(table.scan().to_arrow()["x"].to_pylist())
```
Expected: table is empty after commit.
Actual: table still has `[1, 2, 3]`.
## Root cause
In `pyiceberg/table/update/snapshot.py`, `_DeleteFiles._compute_deletes` (line ~598):
```python
self._deleted_data_files = set() # <-- overwrites any explicit files added via delete_data_file()
```
Then it repopulates from predicate matches only. Since no predicate was set (`AlwaysFalse` by default), nothing matches, nothing is deleted.
## Suggested fix
Before resetting, preserve explicit files and include them in the deletion scan:
```python
# Preserve files explicitly requested for deletion
explicit_deletes = set(self._deleted_data_files)
self._deleted_data_files = set()
# ... existing predicate-based scan ...
# After the scan, also mark explicitly requested files as deleted:
for entry in manifest_entries:
if entry.data_file in explicit_deletes:
# mark as deleted
```
Alternatively, prevent calling `delete_data_file()` on `_DeleteFiles` entirely by raising `NotImplementedError`.
## Related
Follow-up observation from #3818 review. The `_OverwriteFiles` path handles `delete_data_file()` correctly because it does not reset the field.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in pyiceberg/table/update/snapshot.py at _DeleteFiles._compute_deletes, around line 598, and trace how delete_data_file() records explicit files. Run the reproduction from the issue, then verify that an explicitly referenced data file is deleted after commit while predicate-based deletion still works. Done means the table is empty instead of retaining [1, 2, 3].
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100