Skip to content

Commit 0039691

Browse files
authored
Add Java Variant extraction support (#23069)
This PR contributes to NVIDIA/cudf-spark#15180 Adds cuDF Java bindings for the experimental cuDF Variant extraction APIs used to read typed values from Parquet Variant columns. This exposes Java wrappers for: - extracting a Variant field by path - casting extracted Variant values to supported scalar types - extracting and casting a field in one call The implementation wraps the cuDF C++ APIs in `cudf/io/experimental/variant.hpp`, including `get_variant_field`, `cast_variant`, and `extract_variant_field`. This is needed by Spark RAPIDS Variant support so expressions such as `try_variant_get` can call into cuDF through the normal cuDF Java. ## Supported Types The current bindings support the Variant target types currently supported by the cuDF API: - `STRING` - `INT8` - `INT16` - `INT32` - `INT64` Other Spark Variant target types can be added later as cuDF support becomes available. ## Testing Added `VariantUtilsTest` coverage for: - field extraction by path - casting Variant values to supported target types - combined extract-and-cast behavior - unsupported target type validation - null argument validation Authors: - Niranjan Artal (https://github.com/nartal1) - Nghia Truong (https://github.com/ttnghia) Approvers: - Nghia Truong (https://github.com/ttnghia) URL: #23069
1 parent 9e00a5d commit 0039691

4 files changed

Lines changed: 629 additions & 1 deletion

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package ai.rapids.cudf;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import java.util.Objects;
11+
12+
/**
13+
* Utility methods for cuDF's experimental Parquet Variant extraction APIs.
14+
*/
15+
public class VariantUtils {
16+
static {
17+
NativeDepsLoader.loadNativeDeps();
18+
}
19+
20+
// Keep in sync with the target types accepted by cuDF Variant extraction/cast:
21+
// cpp/include/cudf/io/experimental/variant.hpp and
22+
// cpp/src/io/parquet/experimental/variant_extract.cu:is_variant_castable.
23+
private static final List<DType> SUPPORTED_TYPES = Arrays.asList(
24+
DType.STRING, DType.INT8, DType.INT16, DType.INT32, DType.INT64);
25+
26+
private VariantUtils() {}
27+
28+
private static void validateTargetType(DType targetType) {
29+
Objects.requireNonNull(targetType, "targetType");
30+
if (!SUPPORTED_TYPES.contains(targetType)) {
31+
throw new IllegalArgumentException("unsupported Variant target type: " + targetType +
32+
"; supported types are " + SUPPORTED_TYPES);
33+
}
34+
}
35+
36+
/**
37+
* Extract raw Variant-encoded value bytes at {@code path} from a Variant struct column.
38+
*
39+
* @param variantStruct Variant materialization: STRUCT(metadata LIST&lt;UINT8&gt;,
40+
* value LIST&lt;UINT8&gt;, optional shredded children...)
41+
* @param path JSONPath-like path accepted by cuDF's Variant extractor. Paths are expected to
42+
* be ASCII object-field paths like {@code x}, {@code $.x}, or {@code $.x.y}.
43+
* @return LIST&lt;UINT8&gt; column of raw encoded Variant values
44+
*/
45+
public static ColumnVector getVariantFieldValue(ColumnView variantStruct, String path) {
46+
Objects.requireNonNull(variantStruct, "variantStruct");
47+
Objects.requireNonNull(path, "path");
48+
return new ColumnVector(getVariantFieldValue(variantStruct.getNativeView(), path));
49+
}
50+
51+
/**
52+
* Decode raw Variant-encoded value bytes into {@code targetType}. Supported target types are
53+
* {@link DType#STRING}, {@link DType#INT8}, {@link DType#INT16}, {@link DType#INT32}, and
54+
* {@link DType#INT64}.
55+
*/
56+
public static ColumnVector castVariantValue(ColumnView valueBytes, DType targetType) {
57+
Objects.requireNonNull(valueBytes, "valueBytes");
58+
validateTargetType(targetType);
59+
return new ColumnVector(castVariantValue(
60+
valueBytes.getNativeView(), targetType.getTypeId().getNativeId()));
61+
}
62+
63+
/**
64+
* Extract a Variant field and decode it into {@code targetType} in one native call.
65+
* Supported target types are {@link DType#STRING}, {@link DType#INT8}, {@link DType#INT16},
66+
* {@link DType#INT32}, and {@link DType#INT64}.
67+
*/
68+
public static ColumnVector extractVariantField(
69+
ColumnView variantStruct, String path, DType targetType) {
70+
Objects.requireNonNull(variantStruct, "variantStruct");
71+
Objects.requireNonNull(path, "path");
72+
validateTargetType(targetType);
73+
return new ColumnVector(extractVariantField(
74+
variantStruct.getNativeView(), path, targetType.getTypeId().getNativeId()));
75+
}
76+
77+
private static native long getVariantFieldValue(long variantStructHandle, String path);
78+
79+
private static native long castVariantValue(long valueBytesHandle, int cudfTypeId);
80+
81+
private static native long extractVariantField(
82+
long variantStructHandle, String path, int cudfTypeId);
83+
}

java/src/main/native/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# =============================================================================
22
# cmake-format: off
3-
# SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
3+
# SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# SPDX-License-Identifier: Apache-2.0
55
# cmake-format: on
66
# =============================================================================
@@ -179,6 +179,7 @@ add_library(
179179
src/RmmJni.cpp
180180
src/ScalarJni.cpp
181181
src/TableJni.cpp
182+
src/VariantUtilsJni.cpp
182183
src/aggregation128_utils.cu
183184
src/check_nvcomp_output_sizes.cu
184185
src/maps_column_view.cu
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "cudf_jni_apis.hpp"
7+
8+
#include <cudf/io/experimental/variant.hpp>
9+
#include <cudf/types.hpp>
10+
#include <cudf/utilities/default_stream.hpp>
11+
#include <cudf/utilities/memory_resource.hpp>
12+
13+
extern "C" {
14+
15+
JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_VariantUtils_getVariantFieldValue(
16+
JNIEnv* env, jclass, jlong variant_struct_handle, jstring j_path)
17+
{
18+
JNI_NULL_CHECK(env, variant_struct_handle, "variant struct column is null", 0);
19+
JNI_NULL_CHECK(env, j_path, "path is null", 0);
20+
JNI_TRY
21+
{
22+
cudf::jni::auto_set_device(env);
23+
auto const& variant_struct = *reinterpret_cast<cudf::column_view const*>(variant_struct_handle);
24+
cudf::jni::native_jstring path(env, j_path);
25+
return cudf::jni::release_as_jlong(
26+
cudf::io::parquet::experimental::get_variant_field(variant_struct,
27+
path.get(),
28+
cudf::get_default_stream(),
29+
cudf::get_current_device_resource_ref()));
30+
}
31+
JNI_CATCH(env, 0);
32+
}
33+
34+
JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_VariantUtils_castVariantValue(JNIEnv* env,
35+
jclass,
36+
jlong value_bytes_handle,
37+
jint cudf_type_id)
38+
{
39+
JNI_NULL_CHECK(env, value_bytes_handle, "value bytes column is null", 0);
40+
JNI_TRY
41+
{
42+
cudf::jni::auto_set_device(env);
43+
auto const& value_bytes = *reinterpret_cast<cudf::column_view const*>(value_bytes_handle);
44+
return cudf::jni::release_as_jlong(cudf::io::parquet::experimental::cast_variant(
45+
value_bytes,
46+
cudf::data_type{static_cast<cudf::type_id>(cudf_type_id)},
47+
cudf::get_default_stream(),
48+
cudf::get_current_device_resource_ref()));
49+
}
50+
JNI_CATCH(env, 0);
51+
}
52+
53+
JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_VariantUtils_extractVariantField(
54+
JNIEnv* env, jclass, jlong variant_struct_handle, jstring j_path, jint cudf_type_id)
55+
{
56+
JNI_NULL_CHECK(env, variant_struct_handle, "variant struct column is null", 0);
57+
JNI_NULL_CHECK(env, j_path, "path is null", 0);
58+
JNI_TRY
59+
{
60+
cudf::jni::auto_set_device(env);
61+
auto const& variant_struct = *reinterpret_cast<cudf::column_view const*>(variant_struct_handle);
62+
cudf::jni::native_jstring path(env, j_path);
63+
return cudf::jni::release_as_jlong(cudf::io::parquet::experimental::extract_variant_field(
64+
variant_struct,
65+
path.get(),
66+
cudf::data_type{static_cast<cudf::type_id>(cudf_type_id)},
67+
cudf::get_default_stream(),
68+
cudf::get_current_device_resource_ref()));
69+
}
70+
JNI_CATCH(env, 0);
71+
}
72+
73+
} // extern "C"

0 commit comments

Comments
 (0)