Repository: segmentio/parquet-go (go)

Issue:
Panic when reading lists using Read[any]
As the title states, when reading a list using the generic `Read[any]` the library panics with the following error:

```
# ./parquet_panic 
panic: reflect: call of reflect.Value.Cap on interface Value

goroutine 1 [running]:
reflect.Value.capNonSlice({0x7789e0?, 0xc000119e50?, 0x77fc20?})
	/usr/local/go/src/reflect/value.go:1174 +0x105
reflect.Value.Cap(...)
	/usr/local/go/src/reflect/value.go:1158
github.com/segmentio/parquet-go.reconstructFuncOfRepeated.func1({0x7789e0?, 0xc000119e50?, 0x7d6a40?}, {0x94?, 0x75?, 0x14?}, {0xc000132960, 0x4, 0x4})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/row.go:581 +0x15b
github.com/segmentio/parquet-go.reconstructFuncOfGroup.func1({0x7789e0?, 0xc000119d40?, 0x0?}, {0xb0?, 0x2b?, 0x87?}, {0xc000132960, 0x4, 0x4})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/row.go:712 +0x816
github.com/segmentio/parquet-go.(*Schema).Reconstruct(0xc000132780, {0x760a40?, 0xc000119d40?}, {0xc000132960, 0x4, 0x4})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/schema.go:264 +0x21a
github.com/segmentio/parquet-go.(*GenericReader[...]).readRows(0xc000148fa0, {0xc000119d40, 0x1, 0x0})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/reader_go18.go:142 +0x154
github.com/segmentio/parquet-go.(*GenericReader[...]).Read(...)
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/reader_go18.go:107
github.com/segmentio/parquet-go.Read[...]({0x86c720, 0xc0001d6630}, 0xc000192cb0, {0x0?, 0x0?, 0x0?})
	/go/pkg/mod/github.com/segmentio/parquet-go@v0.0.0-20221003171427-0309c7d143fe/parquet_go18.go:30 +0x143
main.main()
	/parquet_panic/main.go:34 +0x2f7
```

Minimal reproduction:
```go
package main

import (
	"bytes"
	"fmt"

	parquet "github.com/segmentio/parquet-go"
)

type TestType struct {
	Key []int
}

func main() {
	schema := parquet.SchemaOf(TestType{})
	buffer := parquet.NewGenericBuffer[any](schema)

	data := make([]any, 1)
	data[0] = TestType{Key: []int{1}}
	_, err := buffer.Write(data)
	if err != nil {
		fmt.Println("Exiting with error: ", err)
		return
	}

	var out bytes.Buffer
	writer := parquet.NewGenericWriter[any](&out, schema)

	_, err = parquet.CopyRows(writer, buffer.Rows())
	if err != nil {
		fmt.Println("Exiting with error: ", err)
		return
	}
	writer.Close()

	br := bytes.NewReader(out.Bytes())
	_, _ = parquet.Read[any](br, br.Size())
}

```

Note that the underlying type of the list does not seem to matter. The same issue reproduces for (at least) the following:
```go
type TestType struct {
	Key []string
}
```
```go
type TestType struct {
	Key []byte `parquet:"key,list"`
}
```

However when using the integrated binary type the issue does not exist. So the following type works:
```go
type TestType struct {
	Key []byte
}
```


Pull request:
fix issue 360
Fixes #360 

The problem came from not properly handling the case where a repeated column is being decoded in a `any` value.


Patch:
diff --git a/row.go b/row.go
index ab95ca7..a696255 100644
--- a/row.go
+++ b/row.go
@@ -578,27 +578,35 @@ func reconstructFuncOfRepeated(columnIndex int16, node Node) (int16, reconstruct
 	rowLength := nextColumnIndex - columnIndex
 	return nextColumnIndex, func(value reflect.Value, lvls levels, row Row) (Row, error) {
 		t := value.Type()
-		c := value.Cap()
+		s := value
+		c := 0
 		n := 0
-		if c > 0 {
-			value.Set(value.Slice(0, c))
-		} else {
-			c = 10
-			value.Set(reflect.MakeSlice(t, c, c))
-		}
+		const defaultCapacity = 10
 
 		defer func() {
-			value.Set(value.Slice(0, n))
+			value.Set(s.Slice(0, n))
 		}()
 
+		if t.Kind() == reflect.Interface {
+			var v []interface{}
+			c = defaultCapacity
+			t = reflect.TypeOf(v)
+			s = reflect.MakeSlice(t, c, c)
+		} else if c = s.Cap(); c > 0 {
+			s = s.Slice(0, c)
+		} else {
+			c = defaultCapacity
+			s = reflect.MakeSlice(t, c, c)
+		}
+
 		return reconstructRepeated(columnIndex, rowLength, lvls, row, func(levels levels, row Row) (Row, error) {
 			if n == c {
 				c *= 2
-				newValue := reflect.MakeSlice(t, c, c)
-				reflect.Copy(newValue, value)
-				value.Set(newValue)
+				newSlice := reflect.MakeSlice(t, c, c)
+				reflect.Copy(newSlice, s)
+				s = newSlice
 			}
-			row, err := reconstruct(value.Index(n), levels, row)
+			row, err := reconstruct(s.Index(n), levels, row)
 			n++
 			return row, err
 		})


Test patch:
diff --git a/parquet_go18_test.go b/parquet_go18_test.go
index a974e8d..481c62c 100644
--- a/parquet_go18_test.go
+++ b/parquet_go18_test.go
@@ -6,6 +6,8 @@ import (
 	"bytes"
 	"fmt"
 	"log"
+	"reflect"
+	"testing"
 
 	"github.com/segmentio/parquet-go"
 )
@@ -116,3 +118,49 @@ func ExampleWrite_any() {
 	// map["FirstName":"Han" "LastName":"Solo"]
 	// map["FirstName":"R2" "LastName":"D2"]
 }
+
+func TestIssue360(t *testing.T) {
+	type TestType struct {
+		Key []int
+	}
+
+	schema := parquet.SchemaOf(TestType{})
+	buffer := parquet.NewGenericBuffer[any](schema)
+
+	data := make([]any, 1)
+	data[0] = TestType{Key: []int{1}}
+	_, err := buffer.Write(data)
+	if err != nil {
+		fmt.Println("Exiting with error: ", err)
+		return
+	}
+
+	var out bytes.Buffer
+	writer := parquet.NewGenericWriter[any](&out, schema)
+
+	_, err = parquet.CopyRows(writer, buffer.Rows())
+	if err != nil {
+		fmt.Println("Exiting with error: ", err)
+		return
+	}
+	writer.Close()
+
+	br := bytes.NewReader(out.Bytes())
+	rows, _ := parquet.Read[any](br, br.Size())
+
+	expect := []any{
+		map[string]any{
+			"Key": []any{
+				int64(1),
+			},
+		},
+	}
+
+	if !reflect.DeepEqual(rows, expect) {
+		t.Error("rows mismatch")
+
+		for _, row := range rows {
+			t.Logf("%#v", row)
+		}
+	}
+}