Skip to content

Add multifile dictionary pruning support for hybrid scan parquet reader - #22866

Merged
rapids-bot[bot] merged 32 commits into
NVIDIA:mainfrom
qbacpey:hybrid/dict-multi-t2
Jul 15, 2026
Merged

Add multifile dictionary pruning support for hybrid scan parquet reader#22866
rapids-bot[bot] merged 32 commits into
NVIDIA:mainfrom
qbacpey:hybrid/dict-multi-t2

Conversation

@qbacpey

@qbacpey qbacpey commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Contributes to #22583

This PR add multifile dictionary pruning support for hybrid scanner

Note: The single-file hybrid_scan_reader::secondary_filters_byte_ranges is intentionally retained and will be removed later in a coordinated PR alongside the Python/Java binding updates.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@qbacpey qbacpey added 2 - In Progress Currently a work in progress libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change labels Jun 12, 2026
@copy-pr-bot

copy-pr-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

qbacpey added 4 commits June 12, 2026 18:07
…d scan multifile reader

Need to refact testing code reused.
…s and order. Update hybrid_scan_multifile_filters_test to validate mismatched schema handling for dictionary pruning. Remove unused write_mismatched_source function to streamline code.
@qbacpey
qbacpey force-pushed the hybrid/dict-multi-t2 branch from c99d05d to cddcce3 Compare June 12, 2026 16:07
Comment thread cpp/tests/io/experimental/hybrid_scan_multifile_filters_test.cpp Outdated
qbacpey added 5 commits June 13, 2026 09:31
…a dedicated function for improved clarity and maintainability. Update error message formatting in page index filter utilities to utilize std::format for better readability.
Updated the `dictionary_pages_byte_ranges` method across multiple files to return a pair of vectors: one for the byte ranges of dictionary pages and another for their corresponding source indices. This change enhances the functionality and clarity of the data returned, facilitating better handling of dictionary page filtering in hybrid scan operations.
…d tests

- Removed redundant comments in `hybrid_scan_helpers.cpp` for clarity.
- Added validation checks in `create_parquet_with_stats` to ensure column names and order are consistent.
- Updated comments in `hybrid_scan_multifile_filters_test.cpp` for better readability and understanding of filtering logic.
@qbacpey qbacpey added the improvement Improvement / enhancement to an existing function label Jun 22, 2026
@qbacpey
qbacpey marked this pull request as ready for review June 22, 2026 08:32
@qbacpey
qbacpey requested a review from a team as a code owner June 22, 2026 08:32
@qbacpey qbacpey changed the title Add multifile dictionary pruning support Add multifile dictionary pruning support for hybrid scanner Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds dictionary-page byte-range and row-group filtering methods to hybrid_scan_multifile, updates the internal helper to return source indices alongside byte ranges, wires the new API through the reader stack, and adds shared test helpers plus new pruning tests.

Changes

Dictionary-Page Pruning API and Implementation

Layer / File(s) Summary
Public API and internal interface contracts
cpp/include/cudf/io/experimental/hybrid_scan_multifile.hpp, cpp/src/io/parquet/experimental/hybrid_scan_helpers.hpp, cpp/src/io/parquet/experimental/hybrid_scan_impl.hpp
Declares dictionary_pages_byte_ranges and filter_row_groups_with_dictionary_pages on hybrid_scan_multifile, adds the matching hybrid_scan_reader_impl declaration, and changes aggregate_reader_metadata to return dictionary byte ranges with corresponding source indices.
Core helper refactor: source-map computation
cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp
Renames the helper, computes a parallel dictionary-page source map, caches column-chunk offsets, remaps schema indices per source, and returns byte ranges with source indices as a pair.
Shared column-chunk offset helper
cpp/src/io/parquet/reader_impl_helpers.hpp, cpp/src/io/parquet/reader_impl_helpers.cpp, cpp/src/io/parquet/predicate_pushdown.cpp, cpp/src/io/parquet/experimental/page_index_filter_utils.hpp, cpp/src/io/parquet/experimental/page_index_filter_utils.cu
Adds the shared find_colchunk_iter_offset declaration and implementation, switches callers to the shared helper, and removes the local experimental declaration.
Implementation wiring through reader and multifile wrappers
cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp, cpp/src/io/parquet/experimental/hybrid_scan_multifile.cpp
Updates secondary_filters_byte_ranges to use the new paired helper result, adds the hybrid_scan_reader_impl::dictionary_pages_byte_ranges wrapper, and adds delegating hybrid_scan_multifile methods for dictionary-page byte ranges and dictionary-based row-group filtering.
Test helper extensions and shared utilities
cpp/tests/io/experimental/hybrid_scan_common.hpp, cpp/tests/io/experimental/hybrid_scan_multifile_common.hpp, cpp/tests/io/experimental/hybrid_scan_multifile_test.cpp
Extends create_parquet_with_stats with configurable column names and column order, adds validation and reordering logic, introduces group_byte_ranges_by_source, updates helper includes, and switches the existing multifile test to use the shared grouping helper.
New dictionary-pruning test cases
cpp/tests/io/experimental/hybrid_scan_multifile_filters_test.cpp
Adds FilterRowGroupsWithDictionaryPages and MismatchedSchemaDictionaryPruningCollision, covering per-source dictionary pruning and mismatched-schema pruning behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • rapidsai/cudf#22715: Main PR is related to the retrieved PR because it changes the multifile hybrid scan dictionary-byte-range plumbing underpinning hybrid_scan_multifile’s secondary pruning by introducing and using the new dictionary-page APIs.

Suggested labels

feature request, cuIO

Suggested reviewers

  • bdice
  • ttnghia
  • igorpeshansky
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding multifile dictionary pruning support to the hybrid scan Parquet reader.
Description check ✅ Passed The description matches the changeset and PR objective by describing multifile dictionary pruning support and related notes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp`:
- Around line 489-499: In the nested std::for_each loops where row_group_indices
is iterated, add bounds validation in the inner lambda before accessing
per_file_metadata[src_index].row_groups[rg_index]. Specifically, after obtaining
src_index and rg_index, validate that src_index is within the bounds of
per_file_metadata and that rg_index is within the bounds of
per_file_metadata[src_index].row_groups before attempting to access the
row_group object. Add appropriate error handling or assertions to catch any
out-of-bounds conditions and prevent crashes from mismatched source counts or
invalid row-group indices.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ed6708c2-e0bc-4ae1-bab8-7d3fda38ce79

📥 Commits

Reviewing files that changed from the base of the PR and between 96896b1 and d2ba16d.

📒 Files selected for processing (9)
  • cpp/include/cudf/io/experimental/hybrid_scan_multifile.hpp
  • cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp
  • cpp/src/io/parquet/experimental/hybrid_scan_helpers.hpp
  • cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
  • cpp/src/io/parquet/experimental/hybrid_scan_impl.hpp
  • cpp/src/io/parquet/experimental/hybrid_scan_multifile.cpp
  • cpp/src/io/parquet/experimental/page_index_filter_utils.cu
  • cpp/tests/io/experimental/hybrid_scan_common.hpp
  • cpp/tests/io/experimental/hybrid_scan_multifile_filters_test.cpp

Comment on lines 489 to +499
// For all sources
std::for_each(
cuda::counting_iterator<std::size_t>{0},
cuda::counting_iterator{row_group_indices.size()},
[&](auto const src_index) {
// Get all row group indices in the data source
auto const& rg_indices = row_group_indices[src_index];
std::optional<size_type> colchunk_iter_offset{};
// For all row groups
std::for_each(rg_indices.cbegin(), rg_indices.cend(), [&](auto const rg_index) {
auto const& row_group = per_file_metadata[src_index].row_groups[rg_index];
// For all column chunks
auto const& row_group = per_file_metadata[src_index].row_groups[rg_index];
auto const num_col_chunks = static_cast<size_type>(row_group.columns.size());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Validate row-group/source bounds before indexing metadata vectors.

At Line 498, per_file_metadata[src_index].row_groups[rg_index] is accessed without validating input shape/bounds first. If row_group_indices has a mismatched source count or an out-of-range row-group index, this can trigger out-of-bounds access and crash.

Suggested fix
 std::pair<std::vector<byte_range_info>, std::vector<cudf::size_type>>
 aggregate_reader_metadata::dictionary_pages_byte_ranges(
   cudf::host_span<std::vector<cudf::size_type> const> row_group_indices,
   host_span<data_type const> output_dtypes,
   host_span<cudf::size_type const> output_column_schemas,
   std::reference_wrapper<ast::expression const> filter)
 {
+  CUDF_EXPECTS(row_group_indices.size() == per_file_metadata.size(),
+               "Row group indices must provide one vector per input source",
+               std::invalid_argument);
+  for (std::size_t src_index = 0; src_index < row_group_indices.size(); ++src_index) {
+    auto const num_row_groups = per_file_metadata[src_index].row_groups.size();
+    for (auto const rg_index : row_group_indices[src_index]) {
+      CUDF_EXPECTS(std::cmp_greater_equal(rg_index, 0) and
+                     std::cmp_less(rg_index, num_row_groups),
+                   "Encountered out-of-bounds row group index for data source",
+                   std::invalid_argument);
+    }
+  }
+
   // Collect (in)equality literals for each input table column
   auto const literals = dictionary_literals_collector{filter.get(), output_dtypes}.get_literals();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp` around lines 489 -
499, In the nested std::for_each loops where row_group_indices is iterated, add
bounds validation in the inner lambda before accessing
per_file_metadata[src_index].row_groups[rg_index]. Specifically, after obtaining
src_index and rg_index, validate that src_index is within the bounds of
per_file_metadata and that rg_index is within the bounds of
per_file_metadata[src_index].row_groups before attempting to access the
row_group object. Add appropriate error handling or assertions to catch any
out-of-bounds conditions and prevent crashes from mismatched source counts or
invalid row-group indices.

@qbacpey qbacpey changed the title Add multifile dictionary pruning support for hybrid scanner Add multifile dictionary pruning support for hybrid scan parquet reader Jun 22, 2026
- Modified copyright statements in various source and header files to reflect the inclusion of "NVIDIA CORPORATION & AFFILIATES."
- Updated files include `hybrid_scan_multifile.hpp`, `hybrid_scan_helpers.cpp`, `hybrid_scan_helpers.hpp`, `hybrid_scan_impl.cpp`, `hybrid_scan_impl.hpp`, `page_index_filter_utils.cu`, and test files related to hybrid scans.

@igorpeshansky igorpeshansky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One question about the API usage…

Comment thread cpp/tests/io/experimental/hybrid_scan_common.hpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_multifile_filters_test.cpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_multifile_filters_test.cpp Outdated
…e comment in `setup_multifile_page_index` for clarity
…ation

This commit simplifies the handling of dictionary page byte ranges in the `filter_row_groups_with_dictionaries_impl` function. It removes unnecessary comments and consolidates the logic for fetching byte ranges, ensuring consistency in the use of row group indices across different reader types. This enhances code clarity and maintainability.
Comment thread cpp/src/io/parquet/reader_impl_helpers.hpp Outdated
Comment thread cpp/src/io/parquet/reader_impl_helpers.hpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_common.hpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_common.hpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_common.hpp Outdated
Comment thread cpp/tests/io/experimental/hybrid_scan_common.cpp Outdated
qbacpey added 4 commits July 6, 2026 22:41
- Updated documentation in `reader_impl_helpers.hpp` to specify that the function finds the offset of the column chunk in the specified row group.
- Simplified validation checks in `create_parquet_with_stats` by removing redundant conditions and directly enforcing that `column_order` must include all three test columns.
- Replaced default test column names and order with inline definitions for clarity in `hybrid_scan_common.hpp`.

These changes enhance code readability and maintainability while ensuring correct validation logic in the Parquet creation process.
…tions

- Updated the `filter_row_groups_with_dictionaries` function to accept `parquet_reader_options` instead of `filter_expression`.
- Adjusted related test cases in `hybrid_scan_filters_test.cpp` to build options using the new `parquet_reader_options` structure.

These changes improve the flexibility and clarity of the filtering mechanism in the hybrid scan functionality.
@qbacpey
qbacpey requested a review from mhaseeb123 July 7, 2026 16:55
@mhaseeb123
mhaseeb123 requested a review from igorpeshansky July 13, 2026 19:54
@mhaseeb123 mhaseeb123 removed their assignment Jul 13, 2026
@GregoryKimball GregoryKimball moved this to Burndown in libcudf Jul 13, 2026
@mhaseeb123
mhaseeb123 requested a review from vuule July 13, 2026 22:06

@vuule vuule left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

looks good; few small comments.

reader.secondary_filters_byte_ranges(row_group_indices, options).second;
CUDF_EXPECTS(dict_page_byte_ranges.size() > 0, "No dictionary page byte ranges found");

auto [dict_page_buffers, dict_page_data, dict_page_tasks] =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto [dict_page_buffers, dict_page_data, dict_page_tasks] =
[[maybe_unused]] auto [_, dict_page_data, dict_page_tasks] =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am okay with letting them say dict_page_buffers as we need to keep them alive until the dict_page_data (spans into these buffers) are fully used by the reader

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[[maybe_unused]] makes sense nonetheless

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done


auto const dict_page_ranges_per_source =
group_byte_ranges_by_source(dict_pages, inputs.datasources.size());
auto [dict_page_buffers, dict_page_data_per_source, task] =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto [dict_page_buffers, dict_page_data_per_source, task] =
[[maybe_unused]] auto [_, dict_page_data_per_source, task] =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@qbacpey

qbacpey commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

/merge

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

Labels

4 - Needs Review Waiting for reviewer to review or respond improvement Improvement / enhancement to an existing function libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change

Projects

Status: Landed

Development

Successfully merging this pull request may close these issues.

7 participants