Skip to content

feat(ipc): Read and write DictionaryEncoding.custom_metadata for dictionary values - #972

Draft
rustyconover wants to merge 2 commits into
apache:mainfrom
rustyconover:gh-49704-dictionary-value-metadata
Draft

rustyconover wants to merge 2 commits into
apache:mainfrom
rustyconover:gh-49704-dictionary-value-metadata

Conversation

@rustyconover

@rustyconover rustyconover commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Draft until the Arrow format vote. This implements the proposed Arrow format 1.6 change from apache/arrow#49704. The format change is apache/arrow#54959, and the companion drafts are apache/arrow#53994 (C++), apache/arrow-go#1381 (Go) and apache/arrow-rs#11622 (Rust).

DictionaryEncoding gains an optional custom_metadata that holds the metadata of the dictionary value type. It is the IPC counterpart of ArrowSchema.dictionary->metadata, while Field.custom_metadata corresponds to the parent ArrowSchema.metadata. Because nanoarrow works on ArrowSchema directly, the mapping is lossless. The member has the same semantics as Field.custom_metadata, applied to the dictionary values, so this PR writes and reads all keys of ArrowSchema.dictionary->metadata.

  • Regenerated header: both commits regenerate flatcc_generated.h with dev/update_fbs.sh (flatcc e3e44836).
    • The first commit regenerates from apache/arrow main as committed, so it only changes comments: upstream docstring fixes and the Tensor/SparseTensor deprecation notes.
    • The second commit regenerates from apache/arrow main plus the draft Schema.fbs change. Its header diff is only the new member.
  • Encoder: writes schema->dictionary->metadata (all keys) to DictionaryEncoding.custom_metadata, omitted when NULL or empty, and keeps writing schema->metadata to Field.custom_metadata. Previously the value metadata was silently dropped.
  • Decoder: keeps Field.custom_metadata on the parent schema, as before, and now sets schema->dictionary->metadata from DictionaryEncoding.custom_metadata.
    • Messages without the member decode exactly as before. There is no fallback for older files, per the spec.
    • This covers dictionaries nested in struct/list children and footer decoding.
  • Footer encoding: carries the member through footer.dictionaries. The dictionary file-writing support from feat: Don't re-emit unchanged dictionaries when writing streams; fix nested dictionary and file writing #928 is now on main.
  • Integration JSON: the reader and writer support the optional "metadata" of a field's "dictionary" object. TestingJSONComparison now compares dictionary value metadata at any depth, treating empty as absent.

Tests:

  • nanoarrow-only (run in CI):
    • encode/decode round trips that check the flatbuffer directly: extension and arbitrary keys, non-UTF-8 values, nesting in struct and list, alongside field metadata, empty metadata omitted;
    • a footer encode/decode round trip with the dictionary encodings filled in;
    • decoding hand-built messages with and without the member;
    • a writer/reader stream round trip;
    • JSON reader/writer/comparison tests.
  • Arrow C++ interop: with NANOARROW_BUILD_TESTS_WITH_ARROW, dictionary(int32, uuid) cases are enabled, including Arrow C++ reading nanoarrow-encoded schemas.

Archery: with the dictionary_extension case enabled for nanoarrow, C Data passes between C++ and nanoarrow in both directions. On main, nanoarrow→C++ fails because the value metadata is lost. Those Archery results predate the dictionary file-writing support merged in #928; the original #622 limitation is no longer the reason to hold this draft.

This PR's code and description were written by AI (Claude Code) and reviewed by a human maintainer and an AI reviewer.

🤖 Generated with Claude Code

Rebased onto current main on October 26. Combined dictionary-value metadata comparison with #993's recursive child-metadata comparison and retained both regression tests. Local macOS arm64 Debug validation with Arrow C++ 25.0.1, LZ4 and ZSTD: 100% tests passed out of 929; 175 cases skipped (including unavailable IPC fixture files and format support absent from released Arrow C++). Remains draft pending the Arrow format vote.

@codecov-commenter

codecov-commenter commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 13.63636% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.45%. Comparing base (08d23f6) to head (fda26a6).

Files with missing lines Patch % Lines
src/nanoarrow/ipc/encoder.c 9.09% 0 Missing and 10 partials ⚠️
src/nanoarrow/testing/testing.cc 20.00% 0 Missing and 8 partials ⚠️
src/nanoarrow/ipc/decoder.c 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #972      +/-   ##
==========================================
- Coverage   78.52%   78.45%   -0.08%     
==========================================
  Files         106      106              
  Lines       17195    17212      +17     
  Branches     2064     2083      +19     
==========================================
+ Hits        13502    13503       +1     
  Misses       2431     2431              
- Partials     1262     1278      +16     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rustyconover
rustyconover force-pushed the gh-49704-dictionary-value-metadata branch from 7a26ce5 to a3edfdd Compare October 16, 2026 22:14
paleolimbot pushed a commit that referenced this pull request Oct 26, 2026
…#993)

### Summary

`TestingJSONComparison::CompareFieldBase` was comparing metadata on the
parent field but not checking metadata on its children.

That meant two nested schemas could compare as equal even when their
child fields had different metadata.

This change makes the comparison recurse into child fields, including
dictionary children, and reuse the existing metadata comparison logic.

It still treats absent and empty metadata as equivalent, and the
existing order-insensitive metadata comparison behavior is preserved.

### Testing

Added a gtest covering metadata differences in list, map, and dictionary
children.

All 291 CTest tests pass.

This is separate from #972, which deals with dictionary value metadata.

Fixes #973.

Co-authored-by: JonSnow1807 <JonSnow1807@users.noreply.github.com>
Rusty Conover and others added 2 commits October 26, 2026 11:55
Regenerated with dev/update_fbs.sh (flatcc e3e44836) from apache/arrow
main, with whitespace cleaned by the pre-commit hooks. This picks up the
upstream .fbs changes since apache#854, which only change comments (e.g. the
docstring fixes from apache/arrow#50826 and the Tensor/SparseTensor
deprecation notes from apache/arrow#51102).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ionary values

Implements the draft Arrow format 1.6 change from apache/arrow#49704
(apache/arrow#53994): an optional custom_metadata on DictionaryEncoding
that holds the metadata of the dictionary value type. It is the IPC
counterpart of ArrowSchema.dictionary->metadata, and Field.custom_metadata
corresponds to the parent ArrowSchema's metadata.

- flatcc_generated.h is regenerated with dev/update_fbs.sh (flatcc
  e3e44836) from apache/arrow main plus the draft Schema.fbs change.
- The encoder writes schema->dictionary->metadata (all keys) to
  DictionaryEncoding.custom_metadata, omitted when it is NULL or empty,
  and keeps writing schema->metadata to Field.custom_metadata. Before,
  the value metadata was dropped.
- The decoder sets schema->dictionary->metadata from
  DictionaryEncoding.custom_metadata and keeps Field.custom_metadata on
  the parent schema, as before. This covers nested dictionaries and
  footer decoding. Footer encoding carries the member when the caller
  fills footer.dictionaries.
- The integration JSON reader and writer support the optional
  "metadata" of a field's "dictionary" object, and the JSON comparison
  compares the dictionary value metadata, including nested dictionaries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rustyconover
rustyconover force-pushed the gh-49704-dictionary-value-metadata branch from a3edfdd to fda26a6 Compare October 26, 2026 16:11

This branch has not been deployed

No deployments
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.

2 participants