Skip to content

Add array indexing support to Parquet variant field extraction - #22895

Merged
rapids-bot[bot] merged 22 commits into
rapidsai:release/26.08from
vuule:fea-variant-array-indexing
Jul 23, 2026
Merged

Add array indexing support to Parquet variant field extraction#22895
rapids-bot[bot] merged 22 commits into
rapidsai:release/26.08from
vuule:fea-variant-array-indexing

Conversation

@vuule

@vuule vuule commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Contributes to #22312

Extends the JSONPath-like path used by get_variant_field/extract_variant_field to descend into array values via zero-based [N] index steps, in addition to the existing object-key descent. This enables extraction of array elements from Parquet VARIANT columns (e.g. $.a[0], $[0].field).

Leading zeroes are allowed, whitespace characters are not.

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

@github-actions github-actions Bot added the libcudf Affects libcudf (C++/CUDA) code. label Jun 15, 2026
@vuule vuule added feature request New feature or request non-breaking Non-breaking change labels Jun 15, 2026
@vuule vuule changed the title Add array-index support to Parquet VARIANT field extraction Add array indexing support to Parquet variant field extraction Jun 16, 2026
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/src/io/parquet/experimental/variant_path.cpp
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
if (variant_basic_type(value_metadata) != basic_type::array) { return {}; }

int const value_header = variant_value_header(value_metadata);
[[maybe_unused]] auto const [offset_size, id_size, num_elements_size] =

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.

Is id_size used anywhere? I think in C++ 26 you can use the underscore to indicate unused variables, but I'm unsure what standard we use.

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.

We currently build with C++20. [[maybe_unused]] is applied to all bindings here. I also renamed id_size to _ to make it clearer which binding is unused.

Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/tests/io/experimental/variant_extract_test.cpp
Comment thread cpp/tests/io/experimental/variant_extract_test.cpp
@GregoryKimball GregoryKimball moved this to Burndown in libcudf Jul 13, 2026
@github-actions github-actions Bot added the Java Affects Java cuDF API. label Jul 20, 2026
@vuule

vuule commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test fdd70bb

@vuule
vuule marked this pull request as ready for review July 20, 2026 22:47
@vuule
vuule requested review from a team as code owners July 20, 2026 22:47
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds bracketed array-index steps to VARIANT paths, validates malformed index syntax, resolves indexed Apache VARIANT arrays with bounds checks, and expands C++ and Java test coverage for nested traversal and malformed inputs.

Changes

Variant array path support

Layer / File(s) Summary
Bracketed index path parsing
cpp/src/io/parquet/experimental/variant_path.*, cpp/include/cudf/io/experimental/variant.hpp
Path parsing accepts non-negative bracketed indices, rejects malformed forms, and documents object/array traversal syntax and null behavior.
Array element resolution
cpp/src/io/parquet/experimental/variant_extract.cu
VARIANT array metadata, offsets, element bounds, and indexed path steps are validated during extraction.
Array traversal validation
cpp/tests/io/experimental/variant_extract_test.cpp, java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java
Tests cover valid indexing, malformed paths, empty or malformed arrays, type mismatches, nested traversal, and out-of-bounds results.

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

Possibly related PRs

  • rapidsai/cudf#23069: Directly updates the related Java malformed-path test and matching variant path call sites.

Suggested labels: tests, improvement, doc, cuIO

Suggested reviewers: vyasr, abigalekim, gforsyth, mroeschke

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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 accurately summarizes the main change: adding array indexing support to variant field extraction.
Description check ✅ Passed The description directly matches the changeset by describing array indexing support, leading zeros, and updated tests/docs.
✨ 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.

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

514-637: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a sliced-column case for array-index paths.

The new array-indexing tests (ApacheArrayPrimitiveIndexing, EmptyArrayIndexing, MixedObjectArrayTraversal, etc.) all use unsliced columns. get_variant_field resolves metadata/value spans via get_sliced_child, so slice-offset arithmetic combined with array-index resolution is untested by this PR.

As per coding guidelines, "Tests must cover empty inputs, nulls, sliced columns, boundary and multi-block sizes, and non-ASCII UTF-8 for string tests."

🤖 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 514 - 637,
Add sliced-column coverage to the array-index path tests, preferably in
ApacheArrayPrimitiveIndexing or MixedObjectArrayTraversal, by creating a sliced
variant column with a nonzero offset and asserting representative indexed paths
still return the expected values and nulls. Reuse the existing extraction
helpers and fixtures while ensuring the slice exercises get_sliced_child
metadata/value span arithmetic.

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.

Nitpick comments:
In `@cpp/tests/io/experimental/variant_extract_test.cpp`:
- Around line 514-637: Add sliced-column coverage to the array-index path tests,
preferably in ApacheArrayPrimitiveIndexing or MixedObjectArrayTraversal, by
creating a sliced variant column with a nonzero offset and asserting
representative indexed paths still return the expected values and nulls. Reuse
the existing extraction helpers and fixtures while ensuring the slice exercises
get_sliced_child metadata/value span arithmetic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: afccee14-ba8d-4397-a369-a235ec56ff66

📥 Commits

Reviewing files that changed from the base of the PR and between 8eba171 and fdd70bb.

📒 Files selected for processing (6)
  • cpp/include/cudf/io/experimental/variant.hpp
  • cpp/src/io/parquet/experimental/variant_extract.cu
  • cpp/src/io/parquet/experimental/variant_path.cpp
  • cpp/src/io/parquet/experimental/variant_path.hpp
  • cpp/tests/io/experimental/variant_extract_test.cpp
  • java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java

vuule added 7 commits July 21, 2026 19:57
Extend the JSONPath-like path used by get_variant_field/extract_variant_field
to descend into array values via zero-based "[N]" steps, in addition to the
existing object-key descent.

- variant_path.cpp: parse "[<non-negative integer>]" steps, preserving the
  literal "[N]" token so the GPU path walker can distinguish index steps from
  object-key steps by their first byte. Wildcards ("[*]"), negative indices,
  quoted names in brackets, and unterminated brackets throw.
- variant_extract.cu: add locate_array_element (parses the array value header
  and returns the N-th element's subspan) and dispatch on "[" in resolve_path.
- variant.hpp: document the extended path grammar and array-indexing behavior.
- Tests: positive int8 array indexing, out-of-bounds / type-mismatch nulls, and
  updated SyntaxErrors to accept "$.a[0]" while rejecting "$.a[-1]" and "$.a[1".

Verified locally: VARIANT_EXTRACT_TEST 33/33 pass.
Reject out-of-range "[N]" indices at parse time (std::from_chars bound
to cudf::size_type) and accumulate the index in uint64_t on the GPU path
walker to avoid signed-int32 overflow (UB). Add a SyntaxErrors case for an
over-long index, document that out-of-range indices throw, and note that
array element offsets are monotonic (unlike object field offsets).
@vuule
vuule force-pushed the fea-variant-array-indexing branch from fdd70bb to 9d84fe3 Compare July 21, 2026 19:58
@vuule
vuule requested review from a team as code owners July 21, 2026 19:58
@vuule
vuule requested review from gforsyth, mroeschke and vyasr July 21, 2026 19:58
@vuule
vuule changed the base branch from main to release/26.08 July 21, 2026 19:58

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
python/cudf_polars/pyproject.toml (1)

1-1: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Per-test timeout protection was dropped python/cudf_polars/pyproject.toml#L48-L50,93-L94 and the CI runners now rely only on ci/timeout_with_stack.py for a 3600s whole-run timeout. That means one hung test can stall the entire suite for up to an hour instead of failing independently. Keep a per-test timeout guard, or explicitly accept the loss of suite continuation on hangs.

🤖 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 `@python/cudf_polars/pyproject.toml` at line 1, Restore per-test timeout
protection in the pytest configuration within pyproject.toml, alongside the
existing suite-level ci/timeout_with_stack.py guard. Configure the timeout
plugin or equivalent test-level mechanism so each hung test fails independently
while preserving the current whole-run timeout.
java/ci/build_static_libcudf_in_container.sh (1)

91-96: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a trap to chown /output on any exit, not only on success.

If cmake --install fails partway through, files already written to /output (bind-mounted to the host OUTPUT_DIR) stay root-owned since the unconditional chown at the end never runs. The sibling build_cudf_java_jar_in_container.sh already solves the identical problem via trap _chown_outputs_on_exit EXIT; this script should do the same for consistency and to avoid leaving root-owned partial output that a subsequent local re-run's rm -rf (see test_java_build_local.sh lines 154-157) may struggle to clean up.

🔧 Proposed fix
+_chown_output_on_exit() {
+  chown -R "${HOST_UID}:${HOST_GID}" "${INSTALL_PREFIX}" 2>/dev/null || true
+}
+trap _chown_output_on_exit EXIT
+
 rapids-logger "Installing static libcudf to ${INSTALL_PREFIX}"
 cmake --install "${BUILD_DIR}"
-
-rapids-logger "Chowning ${INSTALL_PREFIX} to ${HOST_UID}:${HOST_GID}"
-chown -R "${HOST_UID}:${HOST_GID}" "${INSTALL_PREFIX}"
🤖 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 `@java/ci/build_static_libcudf_in_container.sh` around lines 91 - 96, Add an
EXIT trap in build_static_libcudf_in_container.sh matching the sibling script’s
_chown_outputs_on_exit pattern, ensuring /output is chowned to HOST_UID:HOST_GID
on both successful and failed exits. Remove or avoid relying on the later
unconditional chown after cmake --install so partial installations are also
repaired.
🧹 Nitpick comments (2)
ci/timeout_with_stack.py (1)

210-240: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use psutil.wait_procs instead of sequential per-child waits.

Waiting on each child one at a time (lines 222-225) makes the worst-case wait time scale with the number of children (num_children × 3s), adding avoidable delay to an already-timed-out run (e.g. -n 4 pytest-xdist workers). psutil.wait_procs() waits on the whole list concurrently against one shared deadline and returns the still-alive subset directly.

♻️ Proposed refactor using `psutil.wait_procs`
     try:
         parent = psutil.Process(pid)
         children = parent.children(recursive=True)

         # Terminate children first
         for child in children:
             with suppress(psutil.NoSuchProcess):
                 child.terminate()

-        # Create a copy of children list
-        terminated_children = list(children)
-
-        # Wait for all children to terminate
-        for child in terminated_children:
-            with suppress(psutil.TimeoutExpired):
-                child.wait(timeout=3)
-
-        # Kill any remaining children
-        for child in terminated_children:
-            with suppress(psutil.NoSuchProcess):
-                child.kill()
+        # Wait on all children concurrently, then kill any stragglers
+        _, alive = psutil.wait_procs(children, timeout=3)
+        for child in alive:
+            with suppress(psutil.NoSuchProcess):
+                child.kill()

         # Terminate parent
         parent.terminate()
🤖 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 `@ci/timeout_with_stack.py` around lines 210 - 240, Replace the sequential
child.wait loop in the process-termination flow with psutil.wait_procs using the
terminated_children list and a single 3-second timeout. Use its returned alive
subset when killing remaining children, while preserving the existing parent
termination and NoSuchProcess handling.
java/ci/README.md (1)

47-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a language hint to the fenced directory-tree blocks.

markdownlint (MD040) flags the fenced blocks at Line 47 and Line 75 for missing a language identifier.

📝 Proposed fix
-```
+```text
 /tmp/jars/cuda12/
     cudf-26.08.0-SNAPSHOT-cuda12.jar
     cudf-26.08.0-SNAPSHOT.pom
-```
+```text
 /tmp/maven-repo/ai/rapids/cudf/26.08.0-SNAPSHOT/
     cudf-26.08.0-SNAPSHOT-cuda12.jar
     cudf-26.08.0-SNAPSHOT-cuda13.jar
     cudf-26.08.0-SNAPSHOT.pom
🤖 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 `@java/ci/README.md` around lines 47 - 75, Add the text language identifier to
both fenced directory-tree blocks in the README, including the examples
beginning with /tmp/jars/cuda12/ and /tmp/maven-repo/ai/rapids/cudf/. Preserve
their displayed tree contents unchanged.

Source: Linters/SAST tools

🤖 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 `@java/ci/build_static_libcudf_in_container.sh`:
- Line 19: Enable pipefail in both scripts before their
rapids-dependency-file-generator pipelines: update
java/ci/build_static_libcudf_in_container.sh lines 19-19 and
java/ci/build_cudf_java_jar_in_container.sh lines 22-22 to use set -eo pipefail
or an equivalent separate setting. Ensure generator failures propagate instead
of being masked by tee.

In `@java/ci/build_static_libcudf.sh`:
- Around line 103-104: Update the output-directory setup in
build_static_libcudf.sh to reject a pre-existing non-empty --output-dir before
creating or resolving it, matching the stale-output protection used by
CLASSIFIER_OUT in build_cudf_java_jar.sh. Preserve support for a new or empty
directory and only continue with the build after validating it is safe to reuse.

---

Outside diff comments:
In `@java/ci/build_static_libcudf_in_container.sh`:
- Around line 91-96: Add an EXIT trap in build_static_libcudf_in_container.sh
matching the sibling script’s _chown_outputs_on_exit pattern, ensuring /output
is chowned to HOST_UID:HOST_GID on both successful and failed exits. Remove or
avoid relying on the later unconditional chown after cmake --install so partial
installations are also repaired.

In `@python/cudf_polars/pyproject.toml`:
- Line 1: Restore per-test timeout protection in the pytest configuration within
pyproject.toml, alongside the existing suite-level ci/timeout_with_stack.py
guard. Configure the timeout plugin or equivalent test-level mechanism so each
hung test fails independently while preserving the current whole-run timeout.

---

Nitpick comments:
In `@ci/timeout_with_stack.py`:
- Around line 210-240: Replace the sequential child.wait loop in the
process-termination flow with psutil.wait_procs using the terminated_children
list and a single 3-second timeout. Use its returned alive subset when killing
remaining children, while preserving the existing parent termination and
NoSuchProcess handling.

In `@java/ci/README.md`:
- Around line 47-75: Add the text language identifier to both fenced
directory-tree blocks in the README, including the examples beginning with
/tmp/jars/cuda12/ and /tmp/maven-repo/ai/rapids/cudf/. Preserve their displayed
tree contents unchanged.
🪄 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: f39df46d-ff2d-4d85-b5d4-2f0b0c648b6d

📥 Commits

Reviewing files that changed from the base of the PR and between fdd70bb and 9d84fe3.

📒 Files selected for processing (37)
  • .github/workflows/build.yaml
  • ci/run_cudf_polars_polars_tests.sh
  • ci/run_cudf_polars_pytests.sh
  • ci/test_wheel_cudf_polars.sh
  • ci/timeout_with_stack.py
  • conda/environments/all_cuda-129_arch-aarch64.yaml
  • conda/environments/all_cuda-129_arch-x86_64.yaml
  • conda/environments/all_cuda-133_arch-aarch64.yaml
  • conda/environments/all_cuda-133_arch-x86_64.yaml
  • cpp/include/cudf/io/experimental/variant.hpp
  • cpp/src/io/parquet/experimental/variant_extract.cu
  • cpp/src/io/parquet/experimental/variant_path.cpp
  • cpp/src/io/parquet/experimental/variant_path.hpp
  • cpp/tests/io/experimental/variant_extract_test.cpp
  • dependencies.yaml
  • java/ci/README.md
  • java/ci/argparse.sh
  • java/ci/assemble_maven_repo.sh
  • java/ci/build_cudf_java_jar.sh
  • java/ci/build_cudf_java_jar_in_container.sh
  • java/ci/build_static_libcudf.sh
  • java/ci/build_static_libcudf_in_container.sh
  • java/ci/test_java_build_local.sh
  • java/pom.xml
  • java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java
  • python/cudf/cudf/core/column/numerical.py
  • python/cudf/cudf/pandas/_wrappers/pandas.py
  • python/cudf/cudf/pandas/fast_slow_proxy.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
  • python/cudf/cudf/tests/series/methods/test_astype.py
  • python/cudf/cudf_pandas_tests/test_cudf_pandas.py
  • python/cudf/cudf_pandas_tests/test_fast_slow_proxy.py
  • python/cudf_polars/pyproject.toml
  • python/cudf_polars/tests/conftest.py
  • python/cudf_polars/tests/expressions/test_rolling.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_sort.py
💤 Files with no reviewable changes (4)
  • python/cudf_polars/tests/conftest.py
  • python/cudf_polars/tests/streaming/test_sort.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/expressions/test_rolling.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • cpp/src/io/parquet/experimental/variant_path.hpp
  • java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java
  • cpp/include/cudf/io/experimental/variant.hpp
  • cpp/src/io/parquet/experimental/variant_extract.cu
  • cpp/tests/io/experimental/variant_extract_test.cpp
  • cpp/src/io/parquet/experimental/variant_path.cpp

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
python/cudf_polars/pyproject.toml (1)

1-1: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Per-test timeout protection was dropped python/cudf_polars/pyproject.toml#L48-L50,93-L94 and the CI runners now rely only on ci/timeout_with_stack.py for a 3600s whole-run timeout. That means one hung test can stall the entire suite for up to an hour instead of failing independently. Keep a per-test timeout guard, or explicitly accept the loss of suite continuation on hangs.

🤖 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 `@python/cudf_polars/pyproject.toml` at line 1, Restore per-test timeout
protection in the pytest configuration within pyproject.toml, alongside the
existing suite-level ci/timeout_with_stack.py guard. Configure the timeout
plugin or equivalent test-level mechanism so each hung test fails independently
while preserving the current whole-run timeout.
java/ci/build_static_libcudf_in_container.sh (1)

91-96: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a trap to chown /output on any exit, not only on success.

If cmake --install fails partway through, files already written to /output (bind-mounted to the host OUTPUT_DIR) stay root-owned since the unconditional chown at the end never runs. The sibling build_cudf_java_jar_in_container.sh already solves the identical problem via trap _chown_outputs_on_exit EXIT; this script should do the same for consistency and to avoid leaving root-owned partial output that a subsequent local re-run's rm -rf (see test_java_build_local.sh lines 154-157) may struggle to clean up.

🔧 Proposed fix
+_chown_output_on_exit() {
+  chown -R "${HOST_UID}:${HOST_GID}" "${INSTALL_PREFIX}" 2>/dev/null || true
+}
+trap _chown_output_on_exit EXIT
+
 rapids-logger "Installing static libcudf to ${INSTALL_PREFIX}"
 cmake --install "${BUILD_DIR}"
-
-rapids-logger "Chowning ${INSTALL_PREFIX} to ${HOST_UID}:${HOST_GID}"
-chown -R "${HOST_UID}:${HOST_GID}" "${INSTALL_PREFIX}"
🤖 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 `@java/ci/build_static_libcudf_in_container.sh` around lines 91 - 96, Add an
EXIT trap in build_static_libcudf_in_container.sh matching the sibling script’s
_chown_outputs_on_exit pattern, ensuring /output is chowned to HOST_UID:HOST_GID
on both successful and failed exits. Remove or avoid relying on the later
unconditional chown after cmake --install so partial installations are also
repaired.
🧹 Nitpick comments (2)
ci/timeout_with_stack.py (1)

210-240: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use psutil.wait_procs instead of sequential per-child waits.

Waiting on each child one at a time (lines 222-225) makes the worst-case wait time scale with the number of children (num_children × 3s), adding avoidable delay to an already-timed-out run (e.g. -n 4 pytest-xdist workers). psutil.wait_procs() waits on the whole list concurrently against one shared deadline and returns the still-alive subset directly.

♻️ Proposed refactor using `psutil.wait_procs`
     try:
         parent = psutil.Process(pid)
         children = parent.children(recursive=True)

         # Terminate children first
         for child in children:
             with suppress(psutil.NoSuchProcess):
                 child.terminate()

-        # Create a copy of children list
-        terminated_children = list(children)
-
-        # Wait for all children to terminate
-        for child in terminated_children:
-            with suppress(psutil.TimeoutExpired):
-                child.wait(timeout=3)
-
-        # Kill any remaining children
-        for child in terminated_children:
-            with suppress(psutil.NoSuchProcess):
-                child.kill()
+        # Wait on all children concurrently, then kill any stragglers
+        _, alive = psutil.wait_procs(children, timeout=3)
+        for child in alive:
+            with suppress(psutil.NoSuchProcess):
+                child.kill()

         # Terminate parent
         parent.terminate()
🤖 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 `@ci/timeout_with_stack.py` around lines 210 - 240, Replace the sequential
child.wait loop in the process-termination flow with psutil.wait_procs using the
terminated_children list and a single 3-second timeout. Use its returned alive
subset when killing remaining children, while preserving the existing parent
termination and NoSuchProcess handling.
java/ci/README.md (1)

47-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a language hint to the fenced directory-tree blocks.

markdownlint (MD040) flags the fenced blocks at Line 47 and Line 75 for missing a language identifier.

📝 Proposed fix
-```
+```text
 /tmp/jars/cuda12/
     cudf-26.08.0-SNAPSHOT-cuda12.jar
     cudf-26.08.0-SNAPSHOT.pom
-```
+```text
 /tmp/maven-repo/ai/rapids/cudf/26.08.0-SNAPSHOT/
     cudf-26.08.0-SNAPSHOT-cuda12.jar
     cudf-26.08.0-SNAPSHOT-cuda13.jar
     cudf-26.08.0-SNAPSHOT.pom
🤖 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 `@java/ci/README.md` around lines 47 - 75, Add the text language identifier to
both fenced directory-tree blocks in the README, including the examples
beginning with /tmp/jars/cuda12/ and /tmp/maven-repo/ai/rapids/cudf/. Preserve
their displayed tree contents unchanged.

Source: Linters/SAST tools

🤖 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 `@java/ci/build_static_libcudf_in_container.sh`:
- Line 19: Enable pipefail in both scripts before their
rapids-dependency-file-generator pipelines: update
java/ci/build_static_libcudf_in_container.sh lines 19-19 and
java/ci/build_cudf_java_jar_in_container.sh lines 22-22 to use set -eo pipefail
or an equivalent separate setting. Ensure generator failures propagate instead
of being masked by tee.

In `@java/ci/build_static_libcudf.sh`:
- Around line 103-104: Update the output-directory setup in
build_static_libcudf.sh to reject a pre-existing non-empty --output-dir before
creating or resolving it, matching the stale-output protection used by
CLASSIFIER_OUT in build_cudf_java_jar.sh. Preserve support for a new or empty
directory and only continue with the build after validating it is safe to reuse.

---

Outside diff comments:
In `@java/ci/build_static_libcudf_in_container.sh`:
- Around line 91-96: Add an EXIT trap in build_static_libcudf_in_container.sh
matching the sibling script’s _chown_outputs_on_exit pattern, ensuring /output
is chowned to HOST_UID:HOST_GID on both successful and failed exits. Remove or
avoid relying on the later unconditional chown after cmake --install so partial
installations are also repaired.

In `@python/cudf_polars/pyproject.toml`:
- Line 1: Restore per-test timeout protection in the pytest configuration within
pyproject.toml, alongside the existing suite-level ci/timeout_with_stack.py
guard. Configure the timeout plugin or equivalent test-level mechanism so each
hung test fails independently while preserving the current whole-run timeout.

---

Nitpick comments:
In `@ci/timeout_with_stack.py`:
- Around line 210-240: Replace the sequential child.wait loop in the
process-termination flow with psutil.wait_procs using the terminated_children
list and a single 3-second timeout. Use its returned alive subset when killing
remaining children, while preserving the existing parent termination and
NoSuchProcess handling.

In `@java/ci/README.md`:
- Around line 47-75: Add the text language identifier to both fenced
directory-tree blocks in the README, including the examples beginning with
/tmp/jars/cuda12/ and /tmp/maven-repo/ai/rapids/cudf/. Preserve their displayed
tree contents unchanged.
🪄 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: f39df46d-ff2d-4d85-b5d4-2f0b0c648b6d

📥 Commits

Reviewing files that changed from the base of the PR and between fdd70bb and 9d84fe3.

📒 Files selected for processing (37)
  • .github/workflows/build.yaml
  • ci/run_cudf_polars_polars_tests.sh
  • ci/run_cudf_polars_pytests.sh
  • ci/test_wheel_cudf_polars.sh
  • ci/timeout_with_stack.py
  • conda/environments/all_cuda-129_arch-aarch64.yaml
  • conda/environments/all_cuda-129_arch-x86_64.yaml
  • conda/environments/all_cuda-133_arch-aarch64.yaml
  • conda/environments/all_cuda-133_arch-x86_64.yaml
  • cpp/include/cudf/io/experimental/variant.hpp
  • cpp/src/io/parquet/experimental/variant_extract.cu
  • cpp/src/io/parquet/experimental/variant_path.cpp
  • cpp/src/io/parquet/experimental/variant_path.hpp
  • cpp/tests/io/experimental/variant_extract_test.cpp
  • dependencies.yaml
  • java/ci/README.md
  • java/ci/argparse.sh
  • java/ci/assemble_maven_repo.sh
  • java/ci/build_cudf_java_jar.sh
  • java/ci/build_cudf_java_jar_in_container.sh
  • java/ci/build_static_libcudf.sh
  • java/ci/build_static_libcudf_in_container.sh
  • java/ci/test_java_build_local.sh
  • java/pom.xml
  • java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java
  • python/cudf/cudf/core/column/numerical.py
  • python/cudf/cudf/pandas/_wrappers/pandas.py
  • python/cudf/cudf/pandas/fast_slow_proxy.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
  • python/cudf/cudf/tests/series/methods/test_astype.py
  • python/cudf/cudf_pandas_tests/test_cudf_pandas.py
  • python/cudf/cudf_pandas_tests/test_fast_slow_proxy.py
  • python/cudf_polars/pyproject.toml
  • python/cudf_polars/tests/conftest.py
  • python/cudf_polars/tests/expressions/test_rolling.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_sort.py
💤 Files with no reviewable changes (4)
  • python/cudf_polars/tests/conftest.py
  • python/cudf_polars/tests/streaming/test_sort.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/expressions/test_rolling.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • cpp/src/io/parquet/experimental/variant_path.hpp
  • java/src/test/java/ai/rapids/cudf/VariantUtilsTest.java
  • cpp/include/cudf/io/experimental/variant.hpp
  • cpp/src/io/parquet/experimental/variant_extract.cu
  • cpp/tests/io/experimental/variant_extract_test.cpp
  • cpp/src/io/parquet/experimental/variant_path.cpp
🛑 Comments failed to post (2)
java/ci/build_static_libcudf_in_container.sh (1)

19-19: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add set -o pipefail to both in-container scripts to stop masking rapids-dependency-file-generator failures.

Both scripts pipe rapids-dependency-file-generator ... | tee "${ENV_YAML_DIR}/env.yaml" under set -e without pipefail. If the generator fails, tee's own success makes the pipeline's exit status 0, so the script continues with a possibly empty/malformed env.yaml straight into rapids-mamba-retry env create, turning a clear upstream failure into a confusing downstream one.

  • java/ci/build_static_libcudf_in_container.sh#L19: change set -e to set -eo pipefail (or add a separate set -o pipefail line) before the rapids-dependency-file-generator | tee call at Line 48-51.
  • java/ci/build_cudf_java_jar_in_container.sh#L22: apply the same set -eo pipefail change before the equivalent rapids-dependency-file-generator | tee call at Line 56-59.
🔧 Proposed fix (apply to both files)
-set -e
+set -eo pipefail
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

set -eo pipefail
📍 Affects 2 files
  • java/ci/build_static_libcudf_in_container.sh#L19-L19 (this comment)
  • java/ci/build_cudf_java_jar_in_container.sh#L22-L22
🤖 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 `@java/ci/build_static_libcudf_in_container.sh` at line 19, Enable pipefail in
both scripts before their rapids-dependency-file-generator pipelines: update
java/ci/build_static_libcudf_in_container.sh lines 19-19 and
java/ci/build_cudf_java_jar_in_container.sh lines 22-22 to use set -eo pipefail
or an equivalent separate setting. Ensure generator failures propagate instead
of being masked by tee.
java/ci/build_static_libcudf.sh (1)

103-104: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Guard against reusing a stale --output-dir.

Unlike build_cudf_java_jar.sh's CLASSIFIER_OUT check, this script silently reuses whatever already exists at --output-dir. A leftover install tree from a prior/failed run or a different CUDA version could mix with the new build and get linked against downstream in build_cudf_java_jar_in_container.sh, producing a subtly wrong libcudfjni.so.

🛡️ Proposed fix
 mkdir -p "${OUTPUT_DIR}"
+if [[ -n "$(ls -A "${OUTPUT_DIR}" 2>/dev/null)" ]]; then
+  echo "Error: --output-dir '${OUTPUT_DIR}' must be empty or nonexistent" >&2
+  exit 1
+fi
 OUTPUT_DIR="$(cd "${OUTPUT_DIR}" && pwd)"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

mkdir -p "${OUTPUT_DIR}"
if [[ -n "$(ls -A "${OUTPUT_DIR}" 2>/dev/null)" ]]; then
  echo "Error: --output-dir '${OUTPUT_DIR}' must be empty or nonexistent" >&2
  exit 1
fi
OUTPUT_DIR="$(cd "${OUTPUT_DIR}" && pwd)"
🤖 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 `@java/ci/build_static_libcudf.sh` around lines 103 - 104, Update the
output-directory setup in build_static_libcudf.sh to reject a pre-existing
non-empty --output-dir before creating or resolving it, matching the
stale-output protection used by CLASSIFIER_OUT in build_cudf_java_jar.sh.
Preserve support for a new or empty directory and only continue with the build
after validating it is safe to reuse.

@PointKernel PointKernel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small comments but not blocking.

Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated
Comment thread cpp/src/io/parquet/experimental/variant_extract.cu Outdated

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

Some questions, now that you've got the right Simon...

auto const offsets_start = pos;
auto const offsets_bytes = (static_cast<uint64_t>(num_entries.value()) + 1) * offset_size;
if (offsets_bytes > static_cast<uint64_t>(meta_len - offsets_start)) {
if (cuda::std::cmp_greater(offsets_bytes, meta_len - offsets_start)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit of using cuda::std::cmp_greater here? Both values are surely unsigned.

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.

offsets_bytes is unsigned, and meta_len - offsets_start is signed.

It's a convention in cudf since C++20 adoption to prefer std::cmp_xyz to casts for mixed type comparison.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. So it is. Understood.

// Consume the maximal run of decimal digits.
std::size_t n = 1;
while (n < tail.size() && tail[n] >= '0' && tail[n] <= '9') {
++n;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to tolerate white-space inside the [] here? This would no-op if the first thing after the [ was a space, and std::from_chars does not tolerate white-space anyway, not that it would get that far.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would fail with the same error state either way. I just just wondering if it SHOULD tolerate white-space, if (like I asked elsewhere) whatever is upstream providing these expressions cannot be trusted not to have inserted any.

@nartal1 nartal1 Jul 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Apache Spark, Spark’s Variant path parser disables whitespace skipping and only accepts digits between the brackets. So $ [0] and $ [01] are valid, but $ [ 0] and $[0 ] are invalid.

scala> spark.sql("""
     |   SELECT try_variant_get(
     |     parse_json('[10, 20]'),
     |     '$[ 0]',
     |     'int'
     |   )
     | """).show(false)
org.apache.spark.SparkRuntimeException: [INVALID_VARIANT_GET_PATH] The path `$[ 0]` is not a valid variant extraction path in ``try_variant_get``.

if (variant_basic_type(value_metadata) != basic_type::array) { return {}; }

int const value_header = variant_value_header(value_metadata);
[[maybe_unused]] auto const [offset_size, _, num_elements_size] =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the [[maybe_unused]] is just to tolerate and discard the _?

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.

That's right. Using _ doesn't mean anything to the compiler (yet); it's just there to make it clear to the reader which one isn't used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and avoid a compile warning, presumably.

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.

I think there wasn't a warning in this case. This one is for humans only :)


size_type const offsets_start = position;
auto const offsets_bytes = (static_cast<uint64_t>(num_elements) + 1) * offset_size;
if (cuda::std::cmp_greater(offsets_bytes, value_size - offsets_start)) { return {}; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another one of these. I'm guessing this is just an agreed standard?

"$.a[*]",
"$.a[-1]",
"$.a[+1]",
"$.a[ 1]",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this answers my earlier question. But what is upstream of this that guarantees that there won't be any white-space?

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.

I haven't gotten the requirement to allow white-space in indices, so I'd prefer to keep things simple until we have to support this.
@nartal1 can confirm is this is needed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spark throws error if there are whitespaces within array indices. #22895 (comment)

() -> VariantUtils.getVariantFieldValue(variant, "$.x["));
assertThrows(CudfException.class,
() -> VariantUtils.extractVariantField(variant, "$.x[0]", DType.INT32));
() -> VariantUtils.extractVariantField(variant, "$.x[", DType.INT32));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the assumption that the C++ tests cover all the bases, and this is just a smoke test?

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.

I think that's the intent, but I'm not sure. @nartal1 ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. Maybe we can add a smoke test for $[0] but that can be done in the later PR when we add the JNI for this.

@vuule

vuule commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

For 1,048,576 rows, accessing the last element/field:

Entries Array indexing Named field Named / Array
1 0.335 ms 0.353 ms 1.05×
8 0.355 ms 0.466 ms 1.31×
32 0.430 ms 1.296 ms 3.01×
64 0.462 ms 2.719 ms 5.89×

So, as expected, accessing indexed arrays scales better because we have direct access to the elements.

@simoneves
simoneves self-requested a review July 22, 2026 03:34

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

Questions answered. LGTM.

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

LGTM!

@vuule

vuule commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f9c1c2d

@vuule vuule added the 5 - Ready to Merge Testing and reviews complete, ready to merge label Jul 22, 2026
@vuule

vuule commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit f6d1081 into rapidsai:release/26.08 Jul 23, 2026
260 of 262 checks passed
@vuule
vuule deleted the fea-variant-array-indexing branch July 23, 2026 00:27
@GregoryKimball GregoryKimball moved this from Burndown to Landed in libcudf Jul 23, 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 feature request New feature or request Java Affects Java cuDF API. libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change

Projects

Status: Landed

Development

Successfully merging this pull request may close these issues.

7 participants