zeroshade commented on code in PR #1323:
URL: https://github.com/apache/arrow-go/pull/1323#discussion_r4085829885


##########
arrow/array/concat.go:
##########
@@ -223,7 +244,7 @@ func handle32BitOffsetsData(data []arrow.ArrayData, out 
*memory.Buffer, outLen i
 
                buf := d.Buffers()[1]
                if buf == nil {
-                       return nil, errors.New("array/concat: binary array is 
missing an offset buffer")
+                       return nil, errors.New("array/concat: array is missing 
an offset buffer")

Review Comment:
   This resolves my earlier comment — thanks.
   
   For anyone reading later: the type-neutral wording is the right call here 
because this helper is genuinely shared by four logical types (List, LargeList, 
Binary, LargeBinary), so there's no single correct type name to print. 
Threading the logical type through just to name it in an error wasn't worth the 
parameter.
   
   Worth being aware the change is larger than the wording for lists: on `main` 
this branch wasn't reachable for List at all, because `gatherFixedBuffers` 
skipped nil buffers upstream and the failure surfaced later as `index out of 
range [0] with length 0`. So this is a new, earlier, typed failure rather than 
a reworded one.



##########
arrow/array/concat.go:
##########
@@ -211,6 +211,27 @@ func concatBinaryBuffers(data []arrow.ArrayData, 
byteWidth, length int, out *Dat
        return nil
 }
 
+func concatOffsetsData(data []arrow.ArrayData, byteWidth, length int, mem 
memory.Allocator) (*memory.Buffer, []rng, error) {
+       out := memory.NewResizableBuffer(mem)
+       out.Resize(byteWidth * (length + 1))
+
+       var (
+               valueRanges []rng
+               err         error
+       )
+       switch byteWidth {
+       case arrow.Int64SizeBytes:
+               valueRanges, err = handle64BitOffsetsData(data, out, length)
+       default:
+               valueRanges, err = handle32BitOffsetsData(data, out, length)
+       }
+       if err != nil {
+               out.Release()
+               return nil, nil, err
+       }

Review Comment:
   This `out.Release()` is doing more than tidying up — it fixes a real leak, 
and I don't think the PR description gives it credit.
   
   On `main`, the List/LargeList nil-offset path allocates the output buffer in 
`concatOffsets` and then dies via a recovered panic downstream, so the buffer 
is never released. Your own `TestConcatMissingOffsetsDiagnostic` catches it 
because of the `memory.NewCheckedAllocator` + `AssertSize(t, 0)` — running that 
test against `main` reports `LEAK of 64 bytes FROM ...concatOffsets`. Nice that 
the test covers the allocator state and not just the message; that's what made 
the leak visible.
   
   Might be worth a line in the commit message so it's findable later.



##########
arrow/array/concat.go:
##########
@@ -747,7 +768,7 @@ func concat(data []arrow.ArrayData, mem memory.Allocator) 
(arr arrow.ArrayData,
                }
        case *arrow.ListType:
                offsetWidth := dt.Layout().Buffers[1].ByteWidth
-               offsetBuffer, valueRanges, err := 
concatOffsets(gatherFixedBuffers(data, 1, offsetWidth), offsetWidth, mem)
+               offsetBuffer, valueRanges, err := concatOffsetsData(data, 
offsetWidth, out.length, mem)
                if err != nil {
                        return nil, err
                }

Review Comment:
   Follow-up worth considering, either here or as a separate PR: 
`*arrow.MapType` further down still calls 
`concatOffsets(gatherFixedBuffers(data, 1, offsetWidth), offsetWidth, mem)` and 
therefore still has the exact bug you just fixed for List.
   
   I probed it on this branch:
   
   ```
   map err = arrow/concat: runtime error: index out of range [0] with length 0
   checked_allocator.go:191: LEAK of 64 bytes
   ```
   
   I tried the one-line swap to `concatOffsetsData(data, offsetWidth, 
out.length, mem)` and it works — the probe becomes `array/concat: array is 
missing an offset buffer` with no leak, and `./arrow/array/...` plus 
`./arrow/compute/...` pass clean.
   
   That would also leave `concatOffsets`, `handle32BitOffsets`, and 
`handle64BitOffsets` with no callers, so they could be deleted. Bonus: that 
retires the `expand := src[:len(src)+1]` trick in `handle32BitOffsets`, which 
reads one element past a slice's length and relies on the gathered buffer 
having spare capacity — the direct `src[begin:end]` / `src[end]` form you've 
moved to is bounds-checked against the buffer's real length instead.
   
   Entirely your call whether to widen this PR's scope; the List/LargeList 
change stands on its own.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to