Skip to content

Commit e0fd7ef

Browse files
authored
GH-35866: [Go] Provide a copy in arrow.NestedType.Fields() implementations (#35867)
### Rationale for this change Data types should be left immutable after they've been constructed. ### What changes are included in this PR? Provide a copy of fields in the following implementations: * `*arrow.StructType.Fields()` * `*arrow.unionType.Fields()` ### Are these changes tested? `arrow.TestFieldsImmutability` was added to ensure the behavior. ### Are there any user-facing changes? Now the fields for structs & unions will be immutable. * Closes: #35866 Authored-by: candiduslynx <candiduslynx@gmail.com> Signed-off-by: Matt Topol <zotthewizard@gmail.com>
1 parent c67fb39 commit e0fd7ef

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

go/arrow/datatype_nested.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import (
2727

2828
type NestedType interface {
2929
DataType
30+
31+
// Fields method provides a copy of NestedType fields
32+
// (so it can be safely mutated and will not result in updating the NestedType).
3033
Fields() []Field
3134
}
3235

@@ -288,7 +291,14 @@ func (t *StructType) String() string {
288291
return o.String()
289292
}
290293

291-
func (t *StructType) Fields() []Field { return t.fields }
294+
// Fields method provides a copy of StructType fields
295+
// (so it can be safely mutated and will not result in updating the StructType).
296+
func (t *StructType) Fields() []Field {
297+
fields := make([]Field, len(t.fields))
298+
copy(fields, t.fields)
299+
return fields
300+
}
301+
292302
func (t *StructType) Field(i int) Field { return t.fields[i] }
293303

294304
func (t *StructType) FieldByName(name string) (Field, bool) {
@@ -490,7 +500,14 @@ func (t *unionType) init(fields []Field, typeCodes []UnionTypeCode) {
490500
}
491501
}
492502

493-
func (t unionType) Fields() []Field { return t.children }
503+
// Fields method provides a copy of union type fields
504+
// (so it can be safely mutated and will not result in updating the union type).
505+
func (t unionType) Fields() []Field {
506+
fields := make([]Field, len(t.children))
507+
copy(fields, t.children)
508+
return fields
509+
}
510+
494511
func (t unionType) TypeCodes() []UnionTypeCode { return t.typeCodes }
495512
func (t unionType) ChildIDs() []int { return t.childIDs[:] }
496513

go/arrow/datatype_nested_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ package arrow
1919
import (
2020
"reflect"
2121
"testing"
22+
23+
"github.com/google/uuid"
24+
"github.com/stretchr/testify/assert"
2225
)
2326

2427
func TestListOf(t *testing.T) {
@@ -491,3 +494,50 @@ func TestMapOfWithMetadata(t *testing.T) {
491494
})
492495
}
493496
}
497+
498+
func TestFieldsImmutability(t *testing.T) {
499+
cases := []struct {
500+
dt NestedType
501+
expected []Field
502+
}{
503+
{
504+
dt: ListOfField(Field{Name: "name", Type: PrimitiveTypes.Int64}),
505+
expected: ListOfField(Field{Name: "name", Type: PrimitiveTypes.Int64}).Fields(),
506+
},
507+
{
508+
dt: LargeListOfField(Field{Name: "name", Type: PrimitiveTypes.Int64}),
509+
expected: LargeListOfField(Field{Name: "name", Type: PrimitiveTypes.Int64}).Fields(),
510+
},
511+
{
512+
dt: FixedSizeListOfField(1, Field{Name: "name", Type: PrimitiveTypes.Int64}),
513+
expected: FixedSizeListOfField(1, Field{Name: "name", Type: PrimitiveTypes.Int64}).Fields(),
514+
},
515+
{
516+
dt: MapOf(BinaryTypes.String, PrimitiveTypes.Int64),
517+
expected: MapOf(BinaryTypes.String, PrimitiveTypes.Int64).Fields(),
518+
},
519+
{
520+
dt: StructOf(Field{Name: "name", Type: PrimitiveTypes.Int64}),
521+
expected: StructOf(Field{Name: "name", Type: PrimitiveTypes.Int64}).Fields(),
522+
},
523+
{
524+
dt: RunEndEncodedOf(BinaryTypes.String, PrimitiveTypes.Int64),
525+
expected: RunEndEncodedOf(BinaryTypes.String, PrimitiveTypes.Int64).Fields(),
526+
},
527+
{
528+
dt: UnionOf(DenseMode, []Field{{Name: "name", Type: PrimitiveTypes.Int64}}, []UnionTypeCode{0}),
529+
expected: UnionOf(DenseMode, []Field{{Name: "name", Type: PrimitiveTypes.Int64}}, []UnionTypeCode{0}).Fields(),
530+
},
531+
}
532+
533+
for _, tc := range cases {
534+
t.Run(tc.dt.String(), func(t *testing.T) {
535+
fields := tc.dt.Fields()
536+
fields[0].Nullable = !fields[0].Nullable
537+
fields[0].Name = uuid.NewString()
538+
fields[0].Type = nil
539+
540+
assert.Equal(t, tc.expected, tc.dt.Fields())
541+
})
542+
}
543+
}

0 commit comments

Comments
 (0)