Class HybridScanReader
- All Implemented Interfaces:
AutoCloseable
This class is the Java binding for
cudf::io::parquet::experimental::hybrid_scan_reader. It is designed for highly
selective filter expressions over Parquet files and exposes the multi-step pipeline that
the C++ reader uses internally:
- Read the Parquet footer (and optional page index) to drive row-group / page-level pruning.
- Filter the row groups using statistics, bloom filters, and dictionary pages.
- Materialize the filter columns; the reader builds an initial row mask (all true or seeded from page index stats when page-level pruning is enabled) and then mutates it down to only the rows that survive the AST filter.
- Materialize the payload columns using the surviving row mask.
A two-step convenience flow is provided for selective filters
(materializeFilterColumns(int[], DeviceMemoryBuffer[], boolean) then
materializePayloadColumns(int[], DeviceMemoryBuffer[], ColumnVector, boolean)),
and a one-shot materializeAllColumns(int[], DeviceMemoryBuffer[]) is provided
for small files or broad filters.
Chunked / streaming materialization is also supported via the
setupChunkingFor* + materialize*Chunk family of methods, mirroring the C++
chunked reader pipeline.
The filter and payload materialization paths accept a boolean that toggles
page-level pruning: skips decode of pages the filter (or row mask) proves empty, in
exchange for a per-page stats scan and a carried row-mask column. Enable when the
workload prunes many pages; on the filter path this requires prior
setupPageIndex(HostMemoryBuffer).
The reader is created with no filter expression installed. Filter-related APIs
behave as though nothing has been filtered out unless a filter is first supplied via
setFilter(CompiledExpression).
The APIs in this file are experimental and subject to change.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classThe result of a combined row-mask-build + filter-column-materialization call. -
Constructor Summary
ConstructorsConstructorDescriptionHybridScanReader(HostMemoryBuffer footerBuffer, ParquetOptions opts) Create a hybrid scan reader from the bytes of a Parquet file footer. -
Method Summary
Modifier and TypeMethodDescriptionallColumnChunksByteRanges(int[] rowGroupIndices) int[]voidclose()int[][]constructRowGroupPasses(int[] rowGroupIndices, long passReadLimit) Partition the supplied row groups into passes whose total uncompressed size respects the given limit.filterColumnChunksByteRanges(int[] rowGroupIndices) int[]filterRowGroupsWithDictionaryPages(DeviceMemoryBuffer[] dictionaryPageData, int[] rowGroupIndices) Filter row groups using column-chunk dictionary pages loaded into device memory.int[]filterRowGroupsWithStats(int[] rowGroupIndices) Filter row groups using column-chunk statistics from the file footer.booleanmaterializeAllColumns(int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData) Materialize all (or selected) columns in a single pass.materializeFilterColumns(int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData, boolean usePageLevelPruning) Build the initial row mask, materialize the filter columns, and evaluate the compiled filter expression against them.materializePayloadColumns(int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData, ColumnVector rowMask, boolean usePageLevelPruning) Materialize only the payload columns, applying the supplied row mask to the output.payloadColumnChunksByteRanges(int[] rowGroupIndices) secondaryFiltersByteRanges(int[] rowGroupIndices) Get the byte ranges in the source file that hold the bloom filter and dictionary page data needed for the next round of row-group pruning.voidsetFilter(CompiledExpression filter) Install or replace the filter expression used by all subsequent filter-related APIs on this reader; passnullto clear.voidsetupChunkingForAllColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData) Set up chunking state for all-column materialization (single-pass mode).voidsetupChunkingForFilterColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, boolean usePageLevelPruning, DeviceMemoryBuffer[] columnChunkData) Build the initial row mask and set up chunking state for filter-column materialization.voidsetupChunkingForPayloadColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, ColumnVector rowMask, boolean usePageLevelPruning, DeviceMemoryBuffer[] columnChunkData) Set up chunking state for payload-column materialization.voidsetupPageIndex(HostMemoryBuffer pageIndexBuffer) Materialize theColumnIndex/OffsetIndexstructs (collectively, the page index) from the supplied bytes.Transfer ownership of the row mask produced by the chunked filter-column materialization to the caller.longtotalRowsInRowGroups(int[] rowGroupIndices)
-
Constructor Details
-
HybridScanReader
Create a hybrid scan reader from the bytes of a Parquet file footer.The
footerBuffercan be obtained by reading the last few bytes of a Parquet file. See thehybrid_scan_ioexample for a helper that does exactly that.The reader is created with no filter expression installed. Filter-related APIs (e.g.
filterRowGroupsWithStats(int[]),materializeFilterColumns(int[], DeviceMemoryBuffer[], boolean)) behave as though nothing has been filtered out unless a filter is first supplied viasetFilter(CompiledExpression).- Parameters:
footerBuffer- host-resident footer bytes (must remain valid until this constructor returns; the JNI reads the bytes synchronously)opts- Parquet reader options.ParquetOptions.DEFAULTby default.
-
-
Method Details
-
setFilter
Install or replace the filter expression used by all subsequent filter-related APIs on this reader; passnullto clear. Cheap to call, so callers can sweep the same file with several candidate predicates in a loop.The caller retains ownership of any previously installed
CompiledExpressionand must close it themselves — this method replaces this reader's strong reference only.- Parameters:
filter- the new filter expression, ornullto clear
-
pageIndexByteRange
- Returns:
- the byte range of the page index in the Parquet file.
-
setupPageIndex
Materialize theColumnIndex/OffsetIndexstructs (collectively, the page index) from the supplied bytes. Required before any filter or payload materialization call withusePageLevelPruning == true.- Parameters:
pageIndexBuffer- host-resident page index bytes
-
allRowGroups
public int[] allRowGroups()- Returns:
- all row group indices in the Parquet file.
-
totalRowsInRowGroups
public long totalRowsInRowGroups(int[] rowGroupIndices) - Returns:
- the total number of top-level rows in the supplied row groups.
-
filterRowGroupsWithStats
public int[] filterRowGroupsWithStats(int[] rowGroupIndices) Filter row groups using column-chunk statistics from the file footer. -
secondaryFiltersByteRanges
Get the byte ranges in the source file that hold the bloom filter and dictionary page data needed for the next round of row-group pruning. -
filterRowGroupsWithDictionaryPages
public int[] filterRowGroupsWithDictionaryPages(DeviceMemoryBuffer[] dictionaryPageData, int[] rowGroupIndices) Filter row groups using column-chunk dictionary pages loaded into device memory. -
filterColumnChunksByteRanges
- Returns:
- byte ranges for the column chunks of filter columns.
-
payloadColumnChunksByteRanges
- Returns:
- byte ranges for the column chunks of payload columns.
This result is order-dependent. If filter columns have already been processed on this reader (e.g. via
filterColumnChunksByteRanges(int[])ormaterializeFilterColumns(int[], DeviceMemoryBuffer[], boolean)), the filter columns are excluded and only the payload columns are returned. If filter columns have not yet been processed, nothing is excluded and the ranges cover the full set of columns that would be read i.e. the columns projected viaParquetOptions, or all columns in the file when no projection was set.
-
allColumnChunksByteRanges
- Returns:
- byte ranges for all column chunks (filter + payload) of the selected columns.
-
materializeFilterColumns
public HybridScanReader.FilterMaterializationResult materializeFilterColumns(int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData, boolean usePageLevelPruning) Build the initial row mask, materialize the filter columns, and evaluate the compiled filter expression against them. The row mask and the resulting filter table are returned together as aHybridScanReader.FilterMaterializationResult; close it via try-with-resources.Set
usePageLevelPruning = trueto skip decompression and decode of pages the filter proves empty. Requires priorsetupPageIndex(HostMemoryBuffer).Cost: a per-page stats scan of filter columns and a carried row-mask column. Payoff: pruned pages are skipped entirely, typically the dominant read cost. Enable when a meaningful fraction of pages can be pruned; otherwise leave
false.- Parameters:
rowGroupIndices- row groups to readcolumnChunkData- device buffers holding the filter column chunks, in the order returned byfilterColumnChunksByteRanges(int[])usePageLevelPruning- seed the row mask from page-index stats and enable the data page mask; requires priorsetupPageIndex(HostMemoryBuffer)- Returns:
- combined filter table and mutated row mask; caller must close this result
-
materializePayloadColumns
public Table materializePayloadColumns(int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData, ColumnVector rowMask, boolean usePageLevelPruning) Materialize only the payload columns, applying the supplied row mask to the output.Set
usePageLevelPruning = trueto skip decompression and decode of pages containing no rows survivingrowMask. Adds a small mask-build cost; wins whenrowMaskprunes a meaningful fraction of pages.- Parameters:
rowGroupIndices- row groups to readcolumnChunkData- device buffers holding the payload column chunks, in the order returned bypayloadColumnChunksByteRanges(int[])rowMask- row mask (read-only)usePageLevelPruning- enable the data page mask to skip decode of pages the row mask proves empty; requires priorsetupPageIndex(HostMemoryBuffer)- Returns:
- the materialized payload column table
-
materializeAllColumns
Materialize all (or selected) columns in a single pass. The filter expression is applied after reading, so this is most efficient for small files or when most rows survive the filter. For highly-selective filters, prefer the explicit two-step flow. -
setupChunkingForFilterColumns
public void setupChunkingForFilterColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, boolean usePageLevelPruning, DeviceMemoryBuffer[] columnChunkData) Build the initial row mask and set up chunking state for filter-column materialization. The row mask is owned internally by the reader for the duration of the chunked filter pipeline; subsequent calls tomaterializeFilterColumnsChunk()mutate it in place. After all chunks are drained, calltakeFilterRowMask()to transfer ownership of the row mask to the caller (typically to feed it intomaterializePayloadColumns(int[], DeviceMemoryBuffer[], ColumnVector, boolean)).Set
usePageLevelPruning = trueto skip decompression and decode of pages the filter proves empty. Requires priorsetupPageIndex(HostMemoryBuffer).Cost: a per-page stats scan of filter columns and a carried row-mask column. Payoff: pruned pages are skipped entirely, typically the dominant read cost. Enable when a meaningful fraction of pages can be pruned; otherwise leave
false.Calling this method again before
takeFilterRowMask()discards the previous chunked-filter row mask.Caller must keep
columnChunkDataopen untiltakeFilterRowMask()(or a re-setup); the native reader holds references to it across chunk calls.- Parameters:
chunkReadLimit- soft limit (in bytes) on each output chunk returned by the matchingmaterialize*Chunkcall, or 0 for no limitpassReadLimit- soft limit (in bytes) on the working memory used to decompress and decode a single subpass (a page-level slice of the selected row groups), or 0 for no limit. Hybrid scan hardcodes the number of row-group passes to 1, so this parameter only bounds the subpass (decode) working set; it does not repartition row groups. To split row groups across multiple passes up front, callconstructRowGroupPasses(int[], long)first and issue a separate chunked run per returned partition.rowGroupIndices- row groups to readusePageLevelPruning- seed the row mask from page-index stats and enable the data page mask; requires priorsetupPageIndex(HostMemoryBuffer)columnChunkData- device buffers holding the filter column chunks, in the order returned byfilterColumnChunksByteRanges(int[])
-
materializeFilterColumnsChunk
- Returns:
- the next filter-column chunk; throws if no chunk is available or if no chunked filter pipeline is active. The internal row mask owned by this reader is updated in place.
-
takeFilterRowMask
Transfer ownership of the row mask produced by the chunked filter-column materialization to the caller. Typically called after allmaterializeFilterColumnsChunk()calls complete and immediately beforematerializePayloadColumns(int[], DeviceMemoryBuffer[], ColumnVector, boolean).After this call the reader has no chunked-filter row mask; subsequent
materializeFilterColumnsChunk()calls will fail untilsetupChunkingForFilterColumns(long, long, int[], boolean, DeviceMemoryBuffer[])is invoked again. If never called, the row mask is freed when the reader is closed.- Returns:
- the owned row mask
ColumnVector; caller must close it.
-
setupChunkingForPayloadColumns
public void setupChunkingForPayloadColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, ColumnVector rowMask, boolean usePageLevelPruning, DeviceMemoryBuffer[] columnChunkData) Set up chunking state for payload-column materialization. Subsequent calls tomaterializePayloadColumnsChunk(ColumnVector)will yield successive chunks.Set
usePageLevelPruning = trueto skip decompression and decode of pages containing no rows survivingrowMask. Adds a small mask-build cost; wins whenrowMaskprunes a meaningful fraction of pages.Caller must keep
columnChunkDataopen untilhasNextTableChunk()returnsfalse; the native reader holds references to it across chunk calls.- Parameters:
chunkReadLimit- soft limit (in bytes) on each output chunk returned by the matchingmaterialize*Chunkcall, or 0 for no limitpassReadLimit- soft limit (in bytes) on the working memory used to decompress and decode a single subpass (a page-level slice of the selected row groups), or 0 for no limit. Hybrid scan hardcodes the number of row-group passes to 1, so this parameter only bounds the subpass (decode) working set; it does not repartition row groups. To split row groups across multiple passes up front, callconstructRowGroupPasses(int[], long)first and issue a separate chunked run per returned partition.rowGroupIndices- row groups to readrowMask- row mask (read-only)usePageLevelPruning- enable the data page mask to skip decode of pages the row mask proves empty; requires priorsetupPageIndex(HostMemoryBuffer)columnChunkData- device buffers holding the payload column chunks, in the order returned bypayloadColumnChunksByteRanges(int[])
-
materializePayloadColumnsChunk
- Returns:
- the next payload-column chunk; throws if no chunk is available.
-
setupChunkingForAllColumns
public void setupChunkingForAllColumns(long chunkReadLimit, long passReadLimit, int[] rowGroupIndices, DeviceMemoryBuffer[] columnChunkData) Set up chunking state for all-column materialization (single-pass mode). Subsequent calls tomaterializeAllColumnsChunk()will yield successive chunks.Caller must keep
columnChunkDataopen untilhasNextTableChunk()returnsfalse; the native reader holds references to it across chunk calls.- Parameters:
chunkReadLimit- soft limit (in bytes) on each output chunk returned by the matchingmaterialize*Chunkcall, or 0 for no limitpassReadLimit- soft limit (in bytes) on the working memory used to decompress and decode a single subpass (a page-level slice of the selected row groups), or 0 for no limit. Hybrid scan hardcodes the number of row-group passes to 1, so this parameter only bounds the subpass (decode) working set; it does not repartition row groups. To split row groups across multiple passes up front, callconstructRowGroupPasses(int[], long)first and issue a separate chunked run per returned partition.rowGroupIndices- row groups to readcolumnChunkData- device buffers holding all column chunks, in the order returned byallColumnChunksByteRanges(int[])
-
materializeAllColumnsChunk
- Returns:
- the next all-columns chunk; throws if no chunk is available.
-
hasNextTableChunk
public boolean hasNextTableChunk()- Returns:
truewhen a subsequentmaterialize*Chunkcall would return data.
-
constructRowGroupPasses
public int[][] constructRowGroupPasses(int[] rowGroupIndices, long passReadLimit) Partition the supplied row groups into passes whose total uncompressed size respects the given limit. The returned array contains one inner array per pass.- Parameters:
rowGroupIndices- row groups to partitionpassReadLimit- limit on the memory used by a single pass, or 0 for no limit. Each returned pass can then be fed to asetupChunkingFor*call, which will further stream that pass in subpass-sized chunks bounded by its ownpassReadLimitargument.- Returns:
- an array of arrays of row group indices, one per pass
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-