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 a27b596c fix(arrow/array): avoid copying initialized extension 
refcounts (#1089)
a27b596c is described below

commit a27b596cc5067442294b9d38071b0576f9a285e9
Author: Minh Vu <[email protected]>
AuthorDate: Fri Aug 7 18:30:40 2026 +0200

    fix(arrow/array): avoid copying initialized extension refcounts (#1089)
    
    ### Rationale for this change
    
    The extension array constructor initializes the base reference count and
    then copies the complete value through reflection. That can copy an
    atomic after first use and triggers the copylocks vet warning.
    
    ### What changes are included in this PR?
    
    Allocate the concrete extension array first and initialize its embedded
    ExtensionArrayBase directly in the destination.
    
    ### Are these changes tested?
    
    - `go test ./arrow/array`
    - go vet ./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/extension.go      | 22 +++++++++++++---------
 arrow/array/extension_test.go | 19 +++++++++++++++++++
 2 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/arrow/array/extension.go b/arrow/array/extension.go
index e509b5e0..5a4d3c86 100644
--- a/arrow/array/extension.go
+++ b/arrow/array/extension.go
@@ -85,19 +85,23 @@ func NewExtensionArrayWithStorage(dt arrow.ExtensionType, 
storage arrow.Array) a
 // NewExtensionData expects a data with a datatype of arrow.ExtensionType and
 // underlying data built for the storage array.
 func NewExtensionData(data arrow.ArrayData) ExtensionArray {
-       base := ExtensionArrayBase{}
-       base.refCount.Add(1)
-       base.setData(data.(*Data))
+       if data.DataType().ID() != arrow.EXTENSION {
+               panic("arrow/array: must use extension type to construct an 
extension array")
+       }
+       dtype, ok := data.DataType().(arrow.ExtensionType)
+       if !ok {
+               panic("arrow/array: DataType for ExtensionArray must implement 
arrow.ExtensionType")
+       }
 
        // use the ExtensionType's ArrayType to construct the correctly typed 
object
        // to use as the ExtensionArray interface. reflect.New returns a 
pointer to
        // the newly created object.
-       arr := reflect.New(base.ExtensionType().ArrayType())
-       // set the embedded ExtensionArrayBase to the value we created above. 
We know
-       // that this field will exist because the interface requires embedding 
ExtensionArrayBase
-       // so we don't have to separately check, this will panic if called on 
an ArrayType
-       // that doesn't embed ExtensionArrayBase which is what we want.
-       arr.Elem().FieldByName("ExtensionArrayBase").Set(reflect.ValueOf(base))
+       arr := reflect.New(dtype.ArrayType())
+       // Initialize the embedded base in place so its atomic reference count 
is not
+       // copied after first use.
+       base := 
arr.Elem().FieldByName("ExtensionArrayBase").Addr().Interface().(*ExtensionArrayBase)
+       base.refCount.Add(1)
+       base.setData(data.(*Data))
        return arr.Interface().(ExtensionArray)
 }
 
diff --git a/arrow/array/extension_test.go b/arrow/array/extension_test.go
index b2eba8e0..d3875eda 100644
--- a/arrow/array/extension_test.go
+++ b/arrow/array/extension_test.go
@@ -23,6 +23,7 @@ import (
        "github.com/apache/arrow-go/v18/arrow/array"
        "github.com/apache/arrow-go/v18/arrow/memory"
        "github.com/apache/arrow-go/v18/internal/types"
+       "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
 )
 
@@ -81,6 +82,24 @@ func (e *ExtensionTypeTestSuite) TestParametricArrays() {
        e.True(array.RecordEqual(rb, rb))
 }
 
+func TestNewExtensionArrayWithStorageReleasesAllReferences(t *testing.T) {
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+
+       builder := array.NewInt32Builder(mem)
+       builder.Append(42)
+       storage := builder.NewInt32Array()
+       builder.Release()
+
+       ext := array.NewExtensionArrayWithStorage(types.NewParametric1Type(6), 
storage)
+       require.IsType(t, &types.Parametric1Array{}, ext)
+       require.Equal(t, 1, ext.Len())
+       require.Equal(t, "42", ext.ValueStr(0))
+
+       ext.Release()
+       storage.Release()
+       mem.AssertSize(t, 0)
+}
+
 func TestExtensionTypes(t *testing.T) {
        suite.Run(t, new(ExtensionTypeTestSuite))
 }

Reply via email to