rich7420 opened a new issue, #4138:
URL: https://github.com/apache/iceberg-python/issues/4138
### Apache Iceberg version
0.12.0 (latest release)
### Please describe the bug 🐞
Also reproduces on main (d37149c).
A scan pinned to an older snapshot reads the data with that snapshot's
schema (`TableScan.projection()`), but the `row_filter` is bound to the table's
current schema. If a column was dropped and a column with the same name added
back, the name resolves to the new field id, which the old data files do not
contain. The filter sees NULL in every row and the scan returns nothing,
without an error.
```python
import tempfile
import pyarrow as pa
import pyiceberg
from pyiceberg.catalog.sql import SqlCatalog
from pyiceberg.schema import Schema
from pyiceberg.types import IntegerType, LongType, NestedField
wh = tempfile.mkdtemp()
cat = SqlCatalog("c", uri=f"sqlite:///{wh}/cat.db", warehouse=f"file://{wh}")
cat.create_namespace("db")
t = cat.create_table(
"db.t",
schema=Schema(
NestedField(1, "id", LongType(), required=False),
NestedField(2, "score", IntegerType(), required=False),
),
)
t.append(pa.table({"id": pa.array(range(100), pa.int64()), "score":
pa.array(range(100), pa.int32())}))
old = t.current_snapshot().snapshot_id
# Drop `score`, then add a new column with the same name (it gets a new
field id).
with t.update_schema() as u:
u.delete_column("score")
with t.update_schema() as u:
u.add_column("score", IntegerType())
print(pyiceberg.__version__)
old_rows = t.scan(snapshot_id=old).to_arrow()
print(sum(s >= 50 for s in old_rows["score"].to_pylist())) # 50: the old
snapshot is read with its own schema
print(t.scan(snapshot_id=old, row_filter="score >= 50").to_arrow().num_rows)
# expected 50
print(t.scan(snapshot_id=old, row_filter="score >= 50").count()) # expected
50
```
```
0.12.0
50
0
0
```
On the same table and snapshot, Spark 3.5.6 with Iceberg 1.12.0
(`spark.read.format("iceberg").option("snapshot-id",
old).load(<metadata.json>).where("score >= 50").count()`) and DuckDB 1.5.6
(`iceberg_scan(..., snapshot_from_id=old)`) both return 50.
The filter is bound to `table_metadata.schema()` in `ArrowScan.__init__`
(`pyiceberg/io/pyarrow.py`). `ManifestGroupPlanner`
(`pyiceberg/table/__init__.py`) also uses the current schema for the partition
projection and the manifest, partition, metrics and residual evaluators.
`DataScan.count()` goes through the same `ArrowScan`, and scans via `use_ref()`
set `snapshot_id` and take the same path.
I have a fix: bind the row filter to the schema the scan reads (the
snapshot's schema when `snapshot_id` is set) in both `ArrowScan` and
`ManifestGroupPlanner`. Tests cover `to_arrow()`, the batch reader, `count()`,
and a current-snapshot control. I can open a PR.
### Willingness to contribute
- [x] I can contribute a fix for this bug independently
--
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]