Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bfce489
[WIP] Add dictionary page filtering and byte range retrieval to hybri…
qbacpey Jun 12, 2026
187036c
Refactor hybrid scan multifile filters test to simplify row group fil…
qbacpey Jun 12, 2026
0115910
Refactor setup_multifile_page_index to simplify buffer management in …
qbacpey Jun 12, 2026
cddcce3
Enhance create_parquet_with_stats to support customizable column name…
qbacpey Jun 12, 2026
f632241
Refactor column chunk offset retrieval in hybrid scan helpers to use …
qbacpey Jun 13, 2026
451367d
Merge branch 'main' of github.com:rapidsai/cudf into hybrid/dict-mult…
qbacpey Jun 15, 2026
a76277e
Refactor dictionary page byte range functions to return pairs of vectors
qbacpey Jun 15, 2026
291125f
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jun 22, 2026
d2ba16d
Refactor comments and add validation checks in hybrid scan helpers an…
qbacpey Jun 22, 2026
94251cd
Update copyright notices to include "AFFILIATES" in multiple files
qbacpey Jun 22, 2026
a163660
Remove out-of-bounds check from `create_parquet_with_stats` and updat…
qbacpey Jun 23, 2026
865ab76
Merge branch 'main' into hybrid/dict-multi-t2
mhaseeb123 Jun 24, 2026
506ee8c
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jun 24, 2026
4a34b6c
Update copyright notice and refactor hybrid scan multifile tests
qbacpey Jun 24, 2026
b7b1791
Merge branch 'main' into hybrid/dict-multi-t2
qbacpey Jun 24, 2026
91f421e
Merge branch 'main' into hybrid/dict-multi-t2
mhaseeb123 Jun 26, 2026
e5d97d5
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jun 29, 2026
eb2dbd4
Refactor column chunk offset retrieval in parquet reader
qbacpey Jun 29, 2026
2cd38dc
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jun 30, 2026
e6009c1
Refactor hybrid scan reader implementation
qbacpey Jun 30, 2026
2f7d23a
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jun 30, 2026
da507b5
Add hybrid scan read-amplification benchmark to nvbench
qbacpey Jul 2, 2026
cca34ec
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jul 2, 2026
8b01cc4
updates the documentation and error handling for schema indices in th…
qbacpey Jul 2, 2026
ae55fc5
Refactor dictionary page byte range handling in hybrid scan implement…
qbacpey Jul 2, 2026
d1e2b89
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jul 6, 2026
2fc348d
Merge remote-tracking branch 'upstream/main' into hybrid/dict-multi-t2
qbacpey Jul 7, 2026
a07b7b5
Refactor and clarify Parquet reader helper functions
qbacpey Jul 7, 2026
7aa70cf
Refactor filter_row_groups_with_dictionaries to use parquet_reader_op…
qbacpey Jul 7, 2026
f13cac8
Merge branch 'main' into hybrid/dict-multi-t2
mhaseeb123 Jul 13, 2026
eea8f1f
Mark unused variables in hybrid_scan_common.cpp to avoid compiler war…
qbacpey Jul 15, 2026
7d7271e
Merge branch 'main' into hybrid/dict-multi-t2
qbacpey Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions cpp/include/cudf/io/experimental/hybrid_scan_multifile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,35 @@ class hybrid_scan_multifile {
*/
[[nodiscard]] bool has_next_table_chunk() const;

/**
* @brief Get byte ranges of column chunk dictionary pages for row group pruning
*
* @param row_group_indices Span of vectors of input row group indices, one per source
* @param options Parquet reader options
* @return Pair of flattened byte ranges to column chunk dictionary pages subject to the filter
* predicate and their corresponding source indices
*/
[[nodiscard]] std::pair<std::vector<byte_range_info>, std::vector<size_type>>
dictionary_pages_byte_ranges(cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options) const;

/**
* @brief Filter the row groups using column chunk dictionary pages
*
* @param dictionary_page_data Device spans of dictionary page data of column chunks with an
* (in)equality predicate, ordered to match the dictionary page byte
* ranges returned by `dictionary_pages_byte_ranges`
* @param row_group_indices Span of vectors of input row group indices, one per source
* @param options Parquet reader options
* @param stream CUDA stream used for device memory operations and kernel launches
* @return Vector of vectors of filtered row group indices, one per source
*/
[[nodiscard]] std::vector<std::vector<size_type>> filter_row_groups_with_dictionary_pages(
cudf::host_span<cudf::device_span<uint8_t const> const> dictionary_page_data,
cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options,
rmm::cuda_stream_view stream) const;

private:
std::unique_ptr<detail::hybrid_scan_reader_impl> _impl;
};
Expand Down
54 changes: 30 additions & 24 deletions cpp/src/io/parquet/experimental/hybrid_scan_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ using metadata_base = parquet::detail::metadata;
using io::detail::inline_column_buffer;
using parquet::detail::CompactProtocolReader;
using parquet::detail::equality_literals_collector;
using parquet::detail::find_colchunk_iter_offset;
using parquet::detail::input_column_info;
using parquet::detail::row_group_info;
using text::byte_range_info;
Expand Down Expand Up @@ -429,7 +430,8 @@ std::vector<byte_range_info> aggregate_reader_metadata::get_bloom_filter_bytes(
return bloom_filter_bytes;
}

std::vector<byte_range_info> aggregate_reader_metadata::get_dictionary_page_bytes(
std::pair<std::vector<byte_range_info>, std::vector<cudf::size_type>>
aggregate_reader_metadata::dictionary_pages_byte_ranges(
std::span<std::vector<cudf::size_type> const> row_group_indices,
std::span<data_type const> output_dtypes,
std::span<cudf::size_type const> output_column_schemas,
Expand All @@ -447,7 +449,7 @@ std::vector<byte_range_info> aggregate_reader_metadata::get_dictionary_page_byte
std::back_inserter(dictionary_col_schemas),
[](auto& dict_literals) { return not dict_literals.empty(); });

// No (in)equality literals found, return empty vector
// No (in)equality literals found, return empty vectors
if (dictionary_col_schemas.empty()) { return {}; }

// Compute total number of input row groups
Expand All @@ -463,38 +465,41 @@ std::vector<byte_range_info> aggregate_reader_metadata::get_dictionary_page_byte
// Flag to check if we have at least one valid dictionary page
auto have_dictionary_pages = false;

// Association between each dictionary page byte range and its source
std::vector<cudf::size_type> dictionary_page_source_map;
dictionary_page_source_map.reserve(num_chunks);

// Cache each dictionary column's chunk offset across sources and row groups
std::vector<std::optional<size_type>> colchunk_offsets(dictionary_col_schemas.size());

// 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());
Comment on lines 475 to +485

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.

// For all dictionary column chunks
std::for_each(
dictionary_col_schemas.begin(),
dictionary_col_schemas.end(),
[&](auto const& schema_idx) {
// Get the column chunk iterator
if (not colchunk_iter_offset.has_value() or
row_group.columns[colchunk_iter_offset.value()].schema_idx != schema_idx) {
auto const& colchunk_iter = std::find_if(
row_group.columns.begin(), row_group.columns.end(), [schema_idx](auto const& col) {
return col.schema_idx == schema_idx;
});
CUDF_EXPECTS(colchunk_iter != row_group.columns.end(),
"Column chunk with schema index " + std::to_string(schema_idx) +
" not found in row group",
std::invalid_argument);
colchunk_iter_offset = std::distance(row_group.columns.begin(), colchunk_iter);
cuda::counting_iterator<std::size_t>{0},
cuda::counting_iterator{dictionary_col_schemas.size()},
[&](auto const col) {
// Map the schema index to this source
auto const mapped_schema_idx =
map_schema_index(dictionary_col_schemas[col], static_cast<int>(src_index));
auto& colchunk_offset = colchunk_offsets[col];
auto const cached_offset = colchunk_offset.value_or(-1);
if (cached_offset < 0 or cached_offset >= num_col_chunks or
row_group.columns[cached_offset].schema_idx != mapped_schema_idx) {
colchunk_offset = find_colchunk_iter_offset(row_group, mapped_schema_idx);
Comment thread
qbacpey marked this conversation as resolved.
}
auto const colchunk_iter = row_group.columns.begin() + colchunk_iter_offset.value();
auto const& col_chunk = *colchunk_iter;
auto const& col_meta = col_chunk.meta_data;

auto const& col_chunk = row_group.columns[colchunk_offset.value()];
auto const& col_meta = col_chunk.meta_data;

// Make sure that we have page index and the column chunk doesn't have any
// non-dictionary encoded pages
Expand Down Expand Up @@ -550,13 +555,14 @@ std::vector<byte_range_info> aggregate_reader_metadata::get_dictionary_page_byte
}

dictionary_page_bytes.emplace_back(dictionary_offset, dictionary_size);
dictionary_page_source_map.emplace_back(static_cast<size_type>(src_index));
});
});
});

if (not have_dictionary_pages) { return {}; }

return dictionary_page_bytes;
return {std::move(dictionary_page_bytes), std::move(dictionary_page_source_map)};
}

std::vector<std::vector<cudf::size_type>>
Expand Down
14 changes: 8 additions & 6 deletions cpp/src/io/parquet/experimental/hybrid_scan_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,15 @@ class aggregate_reader_metadata : public aggregate_reader_metadata_base {
* @param output_column_schemas schema indices of output columns
* @param filter AST expression to filter row groups based on dictionary pages
*
* @return Byte ranges of dictionary pages, one input column chunk with (in)equality predicate
* @return A pair of vectors containing dictionary page byte ranges and corresponding source
* indices
*/
[[nodiscard]] std::vector<cudf::io::text::byte_range_info> get_dictionary_page_bytes(
std::span<std::vector<size_type> const> row_group_indices,
std::span<data_type const> output_dtypes,
std::span<cudf::size_type const> output_column_schemas,
std::reference_wrapper<ast::expression const> filter);
[[nodiscard]] std::pair<std::vector<cudf::io::text::byte_range_info>,
std::vector<cudf::size_type>>
dictionary_pages_byte_ranges(std::span<std::vector<size_type> const> row_group_indices,
std::span<data_type const> output_dtypes,
std::span<cudf::size_type const> output_column_schemas,
std::reference_wrapper<ast::expression const> filter);

/**
* @brief Filter the row groups using dictionaries based on predicate filter
Expand Down
24 changes: 20 additions & 4 deletions cpp/src/io/parquet/experimental/hybrid_scan_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,30 @@ hybrid_scan_reader_impl::secondary_filters_byte_ranges(
_output_column_schemas,
expr_conv.get_converted_expr().value());
auto const dictionary_page_bytes =
_extended_metadata->get_dictionary_page_bytes(row_group_indices,
output_dtypes,
_output_column_schemas,
expr_conv.get_converted_expr().value());
_extended_metadata
->dictionary_pages_byte_ranges(row_group_indices,
output_dtypes,
_output_column_schemas,
expr_conv.get_converted_expr().value())
.first;

return {bloom_filter_bytes, dictionary_page_bytes};
}

std::pair<std::vector<byte_range_info>, std::vector<cudf::size_type>>
hybrid_scan_reader_impl::dictionary_pages_byte_ranges(
cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options)
{
CUDF_EXPECTS(not row_group_indices.empty(), "Empty input row group indices encountered");
auto [expr_conv, output_dtypes] = prepare_filter_and_output_types(options);

return _extended_metadata->dictionary_pages_byte_ranges(row_group_indices,
output_dtypes,
_output_column_schemas,
expr_conv.get_converted_expr().value());
}

std::vector<std::vector<size_type>>
hybrid_scan_reader_impl::filter_row_groups_with_dictionary_pages(
std::span<cudf::device_span<uint8_t const> const> dictionary_page_data,
Expand Down
9 changes: 8 additions & 1 deletion cpp/src/io/parquet/experimental/hybrid_scan_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ class hybrid_scan_reader_impl : public parquet::detail::reader_impl {
secondary_filters_byte_ranges(std::span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options);

/**
* @copydoc cudf::io::parquet::experimental::hybrid_scan_multifile::dictionary_pages_byte_ranges
*/
[[nodiscard]] std::pair<std::vector<byte_range_info>, std::vector<cudf::size_type>>
dictionary_pages_byte_ranges(cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options);

/**
* @copydoc cudf::io::parquet::experimental::hybrid_scan::filter_row_groups_with_dictionary_pages
*/
Expand Down Expand Up @@ -283,7 +290,7 @@ class hybrid_scan_reader_impl : public parquet::detail::reader_impl {
* @throws std::invalid_argument if @p row_group_indices.size() is all empty or not equal to the
* number of input datasources
*
* @param row_group_indices Input row group indices, one per source
* @param row_group_indices Span of vectors of input row group indices, one per source
* @param total_row_groups Total number of row groups across all sources
* @param pass_read_limit Memory limit to read and decompress row
* group data
Expand Down
20 changes: 20 additions & 0 deletions cpp/src/io/parquet/experimental/hybrid_scan_multifile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,24 @@ std::vector<std::vector<std::vector<size_type>>> hybrid_scan_multifile::construc
return source_passes;
}

std::pair<std::vector<text::byte_range_info>, std::vector<size_type>>
hybrid_scan_multifile::dictionary_pages_byte_ranges(
cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options) const
{
CUDF_FUNC_RANGE();
return _impl->dictionary_pages_byte_ranges(row_group_indices, options);
}

std::vector<std::vector<size_type>> hybrid_scan_multifile::filter_row_groups_with_dictionary_pages(
cudf::host_span<cudf::device_span<uint8_t const> const> dictionary_page_data,
cudf::host_span<std::vector<size_type> const> row_group_indices,
parquet_reader_options const& options,
rmm::cuda_stream_view stream) const
{
CUDF_FUNC_RANGE();
return _impl->filter_row_groups_with_dictionary_pages(
dictionary_page_data, row_group_indices, options, stream);
}

} // namespace cudf::io::parquet::experimental
12 changes: 12 additions & 0 deletions cpp/src/io/parquet/experimental/page_index_filter.cu
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,12 @@ std::unique_ptr<cudf::column> aggregate_reader_metadata::build_row_mask_with_pag
// Return if empty row group indices
if (row_group_indices.empty()) { return cudf::make_empty_column(cudf::type_id::BOOL8); }

// TODO(#22900): remove this guard once this path maps schema indices per source. It currently
// reuses one source's schema index for every source, so it is correct only when schemas match.
CUDF_EXPECTS(schema_idx_maps.empty(),
"Page index statistics filtering does not support mismatched Parquet schemas yet",
std::invalid_argument);

// Check if we have page index for all columns in all row groups
auto const has_page_index = compute_has_page_index(per_file_metadata, row_group_indices);

Expand Down Expand Up @@ -1007,6 +1013,12 @@ thrust::host_vector<bool> aggregate_reader_metadata::compute_data_page_mask(
return thrust::host_vector<bool>(0, stream);
}

// TODO(#22900): remove this guard once this path maps schema indices per source. It currently
// reuses one source's schema index for every source, so it is correct only when schemas match.
CUDF_EXPECTS(schema_idx_maps.empty(),
"Data page masking does not support mismatched Parquet schemas yet",
std::invalid_argument);

// Collect column schema indices from the input columns.
auto column_schema_indices = std::vector<size_type>(input_columns.size());
std::transform(
Expand Down
13 changes: 1 addition & 12 deletions cpp/src/io/parquet/experimental/page_index_filter_utils.cu
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,7 @@

namespace cudf::io::parquet::experimental::detail {

size_type find_colchunk_iter_offset(RowGroup const& row_group, size_type schema_idx)
{
auto const& colchunk_iter =
std::find_if(row_group.columns.begin(), row_group.columns.end(), [schema_idx](auto const& col) {
return col.schema_idx == schema_idx;
});
CUDF_EXPECTS(
colchunk_iter != row_group.columns.end(),
"Column chunk with schema index " + std::to_string(schema_idx) + " not found in row group",
std::invalid_argument);
return std::distance(row_group.columns.begin(), colchunk_iter);
}
using parquet::detail::find_colchunk_iter_offset;

bool compute_has_page_index(std::span<metadata_base const> file_metadatas,
std::span<std::vector<size_type> const> row_group_indices)
Expand Down
9 changes: 0 additions & 9 deletions cpp/src/io/parquet/experimental/page_index_filter_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@ namespace cudf::io::parquet::experimental::detail {

using metadata_base = parquet::detail::metadata;

/**
* @brief Find the offset of the column chunk with the given schema index in the row group
*
* @param row_group Row group
* @param schema_idx Schema index
* @return Offset of the column chunk iterator
*/
[[nodiscard]] size_type find_colchunk_iter_offset(RowGroup const& row_group, size_type schema_idx);

/**
* @brief Compute if the page index is present in all parquet data sources for all columns
*
Expand Down
13 changes: 1 addition & 12 deletions cpp/src/io/parquet/predicate_pushdown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <thrust/iterator/counting_iterator.h>

#include <algorithm>
#include <format>
#include <functional>
#include <numeric>
#include <optional>
Expand Down Expand Up @@ -155,17 +154,7 @@ bool aggregate_reader_metadata::any_row_group_stats_available(

if (cached_offset < 0 or cached_offset >= num_col_chunks or
first_row_group.columns[cached_offset].schema_idx != mapped_schema_idx) {
auto const it = std::find_if(
first_row_group.columns.begin(),
first_row_group.columns.end(),
[mapped_schema_idx](ColumnChunk const& c) { return c.schema_idx == mapped_schema_idx; });
CUDF_EXPECTS(
it != first_row_group.columns.end(),
std::format(
"Column chunk with schema index {} not found in source {}", mapped_schema_idx, src_idx),
std::invalid_argument);
colchunk_offset =
static_cast<size_type>(std::distance(first_row_group.columns.begin(), it));
colchunk_offset = find_colchunk_iter_offset(first_row_group, mapped_schema_idx);
}

if (colchunk_has_stats(first_row_group.columns[colchunk_offset.value()])) { return true; }
Expand Down
22 changes: 14 additions & 8 deletions cpp/src/io/parquet/reader_impl_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ std::size_t derive_pass_read_limit(std::size_t chunk_read_limit)
return pass_read_limit;
}

size_type find_colchunk_iter_offset(RowGroup const& row_group, size_type schema_idx)
{
auto const& colchunk_iter =
Comment thread
mhaseeb123 marked this conversation as resolved.
std::find_if(row_group.columns.begin(), row_group.columns.end(), [schema_idx](auto const& col) {
return col.schema_idx == schema_idx;
});
CUDF_EXPECTS(colchunk_iter != row_group.columns.end(),
std::format("Column chunk with schema index {} not found in row group", schema_idx),
std::invalid_argument);
return std::distance(row_group.columns.begin(), colchunk_iter);
}

namespace flatbuf = cudf::io::parquet::flatbuf;

namespace {
Expand Down Expand Up @@ -1215,14 +1227,8 @@ ColumnChunkMetaData const& aggregate_reader_metadata::get_column_metadata(size_t
// Map schema index to the provided source file index
schema_idx = map_schema_index(schema_idx, src_idx);

auto col =
std::find_if(per_file_metadata[src_idx].row_groups[row_group_index].columns.begin(),
per_file_metadata[src_idx].row_groups[row_group_index].columns.end(),
[schema_idx](ColumnChunk const& col) { return col.schema_idx == schema_idx; });
CUDF_EXPECTS(col != std::end(per_file_metadata[src_idx].row_groups[row_group_index].columns),
"Found no metadata for schema index",
std::range_error);
return col->meta_data;
auto const& row_group = per_file_metadata[src_idx].row_groups[row_group_index];
return row_group.columns[find_colchunk_iter_offset(row_group, schema_idx)].meta_data;
}

std::vector<std::unordered_map<std::string, int64_t>>
Expand Down
12 changes: 12 additions & 0 deletions cpp/src/io/parquet/reader_impl_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ struct row_group_info {
*/
[[nodiscard]] std::size_t derive_pass_read_limit(std::size_t chunk_read_limit);

/**
* @brief Find the offset of the column chunk with the given schema index in the specified row group
*
* @note For mismatched schemas, `schema_idx` must be pre-mapped to the row group's source using
* `map_schema_index`.
*
* @param row_group Row group
* @param schema_idx Schema index, already mapped to the row group's source
* @return Offset of the column chunk within the row group's columns
*/
[[nodiscard]] size_type find_colchunk_iter_offset(RowGroup const& row_group, size_type schema_idx);

/**
* @brief Class for parsing dataset metadata
*/
Expand Down
Loading
Loading