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/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 35d1bf471 refactor(name-mapping)!: make child lookup explicit and 
stable (#2337)
35d1bf471 is described below

commit 35d1bf471ce2c54242c10a09a5cc83c9f164e94a
Author: Minh Vu <[email protected]>
AuthorDate: Wed Oct 28 18:09:08 2026 +0100

    refactor(name-mapping)!: make child lookup explicit and stable (#2337)
    
    Return pointers to stored children and report missing names explicitly. 
Update name-mapping and Parquet callers.
    
    Signed-off-by: Minh Vu <[email protected]>
---
 name_mapping.go                 | 37 +++++++++++++++++++++++++++----------
 name_mapping_test.go            | 36 ++++++++++++++++++++++++++++++++++++
 table/internal/parquet_files.go | 14 +++++++-------
 website/src/releases.md         |  1 +
 4 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/name_mapping.go b/name_mapping.go
index d2beb6e0f..864e7153a 100644
--- a/name_mapping.go
+++ b/name_mapping.go
@@ -43,14 +43,21 @@ func (m *MappedField) ID() int {
        return *m.FieldID
 }
 
-func (m *MappedField) GetField(field string) *MappedField {
-       for _, f := range m.Fields {
-               if slices.Contains(f.Names, field) {
-                       return &f
+// FindFieldByName returns the first direct child with a matching name or 
alias.
+// The pointer refers to the child in m.Fields, not a copy. If there is no
+// matching child, FindFieldByName returns nil, false.
+func (m *MappedField) FindFieldByName(name string) (*MappedField, bool) {
+       if m == nil {
+               return nil, false
+       }
+
+       for i := range m.Fields {
+               if slices.Contains(m.Fields[i].Names, name) {
+                       return &m.Fields[i], true
                }
        }
 
-       return nil
+       return nil, false
 }
 
 func (m *MappedField) Len() int { return len(m.Fields) }
@@ -286,7 +293,9 @@ func (NameMappingAccessor) FieldPartner(partnerStruct 
*MappedField, _ int, field
                return nil
        }
 
-       return partnerStruct.GetField(fieldName)
+       field, _ := partnerStruct.FindFieldByName(fieldName)
+
+       return field
 }
 
 func (NameMappingAccessor) ListElementPartner(partnerList *MappedField) 
*MappedField {
@@ -294,7 +303,9 @@ func (NameMappingAccessor) ListElementPartner(partnerList 
*MappedField) *MappedF
                return nil
        }
 
-       return partnerList.GetField("element")
+       field, _ := partnerList.FindFieldByName("element")
+
+       return field
 }
 
 func (NameMappingAccessor) MapKeyPartner(partnerMap *MappedField) *MappedField 
{
@@ -302,7 +313,9 @@ func (NameMappingAccessor) MapKeyPartner(partnerMap 
*MappedField) *MappedField {
                return nil
        }
 
-       return partnerMap.GetField("key")
+       field, _ := partnerMap.FindFieldByName("key")
+
+       return field
 }
 
 func (NameMappingAccessor) MapValuePartner(partnerMap *MappedField) 
*MappedField {
@@ -310,7 +323,9 @@ func (NameMappingAccessor) MapValuePartner(partnerMap 
*MappedField) *MappedField
                return nil
        }
 
-       return partnerMap.GetField("value")
+       field, _ := partnerMap.FindFieldByName("value")
+
+       return field
 }
 
 type indexedNameMappingAccessor struct {
@@ -355,7 +370,9 @@ func (n indexedNameMappingAccessor) getField(partnerStruct 
*MappedField, fieldNa
                return fieldsByName[fieldName]
        }
 
-       return partnerStruct.GetField(fieldName)
+       field, _ := partnerStruct.FindFieldByName(fieldName)
+
+       return field
 }
 
 func (n indexedNameMappingAccessor) SchemaPartner(partner *MappedField) 
*MappedField {
diff --git a/name_mapping_test.go b/name_mapping_test.go
index f528c5354..fd6b79f40 100644
--- a/name_mapping_test.go
+++ b/name_mapping_test.go
@@ -117,6 +117,42 @@ func TestApplyNameMappingUsesAlias(t *testing.T) {
        assert.Equal(t, 1, result.Field(0).ID)
 }
 
+func TestMappedFieldFindFieldByName(t *testing.T) {
+       root := iceberg.MappedField{Fields: []iceberg.MappedField{
+               {
+                       FieldID: new(7), Names: []string{"old", "current"},
+                       Fields: []iceberg.MappedField{
+                               {FieldID: new(8), Names: []string{"nested"}},
+                       },
+               },
+               {FieldID: new(9), Names: []string{"other", "current"}},
+       }}
+
+       field, ok := root.FindFieldByName("current")
+       require.True(t, ok)
+       require.Same(t, &root.Fields[0], field)
+
+       field.Names[0] = "legacy"
+       assert.Equal(t, "legacy", root.Fields[0].Names[0])
+
+       alias, ok := root.FindFieldByName("legacy")
+       require.True(t, ok)
+       require.Same(t, field, alias)
+
+       nested, ok := field.FindFieldByName("nested")
+       require.True(t, ok)
+       require.Same(t, &root.Fields[0].Fields[0], nested)
+
+       missing, ok := root.FindFieldByName("absent")
+       assert.False(t, ok)
+       assert.Nil(t, missing)
+
+       var nilField *iceberg.MappedField
+       missing, ok = nilField.FindFieldByName("absent")
+       assert.False(t, ok)
+       assert.Nil(t, missing)
+}
+
 func TestNameMappingAccessorRemainsComparable(t *testing.T) {
        first := iceberg.NameMappingAccessor{}
        second := iceberg.NameMappingAccessor{}
diff --git a/table/internal/parquet_files.go b/table/internal/parquet_files.go
index 5a65d33b0..3abe24fc1 100644
--- a/table/internal/parquet_files.go
+++ b/table/internal/parquet_files.go
@@ -2872,7 +2872,7 @@ func visitParquetManifest[T any](manifest 
*pqarrow.SchemaManifest, visitor manif
        results := make([]T, len(manifest.Fields))
        for i, f := range manifest.Fields {
                if mapping != nil {
-                       fieldMap = mapping.GetField(f.Field.Name)
+                       fieldMap, _ = mapping.FindFieldByName(f.Field.Name)
                }
                res := visitManifestField(f, visitor, fieldMap)
                results[i] = visitor.Field(f, res, fieldMap)
@@ -2886,7 +2886,7 @@ func visitParquetManifestStruct[T any](field 
pqarrow.SchemaField, visitor manife
        var fieldMap *iceberg.MappedField
        for i, f := range field.Children {
                if mapping != nil {
-                       fieldMap = mapping.GetField(f.Field.Name)
+                       fieldMap, _ = mapping.FindFieldByName(f.Field.Name)
                }
                res := visitManifestField(f, visitor, fieldMap)
                results[i] = visitor.Field(f, res, fieldMap)
@@ -2899,7 +2899,7 @@ func visitManifestList[T any](field pqarrow.SchemaField, 
visitor manifestVisitor
        elemField := field.Children[0]
        var elemMapping *iceberg.MappedField
        if mapping != nil {
-               elemMapping = mapping.GetField("element")
+               elemMapping, _ = mapping.FindFieldByName("element")
        }
        res := visitManifestField(elemField, visitor, elemMapping)
 
@@ -2911,8 +2911,8 @@ func visitManifestMap[T any](field pqarrow.SchemaField, 
visitor manifestVisitor[
        keyField, valField := kvfield.Children[0], kvfield.Children[1]
        var keyMapping, valMapping *iceberg.MappedField
        if mapping != nil {
-               keyMapping = mapping.GetField("key")
-               valMapping = mapping.GetField("value")
+               keyMapping, _ = mapping.FindFieldByName("key")
+               valMapping, _ = mapping.FindFieldByName("value")
        }
 
        return visitor.Map(field, visitManifestField(keyField, visitor, 
keyMapping), visitManifestField(valField, visitor, valMapping), mapping)
@@ -3077,7 +3077,7 @@ func (p *pruneParquetSchema) Field(field 
pqarrow.SchemaField, result arrow.Field
 func (p *pruneParquetSchema) List(field pqarrow.SchemaField, elemResult 
arrow.Field, mapping *iceberg.MappedField) arrow.Field {
        var elemMapping *iceberg.MappedField
        if mapping != nil {
-               elemMapping = mapping.GetField("element")
+               elemMapping, _ = mapping.FindFieldByName("element")
        }
 
        _, ok := p.selected[p.fieldID(*field.Children[0].Field, elemMapping)]
@@ -3117,7 +3117,7 @@ func (p *pruneParquetSchema) List(field 
pqarrow.SchemaField, elemResult arrow.Fi
 func (p *pruneParquetSchema) Map(field pqarrow.SchemaField, keyResult, 
valResult arrow.Field, mapping *iceberg.MappedField) arrow.Field {
        var valMapping *iceberg.MappedField
        if mapping != nil {
-               valMapping = mapping.GetField("value")
+               valMapping, _ = mapping.FindFieldByName("value")
        }
 
        _, ok := p.selected[p.fieldID(*field.Children[0].Children[1].Field, 
valMapping)]
diff --git a/website/src/releases.md b/website/src/releases.md
index 2cdbdfbff..80cf90d01 100644
--- a/website/src/releases.md
+++ b/website/src/releases.md
@@ -43,6 +43,7 @@ Per-release notes (highlights, breaking changes, 
contributors) are published on
 ### Unreleased compatibility notes
 
 - **KMS factory registration now takes a context.** Change 
`encryption.KMSFactory` implementations to accept `(context.Context, 
map[string]string)` instead of only properties, and call 
`encryption.LoadKeyManagementClient(ctx, props)` instead of 
`LoadKeyManagementClient(props)`. The existing global `RegisterKMS`, 
`UnregisterKMS`, and `GetRegisteredKMSNames` functions still use the default 
registry; `RegisterKMS` still panics for invalid registrations. Use 
`encryption.MustRegisterKMS` for  [...]
+- **Name-mapping child lookup renamed.** Replace `MappedField.GetField(name)` 
with `MappedField.FindFieldByName(name)`, which returns `(*MappedField, bool)`. 
The new method returns a pointer to the actual child field rather than a copy, 
and reports a missing name with `false`.
 - **Data file block size setter removed.** 
`iceberg.DataFileBuilder.BlockSizeInBytes` is removed. New v1 manifests use the 
Avro schema's 64 MiB default when the field is unset. V2/v3 omit this 
deprecated field, and legacy v1 values remain readable.
 - **View creation APIs take a named SQL representation.** 
`view.NewVersionFromSQL`, `view.CreateView`, `view.CreateViewWithIOProperties`, 
and `sql.Catalog.CreateView` now accept a `view.Representation{SQL: sql, 
Dialect: dialect}` instead of positional SQL/dialect strings. The dialect is 
required; Hive preserves the selected representation's dialect.
 

Reply via email to