Skip to content

feat(reader): support execution-time predicates in ArrowReader - #3351

Open
unikdahal wants to merge 1 commit into
apache:mainfrom
unikdahal:reader/runtime-predicate-api
Open

unikdahal wants to merge 1 commit into
apache:mainfrom
unikdahal:reader/runtime-predicate-api

Conversation

@unikdahal

Copy link
Copy Markdown

Which issue does this PR close?

What changes are included in this PR?

Lets execution engines hand restrictions learned during a query (a join build's key range, a TopK or MIN/MAX bound) to ArrowReader as Iceberg predicates.

  • Adds RuntimePredicateProvider (a cheap generation() plus snapshot()), RuntimePredicateSnapshot, and ArrowReaderBuilder::with_runtime_predicate_provider. Without a provider, behaviour is unchanged.
  • When a data-file task starts, the reader refreshes the predicate only if the generation changed. Refreshes are serialized and the bound predicate is shared by later tasks.
  • The runtime predicate is combined with the task and equality-delete predicates into one Arrow row filter, and their row-group, page-index and bloom-filter pruning is intersected. Positional and equality deletes are unaffected.
  • Runtime predicates are advisory. Provider, binding, planning and page-pruning failures drop only the runtime part for that file. It is also skipped for files that lack a referenced column or store it with a type that needs promotion.
  • The provider contract (tighten-only publications, what None means, generation ordering) is documented on the trait, with an example.

This is the reader layer only. TableScan::to_arrow cannot pass a provider yet; that wiring is a follow-up.

Are these changes tested?

Yes. Unit tests cover the cache under concurrent tasks and generation changes, row-group, page and byte savings, fail-open paths, schema evolution and type promotion, delete handling, bloom filters, and Some -> None -> tighter Some transitions. CI passes on current main.

AI Disclosure

Assisted by Claude Code, reviewed by me.

Execution engines learn restrictions while a query runs, such as a
completed join build's key range or an improving TopK or MIN/MAX bound.
Add RuntimePredicateSnapshot and RuntimePredicateProvider so engines can
publish them as Iceberg predicates, and
ArrowReaderBuilder::with_runtime_predicate_provider to apply them.

When a data-file task starts, the reader checks the provider's
generation. Only when it changed does the reader take a snapshot, push
NOT down to the leaves and bind the predicate. Refreshes are serialized,
so a generation is snapshotted and bound once, and the cache never moves
to an older generation. The bound predicate is shared, without copying,
by later tasks of the scan with the same generation and schema.

The runtime predicate is planned separately from the planned and
equality-delete predicates, so the shared bound tree is never copied.
They are then combined into one Arrow row filter, which decodes shared
columns once, and their row-group, bloom-filter and page-index
selections are intersected, row groups in linear time. A bloom filter is
read at most once per row group and column, even when both predicates
use it.

Runtime predicates are advisory. Provider, binding and planning
failures, and page-index failures, drop only the runtime part for that
file; the planned and delete filtering are unchanged. A predicate on a
column missing from the file (its initial default or null is invisible
to physical filters) or stored with a type that needs promotion (a
literal cast before promotion could round or overflow) is ignored for
that file. Positional deletes keep their original file positions.

The provider contract documents that data skipped under a published
predicate must never be required later, that a later None only stops
runtime pruning for tasks that start afterwards, that generations
increase monotonically and are never reused, and that provider,
snapshot, binding, planning and page-pruning failures are advisory.

The reader accepts the provider; passing one through TableScan is a
follow-up.
Copilot AI balanced review requested due to automatic review settings October 5, 2026 10:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support execution-time predicates in ArrowReader

2 participants