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.