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 935f644c fix(arrow/util): release protobuf record builders (#1084)
935f644c is described below
commit 935f644c26155eab8edd530a94c374349cc85111
Author: Minh Vu <[email protected]>
AuthorDate: Wed Aug 5 20:42:48 2026 +0200
fix(arrow/util): release protobuf record builders (#1084)
## What changed
Construct protobuf records through `RecordBuilder.NewRecordBatch` and
release the builder after ownership transfers to the returned record.
Record test helpers now release returned batches as well.
## Why
The manual conversion retained the record builder, each temporary field
array, and the intermediate struct array. Releasing the returned record
did not release those extra references.
## Testing
- `go test ./arrow/util`
- Added checked-allocator coverage for the complete record lifecycle
---
arrow/util/protobuf_reflect.go | 16 +++++-----------
arrow/util/protobuf_reflect_test.go | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+), 11 deletions(-)
diff --git a/arrow/util/protobuf_reflect.go b/arrow/util/protobuf_reflect.go
index 17fa9344..b5ba78ed 100644
--- a/arrow/util/protobuf_reflect.go
+++ b/arrow/util/protobuf_reflect.go
@@ -659,24 +659,18 @@ func (msg ProtobufMessageReflection) Record(mem
memory.Allocator) arrow.RecordBa
}
schema := msg.Schema()
+ if schema.NumFields() == 0 {
+ return array.NewRecordBatch(schema, nil, 1)
+ }
recordBuilder := array.NewRecordBuilder(mem, schema)
+ defer recordBuilder.Release()
- var fieldNames []string
for i, f := range msg.fields {
f.AppendValueOrNull(recordBuilder.Field(i), mem)
- fieldNames = append(fieldNames, f.name())
- }
-
- var arrays []arrow.Array
- for _, bldr := range recordBuilder.Fields() {
- a := bldr.NewArray()
- arrays = append(arrays, a)
}
- structArray, _ := array.NewStructArray(arrays, fieldNames)
-
- return array.RecordFromStructArray(structArray, schema)
+ return recordBuilder.NewRecordBatch()
}
// NewProtobufMessageReflection initialises a ProtobufMessageReflection
diff --git a/arrow/util/protobuf_reflect_test.go
b/arrow/util/protobuf_reflect_test.go
index 70a0e390..c4a085a4 100644
--- a/arrow/util/protobuf_reflect_test.go
+++ b/arrow/util/protobuf_reflect_test.go
@@ -228,6 +228,7 @@ func CheckSchema(t *testing.T, pmr
*ProtobufMessageReflection, want string) {
func CheckRecord(t *testing.T, pmr *ProtobufMessageReflection, jsonStr string)
{
rec := pmr.Record(nil)
+ defer rec.Release()
got, err := json.Marshal(rec)
assert.NoError(t, err)
assert.JSONEq(t, jsonStr, string(got), "got: %s\nwant: %s", got,
jsonStr)
@@ -364,6 +365,14 @@ func TestRecordFromProtobuf(t *testing.T) {
CheckRecord(t, pmr, jsonStr)
}
+func TestRecordReleasesConstructionBuffers(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ pmr := NewProtobufMessageReflection(AllTheTypesNoAnyFixture().msg,
WithEnumHandler(EnumNumber))
+ rec := pmr.Record(mem)
+ rec.Release()
+ mem.AssertSize(t, 0)
+}
+
func TestNullRecordFromProtobuf(t *testing.T) {
pmr := NewProtobufMessageReflection(&util_message.AllTheTypes{})
CheckRecord(t, pmr, `[{
@@ -486,6 +495,17 @@ func TestExcludedNested(t *testing.T) {
CheckRecord(t, pmr, jsonStr)
}
+func TestRecordReturnsOneRowForZeroFieldSchema(t *testing.T) {
+ excludeAll := func(*ProtobufFieldReflection) bool { return true }
+ pmr := NewProtobufMessageReflection(&util_message.AllTheTypesNoAny{},
WithExclusionPolicy(excludeAll))
+
+ rec := pmr.Record(memory.DefaultAllocator)
+ require.NotNil(t, rec)
+ defer rec.Release()
+ require.EqualValues(t, 1, rec.NumRows())
+ require.EqualValues(t, 0, rec.NumCols())
+}
+
type testProtobufReflection struct {
ProtobufFieldReflection
}