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 4e37ccc4 fix(arrow): isolate schemas created by AddField (#1079)
4e37ccc4 is described below
commit 4e37ccc478da98c1a8256d3a484f6381cbf09e12
Author: Minh Vu <[email protected]>
AuthorDate: Wed Aug 5 20:21:09 2026 +0200
fix(arrow): isolate schemas created by AddField (#1079)
### Rationale for this change
Schema.AddField used append directly on the parent field slice when
inserting at the end. If the parent had spare capacity, two schemas derived
from it shared the same appended slot. Creating the second sibling then
silently replaced the first sibling's field while its name index still
described the original field.
For example, a derived schema created with field "first" can later report
field "second" after another AddField call on the same parent.
### What changes are included in this PR?
Always allocate the result field slice before inserting the new field.
Parent and sibling schemas no longer share writable backing storage. Field
ordering, metadata, endianness, and index validation are unchanged.
### Are these changes tested?
Yes. The regression test builds a parent with spare field capacity, derives
two sibling schemas, and verifies that creating the second sibling does not
mutate the first.
- go test ./arrow
- go test -race ./arrow -run
'^TestSchemaAddFieldDoesNotAliasParentStorage$' -count=1
### Are there any user-facing changes?
No API changes. Schemas returned by AddField now remain immutable when
their parent is reused.
---
arrow/schema.go | 13 ++++---------
arrow/schema_test.go | 23 +++++++++++++++++++++++
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arrow/schema.go b/arrow/schema.go
index cb99adb5..78eac9cd 100644
--- a/arrow/schema.go
+++ b/arrow/schema.go
@@ -268,15 +268,10 @@ func (s *Schema) AddField(i int, field Field) (*Schema,
error) {
return nil, fmt.Errorf("arrow: invalid field index %d", i)
}
- var fields []Field
- if i == len(s.fields) {
- fields = append(s.fields, field)
- } else {
- fields = make([]Field, len(s.fields)+1)
- copy(fields[:i], s.fields[:i])
- fields[i] = field
- copy(fields[i+1:], s.fields[i:])
- }
+ fields := make([]Field, len(s.fields)+1)
+ copy(fields[:i], s.fields[:i])
+ fields[i] = field
+ copy(fields[i+1:], s.fields[i:])
return newSchema(fields, &s.meta, s.endianness), nil
}
diff --git a/arrow/schema_test.go b/arrow/schema_test.go
index 0069e69d..b1e213b9 100644
--- a/arrow/schema_test.go
+++ b/arrow/schema_test.go
@@ -373,6 +373,29 @@ func TestSchemaAddField(t *testing.T) {
}
}
+func TestSchemaAddFieldDoesNotAliasParentStorage(t *testing.T) {
+ field := func(name string) Field {
+ return Field{Name: name, Type: PrimitiveTypes.Int32}
+ }
+
+ parent := NewSchema([]Field{field("f1")}, nil)
+ fields := make([]Field, len(parent.fields), len(parent.fields)+1)
+ copy(fields, parent.fields)
+ parent.fields = fields
+
+ first, err := parent.AddField(parent.NumFields(), field("first"))
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if _, err := parent.AddField(parent.NumFields(), field("second")); err
!= nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ if got, want := first.Field(1).Name, "first"; got != want {
+ t.Fatalf("derived schema field changed: got=%q, want=%q", got,
want)
+ }
+}
+
func TestSchemaEqual(t *testing.T) {
fields := []Field{
{Name: "f1", Type: PrimitiveTypes.Int32},