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 24148019 fix(array): dictionary null index zeroing (#871)
24148019 is described below

commit 241480198dd8105596ee3627fc9dfcf6c3308401
Author: Minh Vu <[email protected]>
AuthorDate: Wed Jul 1 00:44:35 2026 +0200

    fix(array): dictionary null index zeroing (#871)
    
    ### What changed
    
    Dictionary concatenation now converts the element position to a byte
    offset before zeroing null index runs in concatDictIndices.
    
    ### Why
    
    concatDictIndices tracks pos in elements, but the null-run branch used
    it as a byte offset. For dictionary index widths larger than one byte,
    that could zero the wrong byte range, corrupting adjacent valid indices
    while leaving the null slot's bytes stale.
    
    ### Validation
    
    - go test ./arrow/array -run
    TestConcatDictionaryNullIndexRunsPreserveWideIndices -count=1
    - go test ./arrow/array -count=1
    -
    PARQUET_TEST_DATA=/Users/hoangvu/Code/OSS/arrow-go/parquet-testing/data
    ARROW_TEST_DATA=/Users/hoangvu/Code/OSS/arrow-go/arrow-testing/data go
    test ./... -count=1
---
 arrow/array/concat.go      |  3 ++-
 arrow/array/concat_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/arrow/array/concat.go b/arrow/array/concat.go
index beec182d..974df418 100644
--- a/arrow/array/concat.go
+++ b/arrow/array/concat.go
@@ -282,7 +282,8 @@ func concatDictIndices(mem memory.Allocator, data 
[]arrow.ArrayData, idxType arr
                                                return
                                        }
                                } else {
-                                       
memory.Set(outData[pos:pos+(int(run.Len)*idxWidth)], 0x00)
+                                       bytePos := pos * idxWidth
+                                       
memory.Set(outData[bytePos:bytePos+(int(run.Len)*idxWidth)], 0x00)
                                }
 
                                pos += int(run.Len)
diff --git a/arrow/array/concat_test.go b/arrow/array/concat_test.go
index 7dd0e0c0..5b595e7c 100644
--- a/arrow/array/concat_test.go
+++ b/arrow/array/concat_test.go
@@ -594,6 +594,60 @@ func TestConcatDictionaryNullSlots(t *testing.T) {
        assert.Truef(t, array.Equal(actual, expected), "got: %s, expected: %s", 
actual, expected)
 }
 
+func TestConcatDictionaryNullIndexRunsPreserveWideIndices(t *testing.T) {
+       tests := []arrow.DataType{
+               arrow.PrimitiveTypes.Int16,
+               arrow.PrimitiveTypes.Uint16,
+               arrow.PrimitiveTypes.Int32,
+               arrow.PrimitiveTypes.Uint32,
+               arrow.PrimitiveTypes.Int64,
+               arrow.PrimitiveTypes.Uint64,
+       }
+
+       for _, indexType := range tests {
+               t.Run(indexType.Name(), func(t *testing.T) {
+                       mem := 
memory.NewCheckedAllocator(memory.DefaultAllocator)
+                       defer mem.AssertSize(t, 0)
+
+                       dt := &arrow.DictionaryType{IndexType: indexType, 
ValueType: arrow.BinaryTypes.String}
+
+                       indices1, _, err := array.FromJSON(mem, indexType, 
strings.NewReader(`[256, null, 1]`))
+                       require.NoError(t, err)
+                       defer indices1.Release()
+                       indices2, _, err := array.FromJSON(mem, indexType, 
strings.NewReader(`[0]`))
+                       require.NoError(t, err)
+                       defer indices2.Release()
+
+                       dictBuilder := array.NewStringBuilder(mem)
+                       defer dictBuilder.Release()
+                       for i := 0; i <= 256; i++ {
+                               dictBuilder.Append(fmt.Sprintf("v%03d", i))
+                       }
+                       dict1 := dictBuilder.NewStringArray()
+                       defer dict1.Release()
+
+                       dictBuilder.Append("other")
+                       dict2 := dictBuilder.NewStringArray()
+                       defer dict2.Release()
+
+                       arr1 := array.NewDictionaryArray(dt, indices1, dict1)
+                       defer arr1.Release()
+                       arr2 := array.NewDictionaryArray(dt, indices2, dict2)
+                       defer arr2.Release()
+
+                       actual, err := array.Concatenate([]arrow.Array{arr1, 
arr2}, mem)
+                       require.NoError(t, err)
+                       defer actual.Release()
+
+                       dict := actual.(*array.Dictionary)
+                       assert.Equal(t, "v256", dict.ValueStr(0))
+                       assert.True(t, dict.IsNull(1))
+                       assert.Equal(t, "v001", dict.ValueStr(2))
+                       assert.Equal(t, "other", dict.ValueStr(3))
+               })
+       }
+}
+
 func TestConcatRunEndEncoded(t *testing.T) {
        mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
        defer mem.AssertSize(t, 0)

Reply via email to