Add logical type inspection for raw Parquet VARIANT values - #23491
Add logical type inspection for raw Parquet VARIANT values#23491abigalekim wants to merge 25 commits into
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds the public ChangesVARIANT type-ID extraction
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
cpp/include/cudf/io/experimental/variant_spec.hpp (1)
56-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssign explicit values to
variant_logical_type.The enumerators of
variant_logical_typerely on implicit sequential values.get_variant_type_idcasts these values toint32_tand returns them as output data. If a new logical type is inserted in the middle of this list later, every subsequent numeric ID shifts silently. This can break downstream consumers that persist or compare these IDs.Assign each enumerator an explicit value now, while the API is new, to fix the wire contract.
♻️ Proposed fix to pin enumerator values
enum class variant_logical_type : uint8_t { - object, - array, - null_value, - boolean, - long_value, - string, - double_value, - decimal, - date, - timestamp, - timestamp_ntz, - float_value, - binary, - uuid + object = 0, + array = 1, + null_value = 2, + boolean = 3, + long_value = 4, + string = 5, + double_value = 6, + decimal = 7, + date = 8, + timestamp = 9, + timestamp_ntz = 10, + float_value = 11, + binary = 12, + uuid = 13 };🤖 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/include/cudf/io/experimental/variant_spec.hpp` around lines 56 - 71, Update the variant_logical_type enum so every enumerator has an explicit numeric value, preserving its current sequential IDs and establishing a stable wire contract for get_variant_type_id. Use the existing declaration order and assign values starting at zero through uuid; do not change the enum members or their ordering.cpp/src/io/parquet/experimental/variant_extract.cu (1)
790-817: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated value-span construction into a helper.
Lines 803-806 repeat the same offset/span construction already used in
cast_variant_primitive_kernel(lines 632-636) andcast_variant_string_fn::operator()(lines 670-674). This is now the third copy of the same three-line idiom.Extract a small
__device__helper that returns the valuedevice_span<uint8_t const>for a row, and call it from all three sites. This reduces duplication and keeps future changes to the span-lookup logic in one place.♻️ Proposed helper extraction
__device__ inline device_span<uint8_t const> value_span_at( cudf::lists_column_device_view const& values, size_type row) { auto const val_begin = values.offset_at(row); auto const val_end = values.offset_at(row + 1); return {values.child().data<uint8_t>() + val_begin, static_cast<std::size_t>(val_end - val_begin)}; }auto const val_begin = values.offset_at(row); auto const val_end = values.offset_at(row + 1); - device_span<uint8_t const> const val{values.child().data<uint8_t>() + val_begin, - static_cast<std::size_t>(val_end - val_begin)}; + auto const val = value_span_at(values, row);🤖 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/variant_extract.cu` around lines 790 - 817, Extract the repeated value offset/span construction into a shared __device__ helper, such as value_span_at, accepting the lists_column_device_view and row and returning device_span<uint8_t const>. Replace the duplicated three-line logic in get_variant_type_id_kernel, cast_variant_primitive_kernel, and cast_variant_string_fn::operator() with calls to this helper, preserving the existing span contents and behavior.
🤖 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/variant_extract_test.cpp`:
- Around line 1621-1657: Increase num_rows in
GetVariantTypeIdTest.LargeMultiRowColumn from 128 to a value above the kernel
block size, such as 600, so get_variant_type_id exercises the multi-block
grid-stride path while preserving the existing cycling type coverage and
expected_ids generation.
---
Nitpick comments:
In `@cpp/include/cudf/io/experimental/variant_spec.hpp`:
- Around line 56-71: Update the variant_logical_type enum so every enumerator
has an explicit numeric value, preserving its current sequential IDs and
establishing a stable wire contract for get_variant_type_id. Use the existing
declaration order and assign values starting at zero through uuid; do not change
the enum members or their ordering.
In `@cpp/src/io/parquet/experimental/variant_extract.cu`:
- Around line 790-817: Extract the repeated value offset/span construction into
a shared __device__ helper, such as value_span_at, accepting the
lists_column_device_view and row and returning device_span<uint8_t const>.
Replace the duplicated three-line logic in get_variant_type_id_kernel,
cast_variant_primitive_kernel, and cast_variant_string_fn::operator() with calls
to this helper, preserving the existing span contents and behavior.
🪄 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: f1423210-95f8-4ad2-ab54-2d2cafd21e53
📒 Files selected for processing (4)
cpp/include/cudf/io/experimental/variant.hppcpp/include/cudf/io/experimental/variant_spec.hppcpp/src/io/parquet/experimental/variant_extract.cucpp/tests/io/experimental/variant_extract_test.cpp
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
|
/ok to test 2dc6168 |
nartal1
left a comment
There was a problem hiding this comment.
Overall LGTM. Just a couple of nits.
| inline std::unique_ptr<cudf::column> make_list_u8_nullable( | ||
| std::vector<std::vector<uint8_t>> const& blobs, std::vector<bool> const& valid) | ||
| { | ||
| auto const n = static_cast<cudf::size_type>(blobs.size()); |
There was a problem hiding this comment.
Please avoid single letter variable names
There was a problem hiding this comment.
I tried to change a lot of these in the codebase! Thank you for the pointer.
|
@abigalekim I will be OOO next week so please feel free to dismiss my |
|
Tip For best results, initiate chat on the files or code changes.
You are interacting with an AI system. |
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
Co-authored-by: Muhammad Haseeb <14217455+mhaseeb123@users.noreply.github.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
cpp/tests/io/experimental/variant_extract_test.cpp (3)
1696-1696:⚠️ Potential issue | 🔴 CriticalUse the declared
streamforget_sliced_child.Line 1696 passes
stream2, but this test no longer declaresstream2. Replace it withstream; otherwise the test fails to compile.- auto const value_child = cudf::structs_column_view{col}.get_sliced_child(1, stream2); + auto const value_child = cudf::structs_column_view{col}.get_sliced_child(1, stream);Run:
#!/usr/bin/env bash set -euo pipefail file=cpp/tests/io/experimental/variant_extract_test.cpp if rg -q '\bstream2\b' "$file"; then echo "stream2 reference remains" >&2 exit 1 fi🤖 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/variant_extract_test.cpp` at line 1696, Update the get_sliced_child call in the variant extraction test to pass the declared stream variable instead of the undeclared stream2 symbol, and ensure no stream2 references remain in the file.
1451-1574:⚠️ Potential issue | 🔴 CriticalUse
vltconsistently after the alias rename.Line 1451 declares
using vlt = ..., but the later assertions still useLT::....LTis undefined, so the test translation unit cannot compile. Replace all remainingLT::references withvlt::.- static_cast<int32_t>(LT::long_value) + static_cast<int32_t>(vlt::long_value)Run:
#!/usr/bin/env bash set -euo pipefail file=cpp/tests/io/experimental/variant_extract_test.cpp if rg -q '\busing vlt\b' "$file" && rg -q '\bLT::' "$file"; then echo "stale LT references remain" >&2 exit 1 fi🤖 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/variant_extract_test.cpp` around lines 1451 - 1574, Replace every remaining LT:: reference in the GetVariantTypeIdTest cases, including NullValue through ObjectAndArray, with the declared vlt:: alias so the test compiles and uses the renamed alias consistently.
17-17: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd direct includes for all new test symbols.
Include
cudf_test/cudf_gtest.hpp,<algorithm>, and<stdexcept>directly. Do not rely on transitive includes.🤖 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/variant_extract_test.cpp` at line 17, Add direct includes for the test symbols used in variant_extract_test.cpp: cudf_test/cudf_gtest.hpp, <algorithm>, and <stdexcept>. Place them with the existing include directives and avoid relying on transitive headers.Source: Coding guidelines
🤖 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.
Outside diff comments:
In `@cpp/tests/io/experimental/variant_extract_test.cpp`:
- Line 1696: Update the get_sliced_child call in the variant extraction test to
pass the declared stream variable instead of the undeclared stream2 symbol, and
ensure no stream2 references remain in the file.
- Around line 1451-1574: Replace every remaining LT:: reference in the
GetVariantTypeIdTest cases, including NullValue through ObjectAndArray, with the
declared vlt:: alias so the test compiles and uses the renamed alias
consistently.
- Line 17: Add direct includes for the test symbols used in
variant_extract_test.cpp: cudf_test/cudf_gtest.hpp, <algorithm>, and
<stdexcept>. Place them with the existing include directives and avoid relying
on transitive headers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 5f5ddb72-0a49-4aeb-8a27-2d00f7e2f584
📒 Files selected for processing (2)
cpp/include/cudf/io/experimental/variant_spec.hppcpp/tests/io/experimental/variant_extract_test.cpp
💤 Files with no reviewable changes (1)
- cpp/include/cudf/io/experimental/variant_spec.hpp
…ak/variant-type-id
|
/ok to test b0dfd69 |
vuule
left a comment
There was a problem hiding this comment.
Few API design questions, mostly for the feature requester :)
| TIMESTAMP = 9, | ||
| TIMESTAMP_NTZ = 10, |
There was a problem hiding this comment.
@nartal1 do we need values for nanosecond types?
There was a problem hiding this comment.
The above looks good. Both TIMESTAMP_MICROS and TIMESTAMP_NANOS maps to TIMESTAMP logical ID.
| * Classifies only the value_metadata header byte; does not validate the remaining payload. | ||
| * A recognized header returns its logical type even when the payload is truncated. A null output | ||
| * row is produced when the input row is null, the blob is empty, or the header carries an | ||
| * unrecognized type. An encoded Variant null (NULLVAL) produces a valid `NULL_VALUE` row. |
There was a problem hiding this comment.
@nartal1 is it okay that this API returns null in multiple cases, i.e.
the input row was null, the blob was empty, or the header was unrecognized/malformed.
I assume this is what the status column is for, but want to confirm.
There was a problem hiding this comment.
This behavior is sufficient here from cudf-spark perspective. We can handle the above from the status column from PR 23560. Thanks for checking.
| * @throws std::invalid_argument if `values` is not a `list<uint8>` column | ||
| */ | ||
| [[nodiscard]] std::unique_ptr<column> get_variant_type_id( | ||
| column_view const& values, |
There was a problem hiding this comment.
@nartal1 do to expect to need a "batched" version of this API, i.e. something that takes a table view and returns a table? If you expect to regularly run this on multiple columns , it could even be the only API, and the caller would create a single column table when we need the current capability.
There was a problem hiding this comment.
For the current tasks, we only need the single-column API.
But later, Spark scan pushdown can request several fields from one Variant and can reference multiple Variant columns in the same scan, so a batched classifier overload may become useful.
However, the primary requirement there is batched multi-field extraction - tracked in #22897. I think that path can also provide any required per-field type or status information without a separate classification pass. Do you have any thoughts on how you would be handling the multi field extraction in cudf?
|
/ok to test e5246f8 |
Description
Implements feature mentioned in #23183. Adds an experimental libcudf operation,
cudf::io::parquet::experimental::get_variant_type_id, that takes a raw Variant list column and returns an int32 column of logical type identifiers. This PR also introduces a new variant_logical_type enum covering all Variant categories.Checklist