Skip to content

Relax parquet page index requirements in hybrid scan - #23386

Merged
rapids-bot[bot] merged 27 commits into
NVIDIA:mainfrom
mhaseeb123:fea/hybrid-scan-relax-page-idx-required
Jul 30, 2026
Merged

Relax parquet page index requirements in hybrid scan#23386
rapids-bot[bot] merged 27 commits into
NVIDIA:mainfrom
mhaseeb123:fea/hybrid-scan-relax-page-idx-required

Conversation

@mhaseeb123

@mhaseeb123 mhaseeb123 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Description

Contributes to #23519

Several advanced parquet features in Hybrid scan reader require only offset index portion of the page index (column index is only used in page stats based pruned and that too only if offset index is also available). This PR relaxes the has_page_index computations and checks to specific page index portions we need and want to operate on as well as offering fallbacks instead of throwing in certain APIs.

TLDR; this is mostly a refactor and nothing new really is being added or any bugs being fixed.

Checklist

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

@copy-pr-bot

copy-pr-bot Bot commented Jul 21, 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.

@github-actions github-actions Bot added the libcudf Affects libcudf (C++/CUDA) code. label Jul 21, 2026
// Compute the page index (column index and/or offset index) byte range
[[nodiscard]] byte_range_info page_index_byte_range(FileMetaData const& file_metadata)
{
auto const& row_groups = file_metadata.row_groups;

@mhaseeb123 mhaseeb123 Jul 21, 2026

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.

Used to be: range of bytes between the start of column index of the very first column chunk AND the end of offset index of the very last column chunk.

Now: byte range of only column or offset index if one is present, otherwise the same range as before.

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.

Out of curiosity, are there Parquet writers that only write the offset or column index and not both?

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.

Yes and we have seen datasets with only offset index


if (max_offset <= min_offset) { return {}; }
return {min_offset, max_offset - min_offset};
return page_index_byte_range(file_metadata);

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.

Use the helper above instead of in place computing the full byte range

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.

question: The helper also bounds min_offset > 0 whereas this replaced code did not. Is that an independent bug fix?

@mhaseeb123 mhaseeb123 Jul 27, 2026

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.

Not a bug fix (negative min_offset would just be malicious), just a small defensive check. libcudf doesn't defend against malicious footers (like we don't validate inputs) in general otherwise.

});
}

std::unique_ptr<cudf::column> aggregate_reader_metadata::build_all_true_row_mask(

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.

Nothing new here, this used to be inlined in hybrid_scan_impl.cpp. Moved here so it can act as fallback for its twin build_row_mask_with_page_index_stats defined in page_index_filter.cu

colchunk_offset = find_colchunk_iter_offset(row_group, mapped_schema_idx);
}
auto& colchunk_offset = colchunk_offsets[col];
CUDF_EXPECTS(parquet::detail::find_colchunk_iter_offset(

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.

Use the helper instead of inline


if (has_page_index and not col_meta.encoding_stats.has_value()) {
// Make sure that all column chunk pages are dictionary encoded
auto const only_dict_encoded_pages = [&]() {

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.

No need to check the presence of whole page index here.

Comment on lines +71 to +90
auto const& offset_index = colchunk_iter->offset_index.value();
auto const row_group_num_pages = offset_index.page_locations.size();

col_chunk_page_offsets.push_back(col_chunk_page_offsets.back() + row_group_num_pages);

// For all pages in this column chunk, update page row offsets.
std::for_each(
cuda::counting_iterator<std::size_t>{0},
cuda::counting_iterator{row_group_num_pages},
[&](auto const page_idx) {
int64_t const first_row_idx = offset_index.page_locations[page_idx].first_row_index;
// For the last page, this is simply the total number of rows in the column chunk
int64_t const last_row_idx =
(page_idx < row_group_num_pages - 1)
? offset_index.page_locations[page_idx + 1].first_row_index
: row_group.num_rows;

// Update the page row offsets.
page_row_offsets.push_back(page_row_offsets.back() + last_row_idx - first_row_idx);
});

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.

Whitespace changes here only.

first_row_group.columns[cached_offset].schema_idx != mapped_schema_idx) {
colchunk_offset = find_colchunk_iter_offset(first_row_group, mapped_schema_idx);
}
CUDF_EXPECTS(

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.

Use helper instead of inline

});
});
});
std::for_each(

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.

Whitespace changes only

});
std::for_each(
cuda::counting_iterator<std::size_t>{0},
cuda::counting_iterator{row_group_indices.size()},

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.

Whitespace changes

});
});
});
std::for_each(cuda::counting_iterator<std::size_t>{0},

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.

Whitespace changes unfortunately.

@@ -31,7 +31,6 @@ using metadata_base = parquet::detail::metadata;
using io::detail::inline_column_buffer;
using parquet::detail::CompactProtocolReader;

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.

Note to reviewers: Please use the "Hide whitespace" feature to make this breezy

Image

@mhaseeb123
mhaseeb123 marked this pull request as ready for review July 21, 2026 23:37
@mhaseeb123
mhaseeb123 requested a review from a team as a code owner July 21, 2026 23:37
@mhaseeb123
mhaseeb123 requested a review from wence- July 21, 2026 23:37
@mhaseeb123 mhaseeb123 added feature request New feature or request 3 - Ready for Review Ready for review by team non-breaking Non-breaking change cuIO cuIO issue labels Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 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

Hybrid Parquet scans now validate page-index byte ranges, detect index availability for selected columns, fall back to unpruned masks when metadata is missing, require offset indexes for page-mask computation, and refine dictionary-page range discovery.

Changes

Parquet hybrid scan index handling

Layer / File(s) Summary
Column-chunk lookup contract
cpp/src/io/parquet/reader_impl_helpers.*, cpp/src/io/parquet/experimental/page_index_filter_utils.*, cpp/src/io/parquet/predicate_pushdown.cpp
Column-chunk lookup now supports cached optional offsets, while metadata and page-row offset consumers use the updated contract and skip individual chunks lacking offset indexes.
Page-index ranges and presence checks
cpp/src/io/parquet/experimental/hybrid_scan_helpers.*, cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
Page-index byte spans are derived from index metadata, setup validates exact buffer ranges, selected index presence is reported, and all-true masks are built centrally.
Offset-index scan pipeline
cpp/src/io/parquet/reader_impl_helpers.*, cpp/src/io/parquet/reader_impl_preprocess.cu, cpp/src/io/parquet/experimental/hybrid_scan_preprocess.cu
Offset-index capability is determined from projected columns and controls page-header counting and decoding in preprocessing.
Selective pruning and fallback masks
cpp/src/io/parquet/experimental/page_index_filter.cu, cpp/tests/io/experimental/*, python/pylibcudf/tests/io/test_experimental_hybrid_scan.py
Page-statistics pruning checks only participating columns and falls back to all-true or empty masks when required metadata is unavailable; tests cover missing indexes and offset-index-only materialization.
Dictionary-page range discovery
cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp
Dictionary-page discovery reuses cached offsets, requires encoding statistics, verifies dictionary encodings, and derives ranges from dictionary or offset-index metadata.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • rapidsai/cudf#22866: Directly relates to dictionary-page byte-range discovery and its multifile API.
  • rapidsai/cudf#23374: Relates to page-index availability and offset-index-driven page-header processing.

Suggested labels: improvement

Suggested reviewers: wence-, kingcrimsontianyu, tomaugspurger, qbacpey, vuule

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.14% 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 accurately summarizes the main change: relaxing parquet page index requirements in hybrid scan.
Description check ✅ Passed The description is clearly related to the changeset and matches the refactor and fallback behavior described in the diff.
✨ 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp (4)

277-283: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Validate every embedded page-index range before parsing.

Checking only the total buffer size does not prove that each column_index_offset/length and offset_index_offset/length lies within the buffer. metadata::setup_page_index() performs pointer arithmetic from those metadata values, so malformed intermediate offsets can cause out-of-bounds reads. Validate each range before calling it.

As per coding guidelines: prevent invalid memory access and validate inputs before use.

🤖 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 277 -
283, Before calling metadata::setup_page_index in the page-index loading flow,
validate every column_index_offset/length and offset_index_offset/length range
from file_metadata lies within the expected page-index buffer, including
overflow-safe bounds checks. Retain the existing non-empty and total-size
validation, and reject any invalid embedded range before setup_page_index
performs pointer arithmetic.

Source: Coding guidelines


78-96: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Search for the first and last index-bearing chunks, not fixed endpoints.

If the physical first column chunk has no index but a later chunk does, min_offset becomes zero and the entire file is treated as having no page-index buffer. The same problem occurs when the physical last chunk lacks an index. Iterate over row groups/columns to find the first and last chunk with either requested index portion.

🤖 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 78 -
96, The min_offset and max_offset calculations must search for index-bearing
chunks rather than assuming row_groups.front().columns.front() and
row_groups.back().columns.back() contain indexes. Update these lambdas to
iterate row groups and columns, selecting the first and last chunk with either a
column or offset index, while preserving the existing offset precedence and zero
fallback when no index-bearing chunk exists.

120-126: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not discard the missing-column fallback.

The result is assigned into an optional, but the updated helper always returns a value or throws. Consequently, has_colchunk is always true and missing column chunks abort the scan instead of producing the intended unfiltered fallback. Align this call site with the final lookup contract and preserve the fallback path.

🤖 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 120 -
126, Update the colchunk lookup around find_colchunk_iter_offset so missing
column chunks remain representable as an empty optional instead of aborting the
scan. Align the call site with the helper’s value-or-throw contract by handling
its failure before assigning the result, preserving the existing has_colchunk,
column-index, offset-index, and unfiltered fallback behavior.

595-599: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Align dictionary-page lookup with the finalized API contract.

This call still stores the result in an optional and unconditionally calls .value(). If lookup failures are meant to support fallback, check for nullopt; otherwise remove the optional cache semantics consistently with the mandatory-return API.

🤖 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 595 -
599, Update the dictionary-page lookup around find_colchunk_iter_offset to match
its finalized return contract: if lookup failure supports fallback, test the
returned optional before accessing it and implement the fallback; otherwise
remove optional handling consistently and use the mandatory offset directly.
Ensure col_chunk access never unconditionally calls value() on a potentially
empty result.
cpp/src/io/parquet/reader_impl_helpers.cpp (1)

60-76: 🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Resolve the find_colchunk_iter_offset API contract before merging.

The implementation now returns mandatory size_type and throws when absent, but callers still model lookup as optional; get_column_metadata additionally calls .value() on the non-optional result. This breaks compilation and removes the fallback behavior required by the PR.

  • cpp/src/io/parquet/reader_impl_helpers.cpp#L60-L76: either restore optional failure results or update all callers to the mandatory-return contract.
  • cpp/src/io/parquet/reader_impl_helpers.hpp#L122-L125: keep the declaration aligned with the chosen semantics.
  • cpp/src/io/parquet/reader_impl_helpers.cpp#L1237-L1239: remove .value() if the return remains size_type.
  • cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp#L120-L126: preserve missing-column fallback handling.
  • cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp#L595-L599: handle lookup failure before dereferencing the optional cache.
rg -n -C3 'find_colchunk_iter_offset' cpp/src/io/parquet
🤖 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/reader_impl_helpers.cpp` around lines 60 - 76, Resolve the
find_colchunk_iter_offset contract consistently across all affected sites:
choose either optional failure results with existing fallback behavior or
mandatory size_type results with exception handling, then align its declaration
in cpp/src/io/parquet/reader_impl_helpers.hpp:122-125. For the mandatory
contract, remove .value() in reader_impl_helpers.cpp:1237-1239; in
hybrid_scan_helpers.cpp:120-126 preserve missing-column fallback handling and in
hybrid_scan_helpers.cpp:595-599 handle lookup failure before dereferencing the
optional cache.
🤖 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/reader_impl_helpers.cpp`:
- Around line 64-67: Update the cached offset validation before indexing
row_group.columns: in the condition guarding the cached return, require
cached_offset.value() >= 0 before checking it against row_group.columns.size().
Preserve the existing schema_idx match and return behavior for valid offsets.

---

Outside diff comments:
In `@cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp`:
- Around line 277-283: Before calling metadata::setup_page_index in the
page-index loading flow, validate every column_index_offset/length and
offset_index_offset/length range from file_metadata lies within the expected
page-index buffer, including overflow-safe bounds checks. Retain the existing
non-empty and total-size validation, and reject any invalid embedded range
before setup_page_index performs pointer arithmetic.
- Around line 78-96: The min_offset and max_offset calculations must search for
index-bearing chunks rather than assuming row_groups.front().columns.front() and
row_groups.back().columns.back() contain indexes. Update these lambdas to
iterate row groups and columns, selecting the first and last chunk with either a
column or offset index, while preserving the existing offset precedence and zero
fallback when no index-bearing chunk exists.
- Around line 120-126: Update the colchunk lookup around
find_colchunk_iter_offset so missing column chunks remain representable as an
empty optional instead of aborting the scan. Align the call site with the
helper’s value-or-throw contract by handling its failure before assigning the
result, preserving the existing has_colchunk, column-index, offset-index, and
unfiltered fallback behavior.
- Around line 595-599: Update the dictionary-page lookup around
find_colchunk_iter_offset to match its finalized return contract: if lookup
failure supports fallback, test the returned optional before accessing it and
implement the fallback; otherwise remove optional handling consistently and use
the mandatory offset directly. Ensure col_chunk access never unconditionally
calls value() on a potentially empty result.

In `@cpp/src/io/parquet/reader_impl_helpers.cpp`:
- Around line 60-76: Resolve the find_colchunk_iter_offset contract consistently
across all affected sites: choose either optional failure results with existing
fallback behavior or mandatory size_type results with exception handling, then
align its declaration in cpp/src/io/parquet/reader_impl_helpers.hpp:122-125. For
the mandatory contract, remove .value() in reader_impl_helpers.cpp:1237-1239; in
hybrid_scan_helpers.cpp:120-126 preserve missing-column fallback handling and in
hybrid_scan_helpers.cpp:595-599 handle lookup failure before dereferencing the
optional cache.
🪄 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: 2c6cb12a-f500-489b-a597-501f0cec0da6

📥 Commits

Reviewing files that changed from the base of the PR and between 624263f and 1216663.

📒 Files selected for processing (3)
  • cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp
  • cpp/src/io/parquet/reader_impl_helpers.cpp
  • cpp/src/io/parquet/reader_impl_helpers.hpp

Comment thread cpp/src/io/parquet/reader_impl_helpers.cpp Outdated

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

♻️ Duplicate comments (1)
cpp/src/io/parquet/reader_impl_helpers.cpp (1)

64-68: 🩺 Stability & Availability | 🔴 Critical

Reject negative cached offsets before indexing.

size_type is signed, so a cached value of -1 passes std::cmp_less(..., row_group.columns.size()) and is then used as a vector index, causing invalid memory access. Require cached_offset.value() >= 0 before the bounds check.

Proposed fix
-  if (cached_offset.has_value() and
+  if (cached_offset.has_value() and cached_offset.value() >= 0 and
       std::cmp_less(cached_offset.value(), row_group.columns.size()) and
🤖 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/reader_impl_helpers.cpp` around lines 64 - 68, Update the
cached_offset validation before indexing row_group.columns to require
cached_offset.value() >= 0 in addition to the existing bounds and schema_idx
checks. Preserve returning the cached offset only for non-negative, in-range
values matching schema_idx.
🤖 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.

Duplicate comments:
In `@cpp/src/io/parquet/reader_impl_helpers.cpp`:
- Around line 64-68: Update the cached_offset validation before indexing
row_group.columns to require cached_offset.value() >= 0 in addition to the
existing bounds and schema_idx checks. Preserve returning the cached offset only
for non-negative, in-range values matching schema_idx.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ebab86bc-dcf1-4410-b764-865e13d8f908

📥 Commits

Reviewing files that changed from the base of the PR and between e105a06 and a142785.

📒 Files selected for processing (1)
  • cpp/src/io/parquet/reader_impl_helpers.cpp

@mhaseeb123
mhaseeb123 requested a review from a team as a code owner July 22, 2026 19:07
@mhaseeb123
mhaseeb123 requested a review from TomAugspurger July 22, 2026 19:07
@github-actions github-actions Bot added the Python Affects Python cuDF API. label Jul 22, 2026
@vuule
vuule self-requested a review July 27, 2026 20:17

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

few nits

Comment thread cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp Outdated
Comment thread cpp/src/io/parquet/experimental/page_index_filter.cu Outdated
Comment thread cpp/src/io/parquet/reader_impl_helpers.cpp Outdated
@mhaseeb123
mhaseeb123 requested a review from vuule July 28, 2026 01:46
if (not col_chunk.offset_index.has_value()) { return; }
// Skip this column chunk if it does not have an offset index. This is because the decode
// paths can use column-index-derived information only together with offset index data.
if (not col_chunk.offset_index.has_value()) { continue; }

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.

Previously if a column didn't have offset index, did we stop page pruning for the whole file? And now with this change only the current column?

@mhaseeb123 mhaseeb123 Jul 29, 2026

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.

Previously if a column didn't have offset index, did we stop page pruning for the whole file?

Yes and we still do if it's missing for any of the columns we care about, just not all columns. That said, all parquet writers in practice write the indexes either for all or no column - but spec allows partial - so this is just defensive. In such (so far non-existent) cases, having partial indexes is still helpful in reducing some preprocessing work.

@kingcrimsontianyu kingcrimsontianyu 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! Just had a high level question.

@mhaseeb123
mhaseeb123 requested a review from a team as a code owner July 29, 2026 22:21
@mhaseeb123
mhaseeb123 requested a review from paul-aiyedun July 29, 2026 22:21
@github-actions github-actions Bot added the Java Affects Java cuDF API. label Jul 29, 2026
assertThrows(CudfException.class, () ->
reader.materializeFilterColumns(survived, filterCols, true));
try (HybridScanReader.FilterMaterializationResult filter_columns =
reader.materializeFilterColumns(survived, filterCols, true)) {

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.

How would a user who sets usePageLevelPruning = true know that page leveling pruning did not occur?

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.

libcudf logs a warning in the fallback case.

@mhaseeb123 mhaseeb123 Jul 30, 2026

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.

Actually, I think I agree that the build_row_mask_with_page_index_stats() should throw as it would make our lives a lot easier. Pushing a commit with some reverts now

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 in 9bdc8af

// Compute the page index (column index and/or offset index) byte range
[[nodiscard]] byte_range_info page_index_byte_range(FileMetaData const& file_metadata)
{
auto const& row_groups = file_metadata.row_groups;

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.

Out of curiosity, are there Parquet writers that only write the offset or column index and not both?

@paul-aiyedun paul-aiyedun 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.

Java updates look good to me. @pmattione-nvidia FYI.

@mhaseeb123 mhaseeb123 added 5 - Ready to Merge Testing and reviews complete, ready to merge and removed 4 - Needs Review Waiting for reviewer to review or respond labels Jul 30, 2026
@mhaseeb123

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit ffdcafc into NVIDIA:main Jul 30, 2026
139 of 140 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jul 30, 2026
@mhaseeb123
mhaseeb123 deleted the fea/hybrid-scan-relax-page-idx-required branch July 30, 2026 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to Merge Testing and reviews complete, ready to merge cuIO cuIO issue feature request New feature or request Java Affects Java cuDF API. libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change pylibcudf Issues specific to the pylibcudf package Python Affects Python cuDF API.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

8 participants