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 5327234c fix(arrow/array): support zero-column tables (#988)
5327234c is described below
commit 5327234c4d0949b9e08da7f47fe3cb5f96e63f47
Author: Minh Vu <[email protected]>
AuthorDate: Thu Jul 23 22:10:30 2026 +0200
fix(arrow/array): support zero-column tables (#988)
### Rationale for this change
Arrow schemas and record batches may have zero fields while still
carrying a logical row count. `NewTableFromSlice` indexed the first
column unconditionally and panicked for an empty schema.
`NewTableFromRecords` inferred rows from columns, so zero-column record
batches lost their row counts.
### What changes are included in this PR?
Handle the empty column slice without indexing `cols[0]`. When the
schema has no fields, sum `RecordBatch.NumRows` to preserve the logical
row count. Tables with columns keep the existing column-based row
inference. Regression tests cover both constructors.
### Are these changes tested?
Yes:
- `go test ./arrow/array`
- `go test -race -count=1 ./arrow/array`
### Are there any user-facing changes?
Zero-column table construction no longer panics, and zero-column record
batches retain their logical row counts. Existing behavior for tables
with columns is unchanged.
---
arrow/array/table.go | 16 ++++++++++++++--
arrow/array/table_test.go | 30 ++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/arrow/array/table.go b/arrow/array/table.go
index 540124b1..6bd44045 100644
--- a/arrow/array/table.go
+++ b/arrow/array/table.go
@@ -149,10 +149,15 @@ func NewTableFromSlice(schema *arrow.Schema, data
[][]arrow.Array) arrow.Table {
chunked.Release()
}
+ var rows int64
+ if len(cols) > 0 {
+ rows = int64(cols[0].Len())
+ }
+
tbl := simpleTable{
schema: schema,
cols: cols,
- rows: int64(cols[0].Len()),
+ rows: rows,
}
tbl.refCount.Add(1)
@@ -178,6 +183,13 @@ func NewTableFromSlice(schema *arrow.Schema, data
[][]arrow.Array) arrow.Table {
func NewTableFromRecords(schema *arrow.Schema, recs []arrow.RecordBatch)
arrow.Table {
arrs := make([]arrow.Array, len(recs))
cols := make([]arrow.Column, schema.NumFields())
+ rows := int64(-1)
+ if len(cols) == 0 {
+ rows = 0
+ for _, rec := range recs {
+ rows += rec.NumRows()
+ }
+ }
defer func(cols []arrow.Column) {
for i := range cols {
@@ -195,7 +207,7 @@ func NewTableFromRecords(schema *arrow.Schema, recs
[]arrow.RecordBatch) arrow.T
chunk.Release()
}
- return NewTable(schema, cols, -1)
+ return NewTable(schema, cols, rows)
}
func (tbl *simpleTable) Schema() *arrow.Schema { return tbl.schema }
diff --git a/arrow/array/table_test.go b/arrow/array/table_test.go
index bbf4a4e6..110b1c7c 100644
--- a/arrow/array/table_test.go
+++ b/arrow/array/table_test.go
@@ -113,6 +113,36 @@ func TestChunked(t *testing.T) {
}
}
+func TestTableFromSliceWithoutColumns(t *testing.T) {
+ schema := arrow.NewSchema(nil, nil)
+ tbl := array.NewTableFromSlice(schema, nil)
+ defer tbl.Release()
+
+ if got, want := tbl.NumRows(), int64(0); got != want {
+ t.Fatalf("NumRows = %d, want %d", got, want)
+ }
+}
+
+func TestTableFromRecordsWithoutColumns(t *testing.T) {
+ schema := arrow.NewSchema(nil, nil)
+ records := []arrow.RecordBatch{
+ array.NewRecordBatch(schema, nil, 2),
+ array.NewRecordBatch(schema, nil, 3),
+ }
+ defer func() {
+ for _, rec := range records {
+ rec.Release()
+ }
+ }()
+
+ tbl := array.NewTableFromRecords(schema, records)
+ defer tbl.Release()
+
+ if got, want := tbl.NumRows(), int64(5); got != want {
+ t.Fatalf("NumRows = %d, want %d", got, want)
+ }
+}
+
func TestChunkedEqualDataType(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)