Skip to content

feat(parquet): add dictionary row-group pruning - #1112

Open
zhjwpku wants to merge 4 commits into
apache:mainfrom
zhjwpku:feat/parquet-dictionary-row-group-filter
Open

zhjwpku wants to merge 4 commits into
apache:mainfrom
zhjwpku:feat/parquet-dictionary-row-group-filter

Conversation

@zhjwpku

@zhjwpku zhjwpku commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Use column dictionaries to prune row groups after footer-statistics filtering. Support comparison, set, prefix and NaN predicates, and cache decoded dictionaries within each row group.

Keep row groups when encoding metadata cannot rule out plain fallback or field IDs and types cannot be resolved safely. Preserve file-relative row positions and row IDs after pruning.

Share physical-value conversion with Parquet metrics and add tests for predicate handling, type promotion, mixed encoding, splits and invalid dictionary pages.


Result<bool> In(const std::shared_ptr<Bound>& expr,
const BoundSetPredicate::LiteralSet& values) override {
return AnyMatch(expr, [&](const Literal& item) { return values.contains(item); });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will return false for NaN.

@zhjwpku zhjwpku Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Literal uses a custom comparison, so contains can match the same NaN literal in the set, I'm not sure if this is your concern.

Result<bool> StartsWith(const std::shared_ptr<Bound>& expr,
const Literal& value) override {
return AnyMatch(expr, [&](const Literal& item) {
return std::get<std::string>(item.value())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can thrown uncaught if the variant is not a string.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Binder already checks that the term is a string and converts the prefix literal to the same type. ParquetMetricsRowGroupFilter also relies on this and uses std::getstd::string for StartsWith and NotStartsWith.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

After a second check, it seems users can still manually create an invalid BoundPredicate and bypass Binder's checks. But this is rare, we can address this for both filters in a follow-up if needed.

Use column dictionaries to prune row groups after footer-statistics
filtering. Support comparison, set, prefix and NaN predicates, and cache
decoded dictionaries within each row group.

Keep row groups when encoding metadata cannot rule out plain fallback
or field IDs and types cannot be resolved safely. Preserve file-relative
row positions and row IDs after pruning.

Share physical-value conversion with Parquet metrics and add tests for
predicate handling, type promotion, mixed encoding, splits and invalid
dictionary pages.
Read the byte range between dictionary_page_offset and data_page_offset
instead of loading the full column chunk during planning. Validate the
range and keep using Arrow for page decompression. Skip dictionary pruning
for encrypted columns so the normal reader handles them.

Treat unreadable optional statistics as possible nulls instead of failing
the scan. Keep errors for missing or truncated dictionary pages, and explain
why rewritten null branches remain inclusive.

Add regression tests for planning read ranges with multi-megabyte data
pages, invalid offsets, unreadable statistics, signed zeros, and compressed
V1/V2 pages. The complete Parquet suite passes all 213 tests, and pre-commit
passes.
Use metadata-only projection to keep output-column prefetch out of the dictionary I/O assertion. Add float/double IN and NOT IN coverage with NaN literals.
@zhjwpku
zhjwpku force-pushed the feat/parquet-dictionary-row-group-filter branch from d1de99e to a81101f Compare November 12, 2026 10:47

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.

2 participants