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 afb7ad0e fix(arrow/scalar): use wide offsets for large binary scalars 
(#1108)
afb7ad0e is described below

commit afb7ad0e370d0ba6fdbd004115ee5f874e04ad5e
Author: Minh Vu <[email protected]>
AuthorDate: Mon Aug 10 18:52:22 2026 +0200

    fix(arrow/scalar): use wide offsets for large binary scalars (#1108)
    
    ### Rationale for this change
    
    MakeArrayFromScalar currently uses 32-bit offsets for every binary-like
    scalar. LargeBinary and LargeString require 64-bit offsets, so their
    arrays can have the wrong physical layout.
    
    ### What changes are included in this PR?
    
    Select the offset width from the scalar type and test ordinary and large
    binary values, including validation and buffer size.
    
    ### Are these changes tested?
    
    - `go test ./arrow/scalar`
    
    ### Are there any user-facing changes?
    
    No API changes. This corrects the reported behavior while preserving the
    existing ownership and compatibility contracts.
---
 arrow/scalar/scalar.go      | 52 +++++++++--------------------
 arrow/scalar/scalar_test.go | 79 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 37 deletions(-)

diff --git a/arrow/scalar/scalar.go b/arrow/scalar/scalar.go
index 8cb31764..535eb367 100644
--- a/arrow/scalar/scalar.go
+++ b/arrow/scalar/scalar.go
@@ -756,37 +756,7 @@ func GetScalar(arr arrow.Array, idx int) (Scalar, error) {
 //
 // Deprecated: Use array.MakeArrayOfNull
 func MakeArrayOfNull(dt arrow.DataType, length int, mem memory.Allocator) 
arrow.Array {
-       var (
-               buffers  = []*memory.Buffer{nil}
-               children []arrow.ArrayData
-       )
-
-       buffers[0] = memory.NewResizableBuffer(mem)
-       buffers[0].Resize(int(bitutil.BytesForBits(int64(length))))
-       defer buffers[0].Release()
-
-       switch t := dt.(type) {
-       case arrow.NestedType:
-               fieldList := t.Fields()
-               children = make([]arrow.ArrayData, len(fieldList))
-               for i, f := range fieldList {
-                       arr := MakeArrayOfNull(f.Type, length, mem)
-                       defer arr.Release()
-                       children[i] = arr.Data()
-               }
-       case arrow.FixedWidthDataType:
-               buffers = append(buffers, memory.NewResizableBuffer(mem))
-               
buffers[1].Resize(int(bitutil.BytesForBits(int64(t.BitWidth()))) * length)
-               defer buffers[1].Release()
-       case arrow.BinaryDataType:
-               buffers = append(buffers, memory.NewResizableBuffer(mem), nil)
-               buffers[1].Resize(arrow.Int32Traits.BytesRequired(length + 1))
-               defer buffers[1].Release()
-       }
-
-       data := array.NewData(dt, length, buffers, children, length, 0)
-       defer data.Release()
-       return array.MakeFromData(data)
+       return array.MakeArrayOfNull(mem, dt, length)
 }
 
 // MakeArrayFromScalar returns an array filled with the scalar value repeated 
length times.
@@ -800,12 +770,20 @@ func MakeArrayFromScalar(sc Scalar, length int, mem 
memory.Allocator) (arrow.Arr
                return MakeArrayOfNull(sc.DataType(), length, mem), nil
        }
 
-       createOffsets := func(valLength int32) *memory.Buffer {
+       createOffsets := func(valLength int64) *memory.Buffer {
                buffer := memory.NewResizableBuffer(mem)
-               buffer.Resize(arrow.Int32Traits.BytesRequired(length + 1))
+               offsetTraits := 
sc.DataType().(arrow.OffsetsDataType).OffsetTypeTraits()
+               buffer.Resize(offsetTraits.BytesRequired(length + 1))
+               if offsetTraits.BytesRequired(1) == arrow.Int64SizeBytes {
+                       out := arrow.Int64Traits.CastFromBytes(buffer.Bytes())
+                       for i, offset := 0, int64(0); i < length+1; i, offset = 
i+1, offset+valLength {
+                               out[i] = offset
+                       }
+                       return buffer
+               }
 
                out := arrow.Int32Traits.CastFromBytes(buffer.Bytes())
-               for i, offset := 0, int32(0); i < length+1; i, offset = i+1, 
offset+valLength {
+               for i, offset := 0, int32(0); i < length+1; i, offset = i+1, 
offset+int32(valLength) {
                        out[i] = offset
                }
                return buffer
@@ -848,7 +826,7 @@ func MakeArrayFromScalar(sc Scalar, length int, mem 
memory.Allocator) (arrow.Arr
                }
 
                valuesBuf := createBuffer(s.Data())
-               offsetsBuf := createOffsets(int32(len(s.Data())))
+               offsetsBuf := createOffsets(int64(len(s.Data())))
                data := array.NewData(sc.DataType(), length, 
[]*memory.Buffer{nil, offsetsBuf, valuesBuf}, nil, 0, 0)
                defer func() {
                        valuesBuf.Release()
@@ -880,7 +858,7 @@ func MakeArrayFromScalar(sc Scalar, length int, mem 
memory.Allocator) (arrow.Arr
                }
                defer valueArray.Release()
 
-               offsetsBuf := createOffsets(int32(s.Value.Len()))
+               offsetsBuf := createOffsets(int64(s.Value.Len()))
                defer offsetsBuf.Release()
                data := array.NewData(s.DataType(), length, 
[]*memory.Buffer{nil, offsetsBuf}, []arrow.ArrayData{valueArray.Data()}, 0, 0)
                defer data.Release()
@@ -935,7 +913,7 @@ func MakeArrayFromScalar(sc Scalar, length int, mem 
memory.Allocator) (arrow.Arr
                }
                defer valueArr.Release()
 
-               offsetsBuf := createOffsets(int32(structArr.Len()))
+               offsetsBuf := createOffsets(int64(structArr.Len()))
                outStructArr := array.NewData(structArr.DataType(), 
keyArr.Len(), []*memory.Buffer{nil}, []arrow.ArrayData{keyArr.Data(), 
valueArr.Data()}, 0, 0)
                data := array.NewData(s.DataType(), length, 
[]*memory.Buffer{nil, offsetsBuf}, []arrow.ArrayData{outStructArr}, 0, 0)
                defer func() {
diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go
index 3cec8f97..1627aef9 100644
--- a/arrow/scalar/scalar_test.go
+++ b/arrow/scalar/scalar_test.go
@@ -1253,6 +1253,85 @@ func TestMakeArrayFromScalar(t *testing.T) {
        }
 }
 
+func TestMakeArrayFromScalarUsesCorrectBinaryOffsetWidth(t *testing.T) {
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       buf := memory.NewBufferBytes([]byte("abc"))
+       defer buf.Release()
+
+       tests := []struct {
+               name    string
+               sc      scalar.Scalar
+               bytes   int
+               offsets []int64
+       }{
+               {name: "binary", sc: scalar.NewBinaryScalar(buf, 
arrow.BinaryTypes.Binary), bytes: 4, offsets: []int64{0, 3, 6, 9}},
+               {name: "string", sc: scalar.NewStringScalar("abc"), bytes: 4, 
offsets: []int64{0, 3, 6, 9}},
+               {name: "large binary", sc: scalar.NewLargeBinaryScalar(buf), 
bytes: 8, offsets: []int64{0, 3, 6, 9}},
+               {name: "large string", sc: scalar.NewLargeStringScalar("abc"), 
bytes: 8, offsets: []int64{0, 3, 6, 9}},
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.name, func(t *testing.T) {
+                       if releasable, ok := tc.sc.(scalar.Releasable); ok {
+                               defer releasable.Release()
+                       }
+
+                       arr, err := scalar.MakeArrayFromScalar(tc.sc, 3, mem)
+                       require.NoError(t, err)
+                       defer arr.Release()
+                       require.NoError(t, array.ValidateFull(arr))
+                       require.Len(t, arr.Data().Buffers()[1].Bytes(), 
4*tc.bytes)
+
+                       switch offsets := arr.(type) {
+                       case *array.Binary:
+                               assert.Equal(t, tc.offsets, 
slicesToInt64(offsets.ValueOffsets()))
+                       case *array.String:
+                               assert.Equal(t, tc.offsets, 
slicesToInt64(offsets.ValueOffsets()))
+                       case *array.LargeBinary:
+                               assert.Equal(t, tc.offsets, 
offsets.ValueOffsets())
+                       case *array.LargeString:
+                               assert.Equal(t, tc.offsets, 
offsets.ValueOffsets())
+                       default:
+                               t.Fatalf("unexpected array type %T", arr)
+                       }
+               })
+       }
+}
+
+func slicesToInt64(values []int32) []int64 {
+       out := make([]int64, len(values))
+       for i, value := range values {
+               out[i] = int64(value)
+       }
+       return out
+}
+
+func TestMakeArrayFromScalarUsesCorrectBinaryOffsetWidthForNulls(t *testing.T) 
{
+       mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+       defer mem.AssertSize(t, 0)
+
+       const length = 3
+       for _, dt := range []arrow.DataType{arrow.BinaryTypes.LargeBinary, 
arrow.BinaryTypes.LargeString} {
+               t.Run(dt.Name(), func(t *testing.T) {
+                       sc := scalar.MakeNullScalar(dt)
+                       if releasable, ok := sc.(scalar.Releasable); ok {
+                               defer releasable.Release()
+                       }
+
+                       arr, err := scalar.MakeArrayFromScalar(sc, length, mem)
+                       require.NoError(t, err)
+                       defer arr.Release()
+
+                       require.NoError(t, array.ValidateFull(arr))
+                       assert.Equal(t, length, arr.Len())
+                       assert.Equal(t, length, arr.NullN())
+                       assert.Equal(t, 
arrow.Int64Traits.BytesRequired(length+1), arr.Data().Buffers()[1].Len())
+               })
+       }
+}
+
 func TestMakeArrayFromScalarRejectsNegativeLength(t *testing.T) {
        mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
        defer mem.AssertSize(t, 0)

Reply via email to