Skip to content

fix: preserve nested Arrow arrays on storage reads - #1142

Merged
andreatnvidia merged 5 commits into
mainfrom
codex/fix-arrow-storage-read-20261107
Nov 10, 2026
Merged

andreatnvidia merged 5 commits into
mainfrom
codex/fix-arrow-storage-read-20261107

Conversation

@lipikaramaswamy

@lipikaramaswamy lipikaramaswamy commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

📋 Summary

Prevent valid nested Arrow arrays from becoming invalid while loading JSON-annotated Parquet artifacts. pandas' ArrowDtype conversion performs an identity cast; with nested null children, PyArrow 24 can produce a malformed child array even though the original Parquet table validates successfully.

The synthetic regression reproduces this on pandas 2.3.3 / PyArrow 24.0.0: upstream main fails the variable-length and uniform nested-list cases; both pass with this fix.

🔗 Related Issue

Fixes #1143

🔄 Changes

  • Exclude nested columns before pandas conversion, then wrap the original arrays directly in ArrowExtensionArray. This avoids both the corrupting identity cast and the redundant Python-object materialization identified by Greptile.
  • Preserve Arrow-backed native columns, explicit JSON decoding, output column order, row indexes, and carried storage metadata. No schema, dependency, or public-signature changes; legacy unannotated reads are unchanged.
  • Add public Parquet-reader regressions for variable-length, uniform-length and empty nested lists with null children. Additional cases cover zero-row/all-nested tables, range/named/MultiIndex row indexes, and sliced/chunked arrays.
  • Related to, but independent of, feat: add dtype_backend to result loading #1128: that PR adds dtype-backend selection; this fixes corruption in the default Arrow-backed path.

🧪 Testing

  • Regression red/green: upstream reader fails 2 cases / passes the empty case; patched reader passes all cases (pandas 2.3.3 / PyArrow 24.0.0).
  • Revised patch: current-main config package 839 passed (pandas 3.0.5 / PyArrow 24.0.0).
  • Revised patch: storage tests 89 passed (pandas 2.3.3 / PyArrow 24.0.0).
  • Repository-supported Ruff lint/format scope (619 files), diff checks and normal commit hooks pass.
  • Earlier patch validation: 362 config utility tests passed on pandas 2.3.3 and 3.0.5; engine/interface tests recorded 4,246 passed / 11 skipped plus 35 sandbox loopback failures. Rerunning both affected files with localhost access gave 52 passed / 8 skipped, clearing all 35 failures.
  • Revised patch: full downstream Anonymizer docs: Update README.md #305 suite 2,017 passed / 2 skipped using the local DD config-source overlay against DD 0.10.0 (pandas 2.3.3 / PyArrow 24.0.0). This is local-overlay integration evidence, not a claim that unpinned docs: Update README.md #305 CI is fixed; downstream still needs a released or pinned DD fix.
  • Tests use source-only PYTHONPATH overlays; shared environments were not modified. No provider-backed E2E/model calls were run.

✅ Checklist

  • Conventional commit messages and DCO signoff.
  • Linked synthetic bug report Annotated Parquet reads can corrupt nested Arrow arrays with null children #1143; collaborator exemption does not require an external triage wait.
  • Three-pass self-review completed after addressing Greptile's performance concern; no remaining critical or warning findings in the supported storage path.
  • Architecture docs: N/A — restores the existing storage contract without changing it.

Description updated with AI assistance.

Avoid the pandas ArrowDtype identity cast that corrupts nested null children. Keep native columns Arrow-backed and preserve explicit JSON decoding and metadata.

Signed-off-by: Lipika Ramaswamy <lipika.ramaswamy@gmail.com>
@lipikaramaswamy
lipikaramaswamy requested a review from a team as a code owner November 9, 2026 00:57
@greptile-apps

greptile-apps Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were found.

Summary

Preserves nested Arrow arrays when reading JSON-annotated Parquet files.

  • Nested Arrow values stay valid when annotated Parquet files are read.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Arrow table] --> B[Read storage contract]
    B --> C[Convert non-nested columns to pandas]
    B --> D[Wrap original nested Arrow arrays]
    C --> E[Restore labels and indexes]
    D --> E
    E --> F[Decode declared JSON values]
    F --> G[Return DataFrame with storage metadata]
Loading

Reviews (5) · Last reviewed commit: "Merge branch 'main' into codex/fix-arrow..." · Reviewed by Greptile

Comment on lines +125 to +130
frame = table.to_pandas(
types_mapper=lambda dtype: None if lazy.pa.types.is_nested(dtype) else lazy.pd.ArrowDtype(dtype)
)
for field in table.schema:
if lazy.pa.types.is_nested(field.type) and field.name in frame.columns:
frame[field.name] = lazy.pd.Series(lazy.pd.arrays.ArrowExtensionArray(table[field.name]), index=frame.index)

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.

P2 Nested reads do extra work

decode_storage_table converts every nested column to Python objects in table.to_pandas(), then immediately discards those objects and wraps the original Arrow arrays. Large nested columns now need extra time and memory, including JSON columns that are decoded again below. Build the frame without converting nested columns, then insert their ArrowExtensionArray values.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/data-designer-config/src/data_designer/config/utils/storage_encoding.py
Line: 125-130

Comment:
**Nested reads do extra work**

`decode_storage_table` converts every nested column to Python objects in `table.to_pandas()`, then immediately discards those objects and wraps the original Arrow arrays. Large nested columns now need extra time and memory, including JSON columns that are decoded again below. Build the frame without converting nested columns, then insert their `ArrowExtensionArray` values.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

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.

Addressed in dd134a1. Nested columns are now excluded from the table sent to pandas conversion, then wrapped directly from the original Arrow arrays; there is no intermediate nested Python-object materialization. Added six edge cases covering zero-row/all-nested tables, RangeIndex/named/MultiIndex row indexes, interleaved column order, and sliced/chunked arrays. Revised patch validation: 839 DD config tests passed, 89 storage tests passed on pandas 2.3.3, and the full downstream Anonymizer suite passed (2,017 passed, 2 skipped) against the local DD source overlay. Normal commit hooks and repository lint/format scope pass. Bug report and synthetic reproducer: #1143. Self-review completed again after the change; PR CI remains separate from these local results.

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Fern preview: https://nvidia-preview-pr-1142.docs.buildwithfern.com/nemo/datadesigner

Fern previews include the docs-website version archive with PR changes synced into latest. Notebook tutorials are rendered without execution outputs in previews.

@svcnvidia-nemo-ci svcnvidia-nemo-ci 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.

No Critical or Warning findings.

I reviewed both changed files against the base and traced the annotated Parquet reader, fragment concatenation, artifact loading, and seed handoff. The decoder wraps the original nested arrays while keeping explicit JSON decoding authoritative. The regression tests cover buffer validity, values, Arrow dtypes, metadata, and column order.

This was a static review; I couldn't independently reproduce the reported test results. One delegated report was unavailable, so I inspected its public-reader, integration, and test-coverage topic directly.

Note: formal approval is not available for this request, so this review is posted as a comment only.

Review generated by codex. This was a full review. Request a full review with /review reset=true.

Signed-off-by: Lipika Ramaswamy <lipika.ramaswamy@gmail.com>

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

The nested-array fix looks good overall. I ran into two metadata edge cases where loading changes the returned columns: non-string labels can lose scalar columns, and tuple-valued indexes come back as data columns. I’d prefer to address those before merging; details are in the inline comments.

Map physical Arrow fields to pandas-restored labels before reinsertion. Restore nested persisted index fields as indexes rather than data columns, without casting the original nested arrays.

Add decoder and annotated-reader regressions for non-string column labels and tuple-valued indexes.

Signed-off-by: Lipika Ramaswamy <lipika.ramaswamy@gmail.com>
Filter stale field and index metadata before pandas conversion. Avoid redundant Arrow index dtype parsing while preserving nested buffers, logical labels, and all-nested row counts.

Cover repeated persisted reads, metadata projections, column labels, indexes, artifact stages, and failure preservation across 85 edge cases.

Signed-off-by: Lipika Ramaswamy <lipika.ramaswamy@gmail.com>
@andreatnvidia
andreatnvidia enabled auto-merge (squash) November 10, 2026 18:51
@andreatnvidia
andreatnvidia merged commit db02374 into main Nov 10, 2026
58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Annotated Parquet reads can corrupt nested Arrow arrays with null children

3 participants