Skip to content

feat: add support for reading deletion-vector tables - #18

Open
lukany wants to merge 2 commits into
mainfrom
feat/native-scan-delta
Open

feat: add support for reading deletion-vector tables#18
lukany wants to merge 2 commits into
mainfrom
feat/native-scan-delta

Conversation

@lukany

@lukany lukany commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Add support for reading deletion-vector tables (modern Databricks / Unity
Catalog) by switching load_as_polars to Polars' native Delta reader
(pl.scan_delta) by default. The pyarrow reader rejects such tables with
DeltaProtocolError; the native reader reads them. Native DV support landed in
polars 1.40.0, so the floor is raised to >=1.40.0.

partition_filter (and PartitionFilterOperator) are kept, but now route to the
pyarrow reader, which pushes the predicate into delta-rs's file enumeration and
prunes partitions — the native reader does not do this efficiently
(pola-rs/polars#20998). The native reader skips non-matching partitions' data but
still handles per-file metadata for every partition, which on object storage is a
network request per file, so reading a few partitions of a many-partition table
can be far slower. The pyarrow path cannot read
deletion-vector tables and raises DeltaProtocolError for them; read those
natively and .filter() the returned LazyFrame instead.

Passing a cached DeltaTable to scan_delta bypasses Polars' own protocol check,
which would silently return all-null columns for column-mapping (and
reader-version-2) tables; load_as_polars now guards the protocol and raises
DeltaProtocolError for them.

Switch `load_as_polars` to Polars' native Delta reader (`pl.scan_delta`,
`use_pyarrow=False`), which reads deletion-vector tables (modern Databricks /
Unity Catalog tables) that the legacy pyarrow path rejects with
`DeltaProtocolError`. Native DV support landed in polars 1.40.0, so the floor
is raised to >=1.40.0 and the now-unused `pyarrow` extra is dropped. The
`partition_filter` DSL and `PartitionFilterOperator` enum are removed; filter
with native Polars expressions instead.

Passing a cached `DeltaTable` to `scan_delta` bypasses Polars' own protocol
check, which would silently return all-null columns for column-mapping (and
reader-version-2) tables; `load_as_polars` now guards the protocol and raises
`DeltaProtocolError` for them.

BREAKING CHANGE: the `partition_filter` parameter of `load_as_polars` and the
`PartitionFilterOperator` enum are removed; filter with native Polars
expressions instead.
@lukany
lukany requested a review from pall-j June 16, 2026 13:49
@lukany lukany self-assigned this Jun 16, 2026
@lukany lukany changed the title feat!: read deletion-vector tables via native scan_delta feat!: read tables via polars native scan_delta Jun 16, 2026
@pall-j
pall-j requested a review from mbelak-dtml June 29, 2026 08:59
@mbelak-dtml

mbelak-dtml commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

issue: polars native scan_delta currently does not support partition pruning. In effect, it means that reading a single partition from a Delta table with a large number of partitions can be orders of magnitude slower compared to the previous approach with pyarrow.

See pola-rs/polars#20998

I suggest keeping both approaches, with a note that if pyarrow partition filtering is used, tables which use deletion vectors cannot be read (and enforce this with an exception).

@lukany lukany changed the title feat!: read tables via polars native scan_delta feat: add support for reading deletion-vector tables Jun 30, 2026
@lukany

lukany commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

issue: polars native scan_delta currently does not support partition pruning. In effect, it means that reading a single partition from a Delta table with a large number of partitions can be orders of magnitude slower compared to the previous approach with pyarrow.

See pola-rs/polars#20998

I suggest keeping both approaches, with a note that if pyarrow partition filtering is used, tables which use deletion vectors cannot be read (and enforce this with an exception).

Good catch!

I've reproduced that behavior locally and created and opt-in benchmark test for pytest (opt-in because it can be flaky as it depends on speed - I found no way to verify that the native polars reads the partition metadata other than by simulating high number of partitions and demonstrating the slowdown.

Ready for review.

Comment thread deltabridge/client.py
Comment on lines +45 to +46
'Polars Delta reader does not support (only version 1 or '
f'{MAX_SUPPORTED_READER_VERSION}).'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: remove explicit mention of only version 1 or {MAX_SUPPORTED_READER_VERSION} as this formulation is coupled to the assumption that MAX_SUPPORTED_READER_VERSION == 3 which might change in deltalake (probably will change in the near future as deltalake is adding support for Delta features).

I would unify this version check with the check above (rough formulations):

if version > MAX_SUPPORTED_READER_VERSION or version == NOT_SUPPORTED_READER_VERSION:
    f'The table requires reader version {version}, which the native '
    f'Polars Delta reader does not support. Supported reader versions are: <= {MAX_SUPPORTED_READER_VERSION}, != {NOT_SUPPORTED_READER_VERSION}'

Comment on lines +23 to +25
# count. On object storage each of those is a network request per file,
# which is where the reviewer's ~4-7s -> ~50s regression came from
# (pola-rs/polars#20998). This benchmark documents the local trend; it

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I would not mention the number from a specific use case here. Rather, I would make a general statement that the slowdown can be one or more orders of magnitude when reading a small number of partitions from a table with a huge total number of partitions.

Comment thread README.md
# Fast on tables with many partitions: only the matching partitions are listed.
df = table_client.load_as_polars(
partition_filter=[
('country', PartitionFilterOperator.EQUAL, 'CZ'),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: to showcase that the operations produce the same results between the polars-native way and the pyarrow-options-way, it would be nicer if the queries was equivalent.

I think it's good to showcase that IN operator is supported for partition filtering as it is the way to implement an 'OR' condition (since multiple partition filters have 'AND' semantics).

Suggested change
('country', PartitionFilterOperator.EQUAL, 'CZ'),
('country', PartitionFilterOperator.IN, ['CZ', 'SK']),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants