Repository: segmentio/parquet-go
Pull request #113 (2022-03-22 00:25:59)

all: support repeated fixed length binary arrays
Previously when decoding a list of fixed length byte arrays, we'd pass
a byte array with capacity 1 to the decoder, which errored when
attempting to decode a 16 byte array - the capacity should be
a multiple of the length.

Fixes #89


Patch:
diff --git a/dictionary.go b/dictionary.go
index 08c5faf..83de7d6 100644
--- a/dictionary.go
+++ b/dictionary.go
@@ -181,7 +181,7 @@ func newFixedLenByteArrayDictionary(typ Type, bufferSize int) *fixedLenByteArray
 	return &fixedLenByteArrayDictionary{
 		typ:    typ,
 		size:   size,
-		values: make([]byte, 0, dictCap(bufferSize, size)),
+		values: make([]byte, 0, dictCap(bufferSize, size)*size),
 	}
 }
 
@@ -349,7 +349,9 @@ func (page *indexedPage) RepetitionLevels() []int8 { return nil }
 
 func (page *indexedPage) DefinitionLevels() []int8 { return nil }
 
-func (page *indexedPage) WriteTo(e encoding.Encoder) error { return e.EncodeInt32(page.values) }
+func (page *indexedPage) WriteTo(e encoding.Encoder) error {
+	return e.EncodeInt32(page.values)
+}
 
 func (page *indexedPage) Values() ValueReader { return &indexedPageReader{page: page} }
 
diff --git a/encoding/encoding.go b/encoding/encoding.go
index 202b998..ee27659 100644
--- a/encoding/encoding.go
+++ b/encoding/encoding.go
@@ -70,19 +70,19 @@ type Encoder interface {
 	// knows that all values can fit in 16 bits.
 	EncodeInt16(data []int16) error
 
-	// Encodes an array of 32 bits integer values using this encoder.
+	// Encodes an array of 32 bit integer values using this encoder.
 	EncodeInt32(data []int32) error
 
-	// Encodes an array of 64 bits integer values using this encoder.
+	// Encodes an array of 64 bit integer values using this encoder.
 	EncodeInt64(data []int64) error
 
-	// Encodes an array of 96 bits integer values using this encoder.
+	// Encodes an array of 96 bit integer values using this encoder.
 	EncodeInt96(data []deprecated.Int96) error
 
-	// Encodes an array of 32 bits floating point values using this encoder.
+	// Encodes an array of 32 bit floating point values using this encoder.
 	EncodeFloat(data []float32) error
 
-	// Encodes an array of 64 bits floating point values using this encoder.
+	// Encodes an array of 64 bit floating point values using this encoder.
 	EncodeDouble(data []float64) error
 
 	// Encodes an array of variable length byte array values using this encoder.
diff --git a/encoding/notsupported.go b/encoding/notsupported.go
index cef14ef..e7b7a85 100644
--- a/encoding/notsupported.go
+++ b/encoding/notsupported.go
@@ -21,9 +21,9 @@ var (
 	// ErrInvalidArguments is an error returned when arguments passed to the
 	// encoding functions are incorrect and will lead to an expected failure.
 	//
-	// As with ErrNotSupported, this error may be wrapped with specific information
-	// about the problem and application are expected to use errors.Is for
-	// comparisons
+	// As with ErrNotSupported, this error may be wrapped with specific
+	// information about the problem and applications are expected to use
+	// errors.Is for comparisons.
 	ErrInvalidArguments = errors.New("invalid encoding arguments")
 )
 
diff --git a/value.go b/value.go
index c600d8d..b28c624 100644
--- a/value.go
+++ b/value.go
@@ -489,7 +489,7 @@ func (v Value) Format(w fmt.State, r rune) {
 		case w.Flag('+'):
 			fmt.Fprintf(w, "%+[1]c %+[1]d %+[1]r %+[1]s", v)
 		case w.Flag('#'):
-			fmt.Fprintf(w, "parquet.Value{%+[1]c,%+[1]d,%+[1]r,%+[1]s}", v)
+			fmt.Fprintf(w, "parquet.Value{%+[1]c, %+[1]d, %+[1]r, %+[1]s}", v)
 		default:
 			v.Format(w, 's')
 		}


Test patch:
diff --git a/writer_test.go b/writer_test.go
index 9b64e46..5562db2 100644
--- a/writer_test.go
+++ b/writer_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 	"testing/quick"
 
+	"github.com/google/uuid"
 	"github.com/hexops/gotextdiff"
 	"github.com/hexops/gotextdiff/myers"
 	"github.com/hexops/gotextdiff/span"
@@ -565,3 +566,40 @@ func TestBloomFilterForDict(t *testing.T) {
 		t.Error("bloom filter should have contained 'test'")
 	}
 }
+
+func TestWriterRepeatedUUIDDict(t *testing.T) {
+	inputID := uuid.MustParse("123456ab-0000-0000-0000-000000000000")
+	records := []struct {
+		List []uuid.UUID `parquet:"list,dict"`
+	}{{
+		[]uuid.UUID{inputID},
+	}}
+	schema := parquet.SchemaOf(&records[0])
+	b := bytes.NewBuffer(nil)
+	w := parquet.NewWriter(b, schema)
+	err := w.Write(records[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := w.Close(); err != nil {
+		t.Fatal(err)
+	}
+
+	f, err := parquet.OpenFile(bytes.NewReader(b.Bytes()), int64(b.Len()))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rows := f.RowGroup(0).Rows()
+	row, err := rows.ReadRow(nil)
+	if err != nil {
+		t.Fatalf("reading row from parquet file: %v", err)
+	}
+	if len(row) != 1 {
+		t.Errorf("expected 1 value in row, got %d", len(row))
+	}
+	if !bytes.Equal(inputID[:], row[0].Bytes()) {
+		t.Errorf("expected to get UUID %q back out, got %q", inputID, row[0].Bytes())
+	}
+}