This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git
The following commit(s) were added to refs/heads/main by this push:
new cf49e3e5 fix(ipc): preserve map child field metadata (#809)
cf49e3e5 is described below
commit cf49e3e5437dc104d76924e6f43da982e01c29f3
Author: Dima Kuznetsov <[email protected]>
AuthorDate: Tue May 12 19:16:32 2026 +0300
fix(ipc): preserve map child field metadata (#809)
### Rationale for this change
IPC deserialization of `MapType` reconstructed map key/value children
from only their data types, which dropped field-level metadata on the
key and value fields. This could silently lose metadata such as field
IDs after an IPC schema round-trip.
### What changes are included in this PR?
The IPC metadata reader now reconstructs `MapType` using
`arrow.MapOfFields(...)` with the already-deserialized key and value
fields, preserving their metadata and nullability.
A regression test was added to verify that map key/value field metadata,
item nullability, and `KeysSorted` survive an IPC schema round-trip.
### Are these changes tested?
Yes - unit test included.
### Are there any user-facing changes?
Yes - IPC schema deserialization now preserves field-level metadata on
MapType key and value children.
---
arrow/ipc/metadata.go | 3 +--
arrow/ipc/metadata_test.go | 41 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/arrow/ipc/metadata.go b/arrow/ipc/metadata.go
index 91f37af7..c139f95e 100644
--- a/arrow/ipc/metadata.go
+++ b/arrow/ipc/metadata.go
@@ -843,8 +843,7 @@ func concreteTypeFromFB(typ flatbuf.Type, data
flatbuffers.Table, children []arr
var dt flatbuf.Map
dt.Init(data.Bytes, data.Pos)
- ret := arrow.MapOf(pairType.Field(0).Type,
pairType.Field(1).Type)
- ret.SetItemNullable(pairType.Field(1).Nullable)
+ ret := arrow.MapOfFields(pairType.Field(0), pairType.Field(1))
ret.KeysSorted = dt.KeysSorted()
return ret, nil
diff --git a/arrow/ipc/metadata_test.go b/arrow/ipc/metadata_test.go
index 64898890..ac8820dc 100644
--- a/arrow/ipc/metadata_test.go
+++ b/arrow/ipc/metadata_test.go
@@ -86,6 +86,47 @@ func TestRWSchema(t *testing.T) {
}
}
+func TestMapFieldMetadataRoundTrip(t *testing.T) {
+ keyMeta := arrow.NewMetadata([]string{"my.key.id"}, []string{"100"})
+ itemMeta := arrow.NewMetadata([]string{"my.item.id"}, []string{"200"})
+
+ mapType := arrow.MapOfFields(
+ arrow.Field{Name: "key", Type: arrow.BinaryTypes.String,
Metadata: keyMeta},
+ arrow.Field{Name: "value", Type: arrow.PrimitiveTypes.Int32,
Nullable: true, Metadata: itemMeta},
+ )
+ mapType.KeysSorted = true
+ schema := arrow.NewSchema([]arrow.Field{
+ {Name: "m", Type: mapType, Nullable: true},
+ }, nil)
+
+ var buf bytes.Buffer
+ w := NewWriter(&buf, WithSchema(schema),
WithAllocator(memory.DefaultAllocator))
+ if err := w.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := NewReader(bytes.NewReader(buf.Bytes()),
WithAllocator(memory.DefaultAllocator))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Release()
+
+ roundTripped := r.Schema()
+ if !roundTripped.Equal(schema) {
+ t.Fatalf("schema mismatch after IPC round-trip:\ngot:
%s\nwant: %s", roundTripped, schema)
+ }
+
+ rtMap := roundTripped.Field(0).Type.(*arrow.MapType)
+ if got, ok := rtMap.KeyField().Metadata.GetValue("my.key.id"); !ok ||
got != "100" {
+ t.Fatalf("key field metadata lost after IPC round-trip: got
keys=%v", rtMap.KeyField().Metadata.Keys())
+ }
+ if got, ok := rtMap.ItemField().Metadata.GetValue("my.item.id"); !ok ||
got != "200" {
+ t.Fatalf("item field metadata lost after IPC round-trip: got
keys=%v", rtMap.ItemField().Metadata.Keys())
+ }
+ assert.True(t, rtMap.ItemField().Nullable)
+ assert.True(t, rtMap.KeysSorted)
+}
+
func TestRWFooter(t *testing.T) {
for _, tc := range []struct {
schema *arrow.Schema