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 2a7ff42a fix(arrow/array): isolate table columns from caller slices
(#1105)
2a7ff42a is described below
commit 2a7ff42a1f244d56d7503e57d0a507b95937813d
Author: Minh Vu <[email protected]>
AuthorDate: Fri Aug 7 20:43:33 2026 +0200
fix(arrow/array): isolate table columns from caller slices (#1105)
### Rationale for this change
NewTable stores the caller's column slice directly. Replacing an element
in that slice after construction can change the table and make its
retained references inconsistent.
### What changes are included in this PR?
Clone the slice before storing it. Column objects keep the same retain
and release behavior as before.
### Are these changes tested?
- `go test ./arrow/array`
### Are there any user-facing changes?
No API changes. This corrects the reported behavior while preserving the
existing ownership and compatibility contracts.
---
arrow/array/table.go | 3 ++-
arrow/array/table_test.go | 33 +++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/arrow/array/table.go b/arrow/array/table.go
index e1f1bb08..43848481 100644
--- a/arrow/array/table.go
+++ b/arrow/array/table.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math"
+ "slices"
"strings"
"sync/atomic"
@@ -102,7 +103,7 @@ type simpleTable struct {
func NewTable(schema *arrow.Schema, cols []arrow.Column, rows int64)
arrow.Table {
tbl := simpleTable{
rows: rows,
- cols: cols,
+ cols: slices.Clone(cols),
schema: schema,
}
tbl.refCount.Add(1)
diff --git a/arrow/array/table_test.go b/arrow/array/table_test.go
index f39b2426..00f8a3aa 100644
--- a/arrow/array/table_test.go
+++ b/arrow/array/table_test.go
@@ -724,6 +724,39 @@ func TestTable(t *testing.T) {
}
}
+func TestNewTableDoesNotAliasColumnSlice(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ field := arrow.Field{Name: "value", Type: arrow.PrimitiveTypes.Int32}
+ schema := arrow.NewSchema([]arrow.Field{field}, nil)
+
+ makeColumn := func(value int32) arrow.Column {
+ builder := array.NewInt32Builder(mem)
+ defer builder.Release()
+ builder.Append(value)
+ arr := builder.NewArray()
+ defer arr.Release()
+ return arrow.NewColumnFromArr(field, arr)
+ }
+
+ original := makeColumn(1)
+ replacement := makeColumn(2)
+ cols := []arrow.Column{original}
+
+ tbl := array.NewTable(schema, cols, -1)
+ defer tbl.Release()
+
+ cols[0] = replacement
+ got := tbl.Column(0).Data().Chunk(0).(*array.Int32).Value(0)
+ if got != 1 {
+ t.Fatalf("table column changed after caller slice mutation:
got=%d, want=1", got)
+ }
+
+ original.Release()
+ replacement.Release()
+}
+
func TestTableAddColumnWithEqualDataType(t *testing.T) {
columnType := arrow.ListOf(arrow.PrimitiveTypes.Int32)
chunk := arrow.NewChunked(columnType, nil)