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 4f42a9e0 fix(arrow/ipc): return errors for invalid record indexes 
(#1120)
4f42a9e0 is described below

commit 4f42a9e05d17c6c45ac05301d28423b899d63e37
Author: Minh Vu <[email protected]>
AuthorDate: Mon Aug 10 19:58:12 2026 +0200

    fix(arrow/ipc): return errors for invalid record indexes (#1120)
    
    ### Rationale for this change
    
    RecordBatchAt panics for an invalid record index even though it returns
    an error. ReadAt also converts an int64 index to int before checking the
    range.
    
    ### What changes are included in this PR?
    
    Return an arrow.ErrIndex error for invalid indexes in RecordBatchAt and
    ReadAt, and check the int64 range before narrowing it to int.
    
    ### Are these changes tested?
    
    - `go test ./arrow/ipc`
    
    ### Are there any user-facing changes?
    
    Invalid record indexes now return errors instead of panicking.
---
 arrow/ipc/file_reader.go |  5 ++++-
 arrow/ipc/ipc_test.go    | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/arrow/ipc/file_reader.go b/arrow/ipc/file_reader.go
index abf2a70f..c9e1f00a 100644
--- a/arrow/ipc/file_reader.go
+++ b/arrow/ipc/file_reader.go
@@ -445,7 +445,7 @@ func (f *FileReader) Record(i int) (arrow.Record, error) {
 // call concurrently.
 func (f *FileReader) RecordBatchAt(i int) (arrow.RecordBatch, error) {
        if i < 0 || i >= f.NumRecords() {
-               panic("arrow/ipc: record index out of bounds")
+               return nil, fmt.Errorf("%w: record index %d out of bounds", 
arrow.ErrIndex, i)
        }
 
        blk, err := f.r.block(f.mem, &f.footer, i)
@@ -509,6 +509,9 @@ func (f *FileReader) Read() (rec arrow.RecordBatch, err 
error) {
 
 // ReadAt reads the i-th record batch from the underlying stream and an error, 
if any.
 func (f *FileReader) ReadAt(i int64) (arrow.RecordBatch, error) {
+       if i < 0 || i >= int64(f.NumRecords()) {
+               return nil, fmt.Errorf("%w: record index %d out of bounds", 
arrow.ErrIndex, i)
+       }
        return f.RecordBatch(int(i))
 }
 
diff --git a/arrow/ipc/ipc_test.go b/arrow/ipc/ipc_test.go
index b7e006ba..895e70d8 100644
--- a/arrow/ipc/ipc_test.go
+++ b/arrow/ipc/ipc_test.go
@@ -21,6 +21,7 @@ import (
        "errors"
        "fmt"
        "io"
+       "math"
        "math/rand"
        "os"
        "strconv"
@@ -802,6 +803,48 @@ func TestRecordBatchCustomMetadataFileRoundtrip(t 
*testing.T) {
        require.Equal(t, meta.Values(), rm.Metadata().Values())
 }
 
+func TestFileReaderRecordBatchIndexErrors(t *testing.T) {
+       mem := memory.NewGoAllocator()
+       schema := arrow.NewSchema(
+               []arrow.Field{{Name: "x", Type: arrow.PrimitiveTypes.Int32}},
+               nil,
+       )
+
+       builder := array.NewInt32Builder(mem)
+       builder.Append(1)
+       column := builder.NewArray()
+       builder.Release()
+       record := array.NewRecordBatch(schema, []arrow.Array{column}, 1)
+       column.Release()
+
+       var buf bytes.Buffer
+       writer, err := ipc.NewFileWriter(&buf, ipc.WithSchema(schema))
+       require.NoError(t, err)
+       require.NoError(t, writer.Write(record))
+       require.NoError(t, writer.Close())
+       record.Release()
+
+       reader, err := ipc.NewFileReader(bytes.NewReader(buf.Bytes()))
+       require.NoError(t, err)
+       defer reader.Close()
+
+       _, err = reader.RecordBatchAt(-1)
+       require.ErrorIs(t, err, arrow.ErrIndex)
+       _, err = reader.RecordBatchAt(reader.NumRecords())
+       require.ErrorIs(t, err, arrow.ErrIndex)
+
+       _, err = reader.ReadAt(-1)
+       require.ErrorIs(t, err, arrow.ErrIndex)
+       _, err = reader.ReadAt(math.MaxInt64)
+       require.ErrorIs(t, err, arrow.ErrIndex)
+       _, err = reader.ReadAt(int64(reader.NumRecords()))
+       require.ErrorIs(t, err, arrow.ErrIndex)
+
+       batch, err := reader.RecordBatchAt(0)
+       require.NoError(t, err)
+       batch.Release()
+}
+
 func TestRecordBatchCustomMetadataInterop(t *testing.T) {
        t.Run("file", func(t *testing.T) {
                f, err := os.Open("testdata/custom_metadata.arrow")

Reply via email to