From d1488ea3cba18951c0c1a9d33d4c44157a99eaad Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 25 Jul 2026 22:42:49 +0200 Subject: [PATCH] fix(arrow/array): ignore zero-count union appends --- arrow/array/union.go | 8 ++++++++ arrow/array/union_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/arrow/array/union.go b/arrow/array/union.go index 58524a768..2b6c55f17 100644 --- a/arrow/array/union.go +++ b/arrow/array/union.go @@ -1201,6 +1201,10 @@ func (b *DenseUnionBuilder) AppendNull() { // for a DenseUnion this is more efficient than calling AppendNull multiple // times in a loop func (b *DenseUnionBuilder) AppendNulls(n int) { + if n <= 0 { + return + } + // only append 1 null to the child builder, use the same offset twice firstChildCode := b.codes[0] childBuilder := b.typeIDtoBuilder[firstChildCode] @@ -1228,6 +1232,10 @@ func (b *DenseUnionBuilder) AppendEmptyValue() { // at that value using the offsets n times. That makes this more efficient // than calling AppendEmptyValue multiple times. func (b *DenseUnionBuilder) AppendEmptyValues(n int) { + if n <= 0 { + return + } + // only append 1 null to the child builder, use the same offset twice firstChildCode := b.codes[0] childBuilder := b.typeIDtoBuilder[firstChildCode] diff --git a/arrow/array/union_test.go b/arrow/array/union_test.go index e20f02473..24c85552f 100644 --- a/arrow/array/union_test.go +++ b/arrow/array/union_test.go @@ -1148,6 +1148,30 @@ func TestUnions(t *testing.T) { suite.Run(t, new(UnionBuilderSuite)) } +func TestDenseUnionBuilderZeroBulkAppendDoesNotMutateChildren(t *testing.T) { + mem := memory.NewCheckedAllocator(memory.NewGoAllocator()) + defer mem.AssertSize(t, 0) + + typ := arrow.DenseUnionOf( + []arrow.Field{{Name: "value", Type: arrow.PrimitiveTypes.Int32}}, + []arrow.UnionTypeCode{0}, + ) + builder := array.NewDenseUnionBuilder(mem, typ) + defer builder.Release() + + builder.AppendNulls(0) + builder.AppendEmptyValues(0) + assert.Zero(t, builder.Len()) + assert.Zero(t, builder.Child(0).Len()) + + builder.Append(0) + builder.Child(0).(*array.Int32Builder).Append(42) + result := builder.NewDenseUnionArray() + defer result.Release() + assert.EqualValues(t, 0, result.ValueOffset(0)) + assert.EqualValues(t, 42, result.Field(0).(*array.Int32).Value(0)) +} + func TestNestedUnionStructDict(t *testing.T) { // ARROW-18274 dt1 := arrow.SparseUnionOf([]arrow.Field{