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 d8a1e7df fix(arrow/csv): preserve first row during headerless
inference (#1145)
d8a1e7df is described below
commit d8a1e7df31312fac2108199e445ae82f2c4978ff
Author: Minh Vu <[email protected]>
AuthorDate: Tue Aug 11 22:36:03 2026 +0200
fix(arrow/csv): preserve first row during headerless inference (#1145)
## What
When schema inference runs with WithHeader(false), the first CSV record
is consumed while generating f0, f1, and so on, but it is not sent
through the builders. This patch keeps that record pending and replays
it through the normal reader path.
## Test
- go test ./arrow/csv -run
TestInferringSchemaWithoutHeaderPreservesFirstRow -count=1
---
arrow/csv/reader.go | 25 ++++++++++++++++++++++---
arrow/csv/reader_test.go | 25 +++++++++++++++++++++++++
2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/arrow/csv/reader.go b/arrow/csv/reader.go
index 0d4ee0f3..9387f23d 100644
--- a/arrow/csv/reader.go
+++ b/arrow/csv/reader.go
@@ -61,6 +61,7 @@ type Reader struct {
columnFilter []string
columnTypes map[string]arrow.DataType
conversions []conversionColumn
+ pendingRecord []string
stringsCanBeNull bool
nulls []string
@@ -214,6 +215,9 @@ func (r *Reader) readHeader() error {
r.columnFilter = nil
}
r.columnTypes = nil
+ if !r.header {
+ r.pendingRecord = append([]string(nil), records...)
+ }
return nil
}
@@ -272,7 +276,7 @@ func (r *Reader) Next() bool {
// from that row.
func (r *Reader) next1() bool {
var recs []string
- recs, r.err = r.r.Read()
+ recs, r.err = r.readRecord()
if r.err != nil {
r.done = true
if errors.Is(r.err, io.EOF) {
@@ -296,11 +300,17 @@ func (r *Reader) nextall() bool {
}()
var recs [][]string
+ if r.pendingRecord != nil {
+ recs = append(recs, r.pendingRecord)
+ r.pendingRecord = nil
+ }
- recs, r.err = r.r.ReadAll()
+ var remaining [][]string
+ remaining, r.err = r.r.ReadAll()
if r.err != nil {
return false
}
+ recs = append(recs, remaining...)
for _, rec := range recs {
r.validate(rec)
@@ -321,7 +331,7 @@ func (r *Reader) nextn() bool {
)
for i := 0; i < r.chunk && !r.done; i++ {
- recs, err = r.r.Read()
+ recs, err = r.readRecord()
if err != nil {
if !errors.Is(err, io.EOF) {
r.err = err
@@ -343,6 +353,15 @@ func (r *Reader) nextn() bool {
return n > 0
}
+func (r *Reader) readRecord() ([]string, error) {
+ if r.pendingRecord != nil {
+ record := r.pendingRecord
+ r.pendingRecord = nil
+ return record, nil
+ }
+ return r.r.Read()
+}
+
func (r *Reader) validate(recs []string) {
if r.err != nil {
return
diff --git a/arrow/csv/reader_test.go b/arrow/csv/reader_test.go
index 80c78482..c7cd61f4 100644
--- a/arrow/csv/reader_test.go
+++ b/arrow/csv/reader_test.go
@@ -957,6 +957,31 @@ func TestInferringSchema(t *testing.T) {
assert.False(t, r.Next())
}
+func TestInferringSchemaWithoutHeaderPreservesFirstRow(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ r := csv.NewInferringReader(strings.NewReader("1,a\n2,b\n"),
+ csv.WithAllocator(mem), csv.WithHeader(false))
+ defer r.Release()
+
+ var (
+ values []int64
+ strings []string
+ )
+ for r.Next() {
+ rec := r.RecordBatch()
+ values = append(values, rec.Column(0).(*array.Int64).Value(0))
+ strings = append(strings,
rec.Column(1).(*array.String).Value(0))
+ }
+
+ require.NoError(t, r.Err())
+ require.Equal(t, []int64{1, 2}, values)
+ require.Equal(t, []string{"a", "b"}, strings)
+ require.Equal(t, "f0", r.Schema().Field(0).Name)
+ require.Equal(t, "f1", r.Schema().Field(1).Name)
+}
+
func TestInferCSVOptions(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
defer mem.AssertSize(t, 0)