Skip to content

Prepend source and row index columns in Hybrid scan reader - #22878

Merged
rapids-bot[bot] merged 21 commits into
NVIDIA:mainfrom
mhaseeb123:fea/prepend-src-idx-col-hybrid-scan
Jul 14, 2026
Merged

Prepend source and row index columns in Hybrid scan reader#22878
rapids-bot[bot] merged 21 commits into
NVIDIA:mainfrom
mhaseeb123:fea/prepend-src-idx-col-hybrid-scan

Conversation

@mhaseeb123

@mhaseeb123 mhaseeb123 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Closes #22849.
Follow up of #22879 + #23077

This PR enables prepending source and row index columns in hybrid scan. For two-step materialization, the extra index columns are prepended to filter columns only. Single-step materialization is identical to the main parquet reader

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

@github-actions github-actions Bot added the libcudf Affects libcudf (C++/CUDA) code. label Jun 12, 2026
@mhaseeb123 mhaseeb123 changed the title Prepend source index column in hybrid scan Prepend source index column in Hybrid scan reader Jun 12, 2026
@mhaseeb123 mhaseeb123 added feature request New feature or request 2 - In Progress Currently a work in progress cuIO cuIO issue Spark Functionality that helps Spark RAPIDS Velox Functionality that helps Velox-cudf labels Jun 12, 2026
@mhaseeb123 mhaseeb123 added the non-breaking Non-breaking change label Jun 12, 2026
mhaseeb123 added a commit to mhaseeb123/cudf that referenced this pull request Jun 15, 2026
mhaseeb123 added a commit to mhaseeb123/cudf that referenced this pull request Jun 24, 2026
_strings_to_categorical = false;
_options.use_jit_filter = false;
_options.case_sensitive_names = true;
_options.prepend_source_index_column = false;

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.

The only new field, rest is all whitespace

_options.decimal_width = options.get_decimal_width();
_options.use_jit_filter = options.is_enabled_use_jit_filter();
_options.case_sensitive_names = options.is_enabled_case_sensitive_names();
_options.prepend_source_index_column = options.is_enabled_prepend_source_index_column();

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.

The only new field, rest is all whitespace

out_metadata.num_rows_per_source =
std::vector<std::size_t>(_file_itm_data.num_rows_per_source.size(), 0);
}
// Empty dataframe case: Simply initialize to a list of zeros

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.

Just remove the if condition (like regular reader) - rest is all whitespace

out_metadata.num_rows_per_source = std::move(_file_itm_data.num_rows_per_source);
}
// Compute the output number of rows per source
if (mode == read_mode::CHUNKED_READ) {

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.

Just remove the if condition (like regular reader) - rest is all whitespace

Comment thread cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp Outdated
apply_decimal_width_cast(out_columns);

// Prepend the source index column to filter columns or all columns
if (_options.prepend_source_index_column and

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.

Only add source index column to filter columns (arbitrary design choice - could be payload as well)

Comment on lines +1094 to +1097
auto const num_prepended_cols = static_cast<size_type>(_options.prepend_source_index_column);
auto const final_filter =
parquet::detail::offset_column_references(_expr_conv.get_converted_expr(), num_prepended_cols);
auto const final_filter_expr = final_filter.get_converted_expr();

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.

Changes borrowed from the main reader (could be put in a common util 🤔 )

_stream,
cudf::get_current_device_resource_ref());
auto final_row_mask = cudf::detail::compute_column(*read_table,
final_filter_expr.value().get(),

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.

Filter with final_filter_expr instead of _expr_conv

*
* @return True if AST filter is not present
*/
[[nodiscard]] bool include_output_num_rows_per_source() const

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 longer needed

@mhaseeb123 mhaseeb123 added 3 - Ready for Review Ready for review by team and removed 2 - In Progress Currently a work in progress labels Jul 1, 2026
@mhaseeb123
mhaseeb123 marked this pull request as ready for review July 1, 2026 19:38
@mhaseeb123
mhaseeb123 requested a review from a team as a code owner July 1, 2026 19:38
@coderabbitai

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

This PR adds a prepend_source_index_column option to the hybrid scan Parquet reader, propagating it through option reset/initialization, adjusting num_rows_per_source computation in read_chunk_internal, and offsetting filter column references in finalize_output via a new compute_offset_filter() helper in reader_impl.hpp, which replaces the removed include_output_num_rows_per_source(). A new multifile test validates the feature. Three test files also receive unrelated SPDX copyright header edits.

Changes

Prepend source index column support

Layer / File(s) Summary
Option initialization
cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
reset_internal_state() defaults _options.prepend_source_index_column to false; initialize_column_selection_options() sets it from parquet_reader_options::is_enabled_prepend_source_index_column().
Output row counts and helper refactor
cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp, cpp/src/io/parquet/reader_impl.hpp
read_chunk_internal unconditionally populates out_metadata.num_rows_per_source on both early-return and normal paths; include_output_num_rows_per_source() is removed and replaced with a new compute_offset_filter() method that builds offset column references.
Finalize filter output
cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
finalize_output prepends the source-index column for FILTER_COLUMNS mode, computes an offset-aware final_filter_expr via compute_offset_filter(), clears num_rows_per_source after filtering, and evaluates the row mask using the offset expression.
Multifile prepend test
cpp/tests/io/experimental/hybrid_scan_multifile_test.cpp
New PrependSourceIndexColumn test writes a multi-source parquet table, builds a col0 % 2 == 0 filter AST, and compares expected reader output (with prepend enabled) against a manually driven hybrid scan reader result.

SPDX copyright header updates

Layer / File(s) Summary
Copyright header edits
cpp/tests/strings/contains_tests.cpp, cpp/tests/strings/findall_tests.cpp, cpp/tests/strings/replace_regex_tests.cpp
SPDX-FileCopyrightText lines updated between "NVIDIA CORPORATION" and "NVIDIA CORPORATION & AFFILIATES" variants.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • rapidsai/cudf#22716: Both PRs modify the hybrid scan reader's finalize_output offset/mask-related logic in hybrid_scan_impl.cpp.
  • rapidsai/cudf#22793: Adds multifile materialization entrypoints that forward into the same hybrid_scan_reader_impl filter/prepend path modified here.
  • rapidsai/cudf#22879: Implements the same prepend_source_index_column option with matching filter column-reference offsetting.

Suggested labels: improvement

Suggested reviewers: qbacpey, vuule, PointKernel, bdice

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 matches the main change: hybrid scan now prepends source-related columns during reader output.
Description check ✅ Passed The description is directly related to the changes and accurately describes the hybrid scan feature update.
✨ 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

🧹 Nitpick comments (1)
cpp/tests/io/experimental/hybrid_scan_test.cpp (1)

961-963: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

num_rows_per_source metadata from materialize_* results is never checked.

Cohort 2 of this PR stack made out_metadata.num_rows_per_source computation unconditional in hybrid_scan_impl.cpp (previously gated on include_output_num_rows_per_source()) and removed the now-unused helper in reader_impl.hpp. This test discards the metadata (.tbl only) from every materialize_filter_columns_chunk, materialize_payload_columns_chunk, and materialize_all_columns_chunk call, so the unconditional computation path introduced by that change is not exercised or validated here.

Also applies to: 981-983, 1009-1011

🤖 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/tests/io/experimental/hybrid_scan_test.cpp` around lines 961 - 963, The
hybrid scan test is ignoring the `num_rows_per_source` metadata returned by
`materialize_*` APIs, so the new unconditional metadata computation in
`hybrid_scan_impl.cpp` is not being exercised. Update the test cases around
`reader->materialize_filter_columns_chunk`,
`reader->materialize_payload_columns_chunk`, and
`reader->materialize_all_columns_chunk` to capture and assert the returned
`out_metadata.num_rows_per_source` (not just `.tbl`), using the existing
`reader`/`row_mask_view` setup to validate the metadata path.
🤖 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/tests/io/experimental/hybrid_scan_test.cpp`:
- Around line 886-889: The hybrid scan test currently uses only one source, so
it never validates source-index assignment or ordering. Update the test around
create_parquet_with_stats and finalize_output to use a multi-source case with
num_concat > 1, then assert the prepended source-index column contains the
expected values for each source rather than only checking column
placement/count. Keep the existing single-source case if needed, but add
coverage in hybrid_scan_test.cpp that exercises multiple row-group sources and
verifies the source-index differentiation logic directly.

---

Nitpick comments:
In `@cpp/tests/io/experimental/hybrid_scan_test.cpp`:
- Around line 961-963: The hybrid scan test is ignoring the
`num_rows_per_source` metadata returned by `materialize_*` APIs, so the new
unconditional metadata computation in `hybrid_scan_impl.cpp` is not being
exercised. Update the test cases around
`reader->materialize_filter_columns_chunk`,
`reader->materialize_payload_columns_chunk`, and
`reader->materialize_all_columns_chunk` to capture and assert the returned
`out_metadata.num_rows_per_source` (not just `.tbl`), using the existing
`reader`/`row_mask_view` setup to validate the metadata path.
🪄 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: c845e231-2ee9-432b-af4a-a383714596f4

📥 Commits

Reviewing files that changed from the base of the PR and between e7cae5e and c2148c1.

📒 Files selected for processing (3)
  • cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
  • cpp/src/io/parquet/reader_impl.hpp
  • cpp/tests/io/experimental/hybrid_scan_test.cpp
💤 Files with no reviewable changes (1)
  • cpp/src/io/parquet/reader_impl.hpp

Comment thread cpp/tests/io/experimental/hybrid_scan_test.cpp Outdated
@mhaseeb123
mhaseeb123 marked this pull request as draft July 9, 2026 19:26
@copy-pr-bot

copy-pr-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@mhaseeb123 mhaseeb123 removed the Spark Functionality that helps Spark RAPIDS label Jul 13, 2026
@mhaseeb123
mhaseeb123 marked this pull request as ready for review July 13, 2026 19:37
@mhaseeb123
mhaseeb123 requested a review from vuule July 13, 2026 19:43
@GregoryKimball GregoryKimball moved this to Burndown in libcudf Jul 13, 2026

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

Seems fine to me, thanks!

@mhaseeb123

Copy link
Copy Markdown
Contributor Author

/ok to test 0967b9b

@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 13, 2026
@mhaseeb123

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 00f5d0c into NVIDIA:main Jul 14, 2026
138 checks passed
@mhaseeb123
mhaseeb123 deleted the fea/prepend-src-idx-col-hybrid-scan branch July 14, 2026 01:38
@GregoryKimball GregoryKimball removed this from libcudf Jul 14, 2026
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 libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change Velox Functionality that helps Velox-cudf

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[STORY] Parquet reader features for Apache Iceberg

4 participants