diff --git a/arrow/scalar/parse.go b/arrow/scalar/parse.go index 69108e8f..41de7717 100644 --- a/arrow/scalar/parse.go +++ b/arrow/scalar/parse.go @@ -140,6 +140,17 @@ func ToScalar(val interface{}, mem memory.Allocator) (Scalar, error) { case reflect.Struct: scalars := make([]Scalar, 0, v.Type().NumField()) fields := make([]string, 0, v.Type().NumField()) + success := false + defer func() { + if success { + return + } + for _, child := range scalars { + if releasable, ok := child.(Releasable); ok { + releasable.Release() + } + } + }() for i := 0; i < v.Type().NumField(); i++ { fld := v.Type().Field(i) tag := fld.Tag.Get("compute") @@ -162,7 +173,12 @@ func ToScalar(val interface{}, mem memory.Allocator) (Scalar, error) { fields = append(fields, "_type_name") } - return NewStructScalarWithNames(scalars, fields) + out, err := NewStructScalarWithNames(scalars, fields) + if err != nil { + return nil, err + } + success = true + return out, nil case reflect.Slice: return createListScalar(v, mem) default: diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go index 24cb7ba0..392019d7 100644 --- a/arrow/scalar/scalar_test.go +++ b/arrow/scalar/scalar_test.go @@ -1410,6 +1410,11 @@ func (v valueFromScalarTarget) FromStructScalar(*scalar.Struct) error { return nil } +type PartialScalarTest struct { + Good []string + Bad []complex64 +} + func TestToScalar(t *testing.T) { ot := &OptionValTest{ToType: arrow.BinaryTypes.String, Allow: true} sc, err := scalar.ToScalar(ot, memory.DefaultAllocator) @@ -1515,6 +1520,17 @@ func TestFromScalarMetadataDoesNotPrependEmptyEntries(t *testing.T) { assert.Equal(t, meta.Values(), out.FieldMeta[0].Values()) } +func TestToScalarReleasesPartialStructOnError(t *testing.T) { + mem := memory.NewCheckedAllocator(memory.NewGoAllocator()) + defer mem.AssertSize(t, 0) + + _, err := scalar.ToScalar(PartialScalarTest{ + Good: []string{"retained before error"}, + Bad: []complex64{complex(1, 2)}, + }, mem) + require.Error(t, err) +} + var dictIndexTypes = []arrow.DataType{ arrow.PrimitiveTypes.Int8, arrow.PrimitiveTypes.Uint8,