github.com/segmentio/parquet-go@v0.0.0-20230712180008-5d42db8f0d47/writer.go (about)

     1  package parquet
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"hash/crc32"
     9  	"io"
    10  	"math/bits"
    11  	"sort"
    12  
    13  	"github.com/segmentio/encoding/thrift"
    14  	"github.com/segmentio/parquet-go/compress"
    15  	"github.com/segmentio/parquet-go/encoding"
    16  	"github.com/segmentio/parquet-go/encoding/plain"
    17  	"github.com/segmentio/parquet-go/format"
    18  )
    19  
    20  // Deprecated: A Writer uses a parquet schema and sequence of Go values to
    21  // produce a parquet file to an io.Writer.
    22  //
    23  // This example showcases a typical use of parquet writers:
    24  //
    25  //	writer := parquet.NewWriter(output)
    26  //
    27  //	for _, row := range rows {
    28  //		if err := writer.Write(row); err != nil {
    29  //			...
    30  //		}
    31  //	}
    32  //
    33  //	if err := writer.Close(); err != nil {
    34  //		...
    35  //	}
    36  //
    37  // The Writer type optimizes for minimal memory usage, each page is written as
    38  // soon as it has been filled so only a single page per column needs to be held
    39  // in memory and as a result, there are no opportunities to sort rows within an
    40  // entire row group. Programs that need to produce parquet files with sorted
    41  // row groups should use the Buffer type to buffer and sort the rows prior to
    42  // writing them to a Writer.
    43  //
    44  // For programs building with Go 1.18 or later, the GenericWriter[T] type
    45  // supersedes this one.
    46  type Writer struct {
    47  	output io.Writer
    48  	config *WriterConfig
    49  	schema *Schema
    50  	writer *writer
    51  	rowbuf []Row
    52  }
    53  
    54  // NewWriter constructs a parquet writer writing a file to the given io.Writer.
    55  //
    56  // The function panics if the writer configuration is invalid. Programs that
    57  // cannot guarantee the validity of the options passed to NewWriter should
    58  // construct the writer configuration independently prior to calling this
    59  // function:
    60  //
    61  //	config, err := parquet.NewWriterConfig(options...)
    62  //	if err != nil {
    63  //		// handle the configuration error
    64  //		...
    65  //	} else {
    66  //		// this call to create a writer is guaranteed not to panic
    67  //		writer := parquet.NewWriter(output, config)
    68  //		...
    69  //	}
    70  func NewWriter(output io.Writer, options ...WriterOption) *Writer {
    71  	config, err := NewWriterConfig(options...)
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  	w := &Writer{
    76  		output: output,
    77  		config: config,
    78  	}
    79  	if config.Schema != nil {
    80  		w.configure(config.Schema)
    81  	}
    82  	return w
    83  }
    84  
    85  func (w *Writer) configure(schema *Schema) {
    86  	if schema != nil {
    87  		w.config.Schema = schema
    88  		w.schema = schema
    89  		w.writer = newWriter(w.output, w.config)
    90  	}
    91  }
    92  
    93  // Close must be called after all values were produced to the writer in order to
    94  // flush all buffers and write the parquet footer.
    95  func (w *Writer) Close() error {
    96  	if w.writer != nil {
    97  		return w.writer.close()
    98  	}
    99  	return nil
   100  }
   101  
   102  // Flush flushes all buffers into a row group to the underlying io.Writer.
   103  //
   104  // Flush is called automatically on Close, it is only useful to call explicitly
   105  // if the application needs to limit the size of row groups or wants to produce
   106  // multiple row groups per file.
   107  //
   108  // If the writer attempts to create more than MaxRowGroups row groups the method
   109  // returns ErrTooManyRowGroups.
   110  func (w *Writer) Flush() error {
   111  	if w.writer != nil {
   112  		return w.writer.flush()
   113  	}
   114  	return nil
   115  }
   116  
   117  // Reset clears the state of the writer without flushing any of the buffers,
   118  // and setting the output to the io.Writer passed as argument, allowing the
   119  // writer to be reused to produce another parquet file.
   120  //
   121  // Reset may be called at any time, including after a writer was closed.
   122  func (w *Writer) Reset(output io.Writer) {
   123  	if w.output = output; w.writer != nil {
   124  		w.writer.reset(w.output)
   125  	}
   126  }
   127  
   128  // Write is called to write another row to the parquet file.
   129  //
   130  // The method uses the parquet schema configured on w to traverse the Go value
   131  // and decompose it into a set of columns and values. If no schema were passed
   132  // to NewWriter, it is deducted from the Go type of the row, which then have to
   133  // be a struct or pointer to struct.
   134  func (w *Writer) Write(row interface{}) error {
   135  	if w.schema == nil {
   136  		w.configure(SchemaOf(row))
   137  	}
   138  	if cap(w.rowbuf) == 0 {
   139  		w.rowbuf = make([]Row, 1)
   140  	} else {
   141  		w.rowbuf = w.rowbuf[:1]
   142  	}
   143  	defer clearRows(w.rowbuf)
   144  	w.rowbuf[0] = w.schema.Deconstruct(w.rowbuf[0][:0], row)
   145  	_, err := w.WriteRows(w.rowbuf)
   146  	return err
   147  }
   148  
   149  // WriteRows is called to write rows to the parquet file.
   150  //
   151  // The Writer must have been given a schema when NewWriter was called, otherwise
   152  // the structure of the parquet file cannot be determined from the row only.
   153  //
   154  // The row is expected to contain values for each column of the writer's schema,
   155  // in the order produced by the parquet.(*Schema).Deconstruct method.
   156  func (w *Writer) WriteRows(rows []Row) (int, error) {
   157  	return w.writer.WriteRows(rows)
   158  }
   159  
   160  // WriteRowGroup writes a row group to the parquet file.
   161  //
   162  // Buffered rows will be flushed prior to writing rows from the group, unless
   163  // the row group was empty in which case nothing is written to the file.
   164  //
   165  // The content of the row group is flushed to the writer; after the method
   166  // returns successfully, the row group will be empty and in ready to be reused.
   167  func (w *Writer) WriteRowGroup(rowGroup RowGroup) (int64, error) {
   168  	rowGroupSchema := rowGroup.Schema()
   169  	switch {
   170  	case rowGroupSchema == nil:
   171  		return 0, ErrRowGroupSchemaMissing
   172  	case w.schema == nil:
   173  		w.configure(rowGroupSchema)
   174  	case !nodesAreEqual(w.schema, rowGroupSchema):
   175  		return 0, ErrRowGroupSchemaMismatch
   176  	}
   177  	if err := w.writer.flush(); err != nil {
   178  		return 0, err
   179  	}
   180  	w.writer.configureBloomFilters(rowGroup.ColumnChunks())
   181  	rows := rowGroup.Rows()
   182  	defer rows.Close()
   183  	n, err := CopyRows(w.writer, rows)
   184  	if err != nil {
   185  		return n, err
   186  	}
   187  	return w.writer.writeRowGroup(rowGroup.Schema(), rowGroup.SortingColumns())
   188  }
   189  
   190  // ReadRowsFrom reads rows from the reader passed as arguments and writes them
   191  // to w.
   192  //
   193  // This is similar to calling WriteRow repeatedly, but will be more efficient
   194  // if optimizations are supported by the reader.
   195  func (w *Writer) ReadRowsFrom(rows RowReader) (written int64, err error) {
   196  	if w.schema == nil {
   197  		if r, ok := rows.(RowReaderWithSchema); ok {
   198  			w.configure(r.Schema())
   199  		}
   200  	}
   201  	if cap(w.rowbuf) < defaultRowBufferSize {
   202  		w.rowbuf = make([]Row, defaultRowBufferSize)
   203  	} else {
   204  		w.rowbuf = w.rowbuf[:cap(w.rowbuf)]
   205  	}
   206  	return copyRows(w.writer, rows, w.rowbuf)
   207  }
   208  
   209  // Schema returns the schema of rows written by w.
   210  //
   211  // The returned value will be nil if no schema has yet been configured on w.
   212  func (w *Writer) Schema() *Schema { return w.schema }
   213  
   214  // SetKeyValueMetadata sets a key/value pair in the Parquet file metadata.
   215  //
   216  // Keys are assumed to be unique, if the same key is repeated multiple times the
   217  // last value is retained. While the parquet format does not require unique keys,
   218  // this design decision was made to optimize for the most common use case where
   219  // applications leverage this extension mechanism to associate single values to
   220  // keys. This may create incompatibilities with other parquet libraries, or may
   221  // cause some key/value pairs to be lost when open parquet files written with
   222  // repeated keys. We can revisit this decision if it ever becomes a blocker.
   223  func (w *Writer) SetKeyValueMetadata(key, value string) {
   224  	for i, kv := range w.writer.metadata {
   225  		if kv.Key == key {
   226  			kv.Value = value
   227  			w.writer.metadata[i] = kv
   228  			return
   229  		}
   230  	}
   231  	w.writer.metadata = append(w.writer.metadata, format.KeyValue{
   232  		Key:   key,
   233  		Value: value,
   234  	})
   235  }
   236  
   237  type writer struct {
   238  	buffer  *bufio.Writer
   239  	writer  offsetTrackingWriter
   240  	values  [][]Value
   241  	numRows int64
   242  	maxRows int64
   243  
   244  	createdBy string
   245  	metadata  []format.KeyValue
   246  
   247  	columns     []*writerColumn
   248  	columnChunk []format.ColumnChunk
   249  	columnIndex []format.ColumnIndex
   250  	offsetIndex []format.OffsetIndex
   251  
   252  	columnOrders   []format.ColumnOrder
   253  	schemaElements []format.SchemaElement
   254  	rowGroups      []format.RowGroup
   255  	columnIndexes  [][]format.ColumnIndex
   256  	offsetIndexes  [][]format.OffsetIndex
   257  	sortingColumns []format.SortingColumn
   258  }
   259  
   260  func newWriter(output io.Writer, config *WriterConfig) *writer {
   261  	w := new(writer)
   262  	if config.WriteBufferSize <= 0 {
   263  		w.writer.Reset(output)
   264  	} else {
   265  		w.buffer = bufio.NewWriterSize(output, config.WriteBufferSize)
   266  		w.writer.Reset(w.buffer)
   267  	}
   268  	w.maxRows = config.MaxRowsPerRowGroup
   269  	w.createdBy = config.CreatedBy
   270  	w.metadata = make([]format.KeyValue, 0, len(config.KeyValueMetadata))
   271  	for k, v := range config.KeyValueMetadata {
   272  		w.metadata = append(w.metadata, format.KeyValue{Key: k, Value: v})
   273  	}
   274  	sortKeyValueMetadata(w.metadata)
   275  	w.sortingColumns = make([]format.SortingColumn, len(config.Sorting.SortingColumns))
   276  
   277  	config.Schema.forEachNode(func(name string, node Node) {
   278  		nodeType := node.Type()
   279  
   280  		repetitionType := (*format.FieldRepetitionType)(nil)
   281  		if node != config.Schema { // the root has no repetition type
   282  			repetitionType = fieldRepetitionTypePtrOf(node)
   283  		}
   284  
   285  		// For backward compatibility with older readers, the parquet specification
   286  		// recommends to set the scale and precision on schema elements when the
   287  		// column is of logical type decimal.
   288  		logicalType := nodeType.LogicalType()
   289  		scale, precision := (*int32)(nil), (*int32)(nil)
   290  		if logicalType != nil && logicalType.Decimal != nil {
   291  			scale = &logicalType.Decimal.Scale
   292  			precision = &logicalType.Decimal.Precision
   293  		}
   294  
   295  		typeLength := (*int32)(nil)
   296  		if n := int32(nodeType.Length()); n > 0 {
   297  			typeLength = &n
   298  		}
   299  
   300  		w.schemaElements = append(w.schemaElements, format.SchemaElement{
   301  			Type:           nodeType.PhysicalType(),
   302  			TypeLength:     typeLength,
   303  			RepetitionType: repetitionType,
   304  			Name:           name,
   305  			NumChildren:    int32(len(node.Fields())),
   306  			ConvertedType:  nodeType.ConvertedType(),
   307  			Scale:          scale,
   308  			Precision:      precision,
   309  			LogicalType:    logicalType,
   310  		})
   311  	})
   312  
   313  	dataPageType := format.DataPage
   314  	if config.DataPageVersion == 2 {
   315  		dataPageType = format.DataPageV2
   316  	}
   317  
   318  	defaultCompression := config.Compression
   319  	if defaultCompression == nil {
   320  		defaultCompression = &Uncompressed
   321  	}
   322  
   323  	// Those buffers are scratch space used to generate the page header and
   324  	// content, they are shared by all column chunks because they are only
   325  	// used during calls to writeDictionaryPage or writeDataPage, which are
   326  	// not done concurrently.
   327  	buffers := new(writerBuffers)
   328  
   329  	forEachLeafColumnOf(config.Schema, func(leaf leafColumn) {
   330  		encoding := encodingOf(leaf.node)
   331  		dictionary := Dictionary(nil)
   332  		columnType := leaf.node.Type()
   333  		columnIndex := int(leaf.columnIndex)
   334  		compression := leaf.node.Compression()
   335  
   336  		if compression == nil {
   337  			compression = defaultCompression
   338  		}
   339  
   340  		if isDictionaryEncoding(encoding) {
   341  			dictBuffer := columnType.NewValues(
   342  				make([]byte, 0, defaultDictBufferSize),
   343  				nil,
   344  			)
   345  			dictionary = columnType.NewDictionary(columnIndex, 0, dictBuffer)
   346  			columnType = dictionary.Type()
   347  		}
   348  
   349  		c := &writerColumn{
   350  			buffers:            buffers,
   351  			pool:               config.ColumnPageBuffers,
   352  			columnPath:         leaf.path,
   353  			columnType:         columnType,
   354  			columnIndex:        columnType.NewColumnIndexer(config.ColumnIndexSizeLimit),
   355  			columnFilter:       searchBloomFilterColumn(config.BloomFilters, leaf.path),
   356  			compression:        compression,
   357  			dictionary:         dictionary,
   358  			dataPageType:       dataPageType,
   359  			maxRepetitionLevel: leaf.maxRepetitionLevel,
   360  			maxDefinitionLevel: leaf.maxDefinitionLevel,
   361  			bufferIndex:        int32(leaf.columnIndex),
   362  			bufferSize:         int32(float64(config.PageBufferSize) * 0.98),
   363  			writePageStats:     config.DataPageStatistics,
   364  			encodings:          make([]format.Encoding, 0, 3),
   365  			// Data pages in version 2 can omit compression when dictionary
   366  			// encoding is employed; only the dictionary page needs to be
   367  			// compressed, the data pages are encoded with the hybrid
   368  			// RLE/Bit-Pack encoding which doesn't benefit from an extra
   369  			// compression layer.
   370  			isCompressed: isCompressed(compression) && (dataPageType != format.DataPageV2 || dictionary == nil),
   371  		}
   372  
   373  		c.header.encoder.Reset(c.header.protocol.NewWriter(&buffers.header))
   374  
   375  		if leaf.maxDefinitionLevel > 0 {
   376  			c.encodings = addEncoding(c.encodings, format.RLE)
   377  		}
   378  
   379  		if isDictionaryEncoding(encoding) {
   380  			c.encodings = addEncoding(c.encodings, format.Plain)
   381  		}
   382  
   383  		c.encoding = encoding
   384  		c.encodings = addEncoding(c.encodings, c.encoding.Encoding())
   385  		sortPageEncodings(c.encodings)
   386  
   387  		w.columns = append(w.columns, c)
   388  
   389  		if sortingIndex := searchSortingColumn(config.Sorting.SortingColumns, leaf.path); sortingIndex < len(w.sortingColumns) {
   390  			w.sortingColumns[sortingIndex] = format.SortingColumn{
   391  				ColumnIdx:  int32(leaf.columnIndex),
   392  				Descending: config.Sorting.SortingColumns[sortingIndex].Descending(),
   393  				NullsFirst: config.Sorting.SortingColumns[sortingIndex].NullsFirst(),
   394  			}
   395  		}
   396  	})
   397  
   398  	// Pre-allocate the backing array so that in most cases where the rows
   399  	// contain a single value we will hit collocated memory areas when writing
   400  	// rows to the writer. This won't benefit repeated columns much but in that
   401  	// case we would just waste a bit of memory which we can afford.
   402  	values := make([]Value, len(w.columns))
   403  	w.values = make([][]Value, len(w.columns))
   404  	for i := range values {
   405  		w.values[i] = values[i : i : i+1]
   406  	}
   407  
   408  	w.columnChunk = make([]format.ColumnChunk, len(w.columns))
   409  	w.columnIndex = make([]format.ColumnIndex, len(w.columns))
   410  	w.offsetIndex = make([]format.OffsetIndex, len(w.columns))
   411  	w.columnOrders = make([]format.ColumnOrder, len(w.columns))
   412  
   413  	for i, c := range w.columns {
   414  		w.columnChunk[i] = format.ColumnChunk{
   415  			MetaData: format.ColumnMetaData{
   416  				Type:             format.Type(c.columnType.Kind()),
   417  				Encoding:         c.encodings,
   418  				PathInSchema:     c.columnPath,
   419  				Codec:            c.compression.CompressionCodec(),
   420  				KeyValueMetadata: nil, // TODO
   421  			},
   422  		}
   423  	}
   424  
   425  	for i, c := range w.columns {
   426  		c.columnChunk = &w.columnChunk[i]
   427  		c.offsetIndex = &w.offsetIndex[i]
   428  	}
   429  
   430  	for i, c := range w.columns {
   431  		w.columnOrders[i] = *c.columnType.ColumnOrder()
   432  	}
   433  
   434  	return w
   435  }
   436  
   437  func (w *writer) reset(writer io.Writer) {
   438  	if w.buffer == nil {
   439  		w.writer.Reset(writer)
   440  	} else {
   441  		w.buffer.Reset(writer)
   442  		w.writer.Reset(w.buffer)
   443  	}
   444  	for _, c := range w.columns {
   445  		c.reset()
   446  	}
   447  	for i := range w.rowGroups {
   448  		w.rowGroups[i] = format.RowGroup{}
   449  	}
   450  	for i := range w.columnIndexes {
   451  		w.columnIndexes[i] = nil
   452  	}
   453  	for i := range w.offsetIndexes {
   454  		w.offsetIndexes[i] = nil
   455  	}
   456  	w.rowGroups = w.rowGroups[:0]
   457  	w.columnIndexes = w.columnIndexes[:0]
   458  	w.offsetIndexes = w.offsetIndexes[:0]
   459  }
   460  
   461  func (w *writer) close() error {
   462  	if err := w.writeFileHeader(); err != nil {
   463  		return err
   464  	}
   465  	if err := w.flush(); err != nil {
   466  		return err
   467  	}
   468  	if err := w.writeFileFooter(); err != nil {
   469  		return err
   470  	}
   471  	if w.buffer != nil {
   472  		return w.buffer.Flush()
   473  	}
   474  	return nil
   475  }
   476  
   477  func (w *writer) flush() error {
   478  	_, err := w.writeRowGroup(nil, nil)
   479  	return err
   480  }
   481  
   482  func (w *writer) writeFileHeader() error {
   483  	if w.writer.writer == nil {
   484  		return io.ErrClosedPipe
   485  	}
   486  	if w.writer.offset == 0 {
   487  		_, err := w.writer.WriteString("PAR1")
   488  		return err
   489  	}
   490  	return nil
   491  }
   492  
   493  func (w *writer) configureBloomFilters(columnChunks []ColumnChunk) {
   494  	for i, c := range w.columns {
   495  		if c.columnFilter != nil {
   496  			c.resizeBloomFilter(columnChunks[i].NumValues())
   497  		}
   498  	}
   499  }
   500  
   501  func (w *writer) writeFileFooter() error {
   502  	// The page index is composed of two sections: column and offset indexes.
   503  	// They are written after the row groups, right before the footer (which
   504  	// is written by the parent Writer.Close call).
   505  	//
   506  	// This section both writes the page index and generates the values of
   507  	// ColumnIndexOffset, ColumnIndexLength, OffsetIndexOffset, and
   508  	// OffsetIndexLength in the corresponding columns of the file metadata.
   509  	//
   510  	// Note: the page index is always written, even if we created data pages v1
   511  	// because the parquet format is backward compatible in this case. Older
   512  	// readers will simply ignore this section since they do not know how to
   513  	// decode its content, nor have loaded any metadata to reference it.
   514  	protocol := new(thrift.CompactProtocol)
   515  	encoder := thrift.NewEncoder(protocol.NewWriter(&w.writer))
   516  
   517  	for i, columnIndexes := range w.columnIndexes {
   518  		rowGroup := &w.rowGroups[i]
   519  		for j := range columnIndexes {
   520  			column := &rowGroup.Columns[j]
   521  			column.ColumnIndexOffset = w.writer.offset
   522  			if err := encoder.Encode(&columnIndexes[j]); err != nil {
   523  				return err
   524  			}
   525  			column.ColumnIndexLength = int32(w.writer.offset - column.ColumnIndexOffset)
   526  		}
   527  	}
   528  
   529  	for i, offsetIndexes := range w.offsetIndexes {
   530  		rowGroup := &w.rowGroups[i]
   531  		for j := range offsetIndexes {
   532  			column := &rowGroup.Columns[j]
   533  			column.OffsetIndexOffset = w.writer.offset
   534  			if err := encoder.Encode(&offsetIndexes[j]); err != nil {
   535  				return err
   536  			}
   537  			column.OffsetIndexLength = int32(w.writer.offset - column.OffsetIndexOffset)
   538  		}
   539  	}
   540  
   541  	numRows := int64(0)
   542  	for rowGroupIndex := range w.rowGroups {
   543  		numRows += w.rowGroups[rowGroupIndex].NumRows
   544  	}
   545  
   546  	footer, err := thrift.Marshal(new(thrift.CompactProtocol), &format.FileMetaData{
   547  		Version:          1,
   548  		Schema:           w.schemaElements,
   549  		NumRows:          numRows,
   550  		RowGroups:        w.rowGroups,
   551  		KeyValueMetadata: w.metadata,
   552  		CreatedBy:        w.createdBy,
   553  		ColumnOrders:     w.columnOrders,
   554  	})
   555  	if err != nil {
   556  		return err
   557  	}
   558  
   559  	length := len(footer)
   560  	footer = append(footer, 0, 0, 0, 0)
   561  	footer = append(footer, "PAR1"...)
   562  	binary.LittleEndian.PutUint32(footer[length:], uint32(length))
   563  
   564  	_, err = w.writer.Write(footer)
   565  	return err
   566  }
   567  
   568  func (w *writer) writeRowGroup(rowGroupSchema *Schema, rowGroupSortingColumns []SortingColumn) (int64, error) {
   569  	numRows := w.columns[0].totalRowCount()
   570  	if numRows == 0 {
   571  		return 0, nil
   572  	}
   573  
   574  	if len(w.rowGroups) == MaxRowGroups {
   575  		return 0, ErrTooManyRowGroups
   576  	}
   577  
   578  	defer func() {
   579  		w.numRows = 0
   580  		for _, c := range w.columns {
   581  			c.reset()
   582  		}
   583  		for i := range w.columnIndex {
   584  			w.columnIndex[i] = format.ColumnIndex{}
   585  		}
   586  	}()
   587  
   588  	for _, c := range w.columns {
   589  		if err := c.flush(); err != nil {
   590  			return 0, err
   591  		}
   592  		if err := c.flushFilterPages(); err != nil {
   593  			return 0, err
   594  		}
   595  	}
   596  
   597  	if err := w.writeFileHeader(); err != nil {
   598  		return 0, err
   599  	}
   600  	fileOffset := w.writer.offset
   601  
   602  	for _, c := range w.columns {
   603  		if len(c.filter) > 0 {
   604  			c.columnChunk.MetaData.BloomFilterOffset = w.writer.offset
   605  			if err := c.writeBloomFilter(&w.writer); err != nil {
   606  				return 0, err
   607  			}
   608  		}
   609  	}
   610  
   611  	for i, c := range w.columns {
   612  		w.columnIndex[i] = format.ColumnIndex(c.columnIndex.ColumnIndex())
   613  
   614  		if c.dictionary != nil {
   615  			c.columnChunk.MetaData.DictionaryPageOffset = w.writer.offset
   616  			if err := c.writeDictionaryPage(&w.writer, c.dictionary); err != nil {
   617  				return 0, fmt.Errorf("writing dictionary page of row group colum %d: %w", i, err)
   618  			}
   619  		}
   620  
   621  		dataPageOffset := w.writer.offset
   622  		c.columnChunk.MetaData.DataPageOffset = dataPageOffset
   623  		for j := range c.offsetIndex.PageLocations {
   624  			c.offsetIndex.PageLocations[j].Offset += dataPageOffset
   625  		}
   626  
   627  		for _, page := range c.pages {
   628  			if _, err := io.Copy(&w.writer, page); err != nil {
   629  				return 0, fmt.Errorf("writing buffered pages of row group column %d: %w", i, err)
   630  			}
   631  		}
   632  	}
   633  
   634  	totalByteSize := int64(0)
   635  	totalCompressedSize := int64(0)
   636  
   637  	for i := range w.columnChunk {
   638  		c := &w.columnChunk[i].MetaData
   639  		sortPageEncodingStats(c.EncodingStats)
   640  		totalByteSize += int64(c.TotalUncompressedSize)
   641  		totalCompressedSize += int64(c.TotalCompressedSize)
   642  	}
   643  
   644  	sortingColumns := w.sortingColumns
   645  	if len(sortingColumns) == 0 && len(rowGroupSortingColumns) > 0 {
   646  		sortingColumns = make([]format.SortingColumn, 0, len(rowGroupSortingColumns))
   647  		forEachLeafColumnOf(rowGroupSchema, func(leaf leafColumn) {
   648  			if sortingIndex := searchSortingColumn(rowGroupSortingColumns, leaf.path); sortingIndex < len(sortingColumns) {
   649  				sortingColumns[sortingIndex] = format.SortingColumn{
   650  					ColumnIdx:  int32(leaf.columnIndex),
   651  					Descending: rowGroupSortingColumns[sortingIndex].Descending(),
   652  					NullsFirst: rowGroupSortingColumns[sortingIndex].NullsFirst(),
   653  				}
   654  			}
   655  		})
   656  	}
   657  
   658  	columns := make([]format.ColumnChunk, len(w.columnChunk))
   659  	copy(columns, w.columnChunk)
   660  
   661  	columnIndex := make([]format.ColumnIndex, len(w.columnIndex))
   662  	copy(columnIndex, w.columnIndex)
   663  
   664  	offsetIndex := make([]format.OffsetIndex, len(w.offsetIndex))
   665  	copy(offsetIndex, w.offsetIndex)
   666  
   667  	for i := range columns {
   668  		c := &columns[i]
   669  		c.MetaData.EncodingStats = make([]format.PageEncodingStats, len(c.MetaData.EncodingStats))
   670  		copy(c.MetaData.EncodingStats, w.columnChunk[i].MetaData.EncodingStats)
   671  	}
   672  
   673  	for i := range offsetIndex {
   674  		c := &offsetIndex[i]
   675  		c.PageLocations = make([]format.PageLocation, len(c.PageLocations))
   676  		copy(c.PageLocations, w.offsetIndex[i].PageLocations)
   677  	}
   678  
   679  	w.rowGroups = append(w.rowGroups, format.RowGroup{
   680  		Columns:             columns,
   681  		TotalByteSize:       totalByteSize,
   682  		NumRows:             numRows,
   683  		SortingColumns:      sortingColumns,
   684  		FileOffset:          fileOffset,
   685  		TotalCompressedSize: totalCompressedSize,
   686  		Ordinal:             int16(len(w.rowGroups)),
   687  	})
   688  
   689  	w.columnIndexes = append(w.columnIndexes, columnIndex)
   690  	w.offsetIndexes = append(w.offsetIndexes, offsetIndex)
   691  	return numRows, nil
   692  }
   693  
   694  func (w *writer) WriteRows(rows []Row) (int, error) {
   695  	return w.writeRows(len(rows), func(start, end int) (int, error) {
   696  		defer func() {
   697  			for i, values := range w.values {
   698  				clearValues(values)
   699  				w.values[i] = values[:0]
   700  			}
   701  		}()
   702  
   703  		// TODO: if an error occurs in this method the writer may be left in an
   704  		// partially functional state. Applications are not expected to continue
   705  		// using the writer after getting an error, but maybe we could ensure that
   706  		// we are preventing further use as well?
   707  		for _, row := range rows[start:end] {
   708  			row.Range(func(columnIndex int, columnValues []Value) bool {
   709  				w.values[columnIndex] = append(w.values[columnIndex], columnValues...)
   710  				return true
   711  			})
   712  		}
   713  
   714  		for i, values := range w.values {
   715  			if len(values) > 0 {
   716  				if err := w.columns[i].writeRows(values); err != nil {
   717  					return 0, err
   718  				}
   719  			}
   720  		}
   721  
   722  		return end - start, nil
   723  	})
   724  }
   725  
   726  func (w *writer) writeRows(numRows int, write func(i, j int) (int, error)) (int, error) {
   727  	written := 0
   728  
   729  	for written < numRows {
   730  		remain := w.maxRows - w.numRows
   731  		length := numRows - written
   732  
   733  		if remain == 0 {
   734  			remain = w.maxRows
   735  
   736  			if err := w.flush(); err != nil {
   737  				return written, err
   738  			}
   739  		}
   740  
   741  		if remain < int64(length) {
   742  			length = int(remain)
   743  		}
   744  
   745  		// Since the writer cannot flush pages across row boundaries, calls to
   746  		// WriteRows with very large slices can result in greatly exceeding the
   747  		// target page size. To set a limit to the impact of these large writes
   748  		// we chunk the input in slices of 64 rows.
   749  		//
   750  		// Note that this mechanism isn't perfect; for example, values may hold
   751  		// large byte slices which could still cause the column buffers to grow
   752  		// beyond the target page size.
   753  		const maxRowsPerWrite = 64
   754  		if length > maxRowsPerWrite {
   755  			length = maxRowsPerWrite
   756  		}
   757  
   758  		n, err := write(written, written+length)
   759  		written += n
   760  		w.numRows += int64(n)
   761  		if err != nil {
   762  			return written, err
   763  		}
   764  	}
   765  
   766  	return written, nil
   767  }
   768  
   769  // The WriteValues method is intended to work in pair with WritePage to allow
   770  // programs to target writing values to specific columns of of the writer.
   771  func (w *writer) WriteValues(values []Value) (numValues int, err error) {
   772  	return w.columns[values[0].Column()].WriteValues(values)
   773  }
   774  
   775  // One writerBuffers is used by each writer instance, the memory buffers here
   776  // are shared by all columns of the writer because serialization is not done
   777  // concurrently, which helps keep memory utilization low, both in the total
   778  // footprint and GC cost.
   779  //
   780  // The type also exposes helper methods to facilitate the generation of parquet
   781  // pages. A scratch space is used when serialization requires combining multiple
   782  // buffers or compressing the page data, with double-buffering technique being
   783  // employed by swapping the scratch and page buffers to minimize memory copies.
   784  type writerBuffers struct {
   785  	header      bytes.Buffer // buffer where page headers are encoded
   786  	repetitions []byte       // buffer used to encode repetition levels
   787  	definitions []byte       // buffer used to encode definition levels
   788  	page        []byte       // page buffer holding the page data
   789  	scratch     []byte       // scratch space used for compression
   790  }
   791  
   792  func (wb *writerBuffers) crc32() (checksum uint32) {
   793  	checksum = crc32.Update(checksum, crc32.IEEETable, wb.repetitions)
   794  	checksum = crc32.Update(checksum, crc32.IEEETable, wb.definitions)
   795  	checksum = crc32.Update(checksum, crc32.IEEETable, wb.page)
   796  	return checksum
   797  }
   798  
   799  func (wb *writerBuffers) size() int {
   800  	return len(wb.repetitions) + len(wb.definitions) + len(wb.page)
   801  }
   802  
   803  func (wb *writerBuffers) reset() {
   804  	wb.repetitions = wb.repetitions[:0]
   805  	wb.definitions = wb.definitions[:0]
   806  	wb.page = wb.page[:0]
   807  }
   808  
   809  func encodeLevels(dst, src []byte, maxLevel byte) ([]byte, error) {
   810  	bitWidth := bits.Len8(maxLevel)
   811  	return levelEncodingsRLE[bitWidth-1].EncodeLevels(dst, src)
   812  }
   813  
   814  func (wb *writerBuffers) encodeRepetitionLevels(page Page, maxRepetitionLevel byte) (err error) {
   815  	wb.repetitions, err = encodeLevels(wb.repetitions, page.RepetitionLevels(), maxRepetitionLevel)
   816  	return
   817  }
   818  
   819  func (wb *writerBuffers) encodeDefinitionLevels(page Page, maxDefinitionLevel byte) (err error) {
   820  	wb.definitions, err = encodeLevels(wb.definitions, page.DefinitionLevels(), maxDefinitionLevel)
   821  	return
   822  }
   823  
   824  func (wb *writerBuffers) prependLevelsToDataPageV1(maxRepetitionLevel, maxDefinitionLevel byte) {
   825  	hasRepetitionLevels := maxRepetitionLevel > 0
   826  	hasDefinitionLevels := maxDefinitionLevel > 0
   827  
   828  	if hasRepetitionLevels || hasDefinitionLevels {
   829  		wb.scratch = wb.scratch[:0]
   830  		// In data pages v1, the repetition and definition levels are prefixed
   831  		// with the 4 bytes length of the sections. While the parquet-format
   832  		// documentation indicates that the length prefix is part of the hybrid
   833  		// RLE/Bit-Pack encoding, this is the only condition where it is used
   834  		// so we treat it as a special case rather than implementing it in the
   835  		// encoding.
   836  		//
   837  		// Reference https://github.com/apache/parquet-format/blob/master/Encodings.md#run-length-encoding--bit-packing-hybrid-rle--3
   838  		if hasRepetitionLevels {
   839  			wb.scratch = plain.AppendInt32(wb.scratch, int32(len(wb.repetitions)))
   840  			wb.scratch = append(wb.scratch, wb.repetitions...)
   841  			wb.repetitions = wb.repetitions[:0]
   842  		}
   843  		if hasDefinitionLevels {
   844  			wb.scratch = plain.AppendInt32(wb.scratch, int32(len(wb.definitions)))
   845  			wb.scratch = append(wb.scratch, wb.definitions...)
   846  			wb.definitions = wb.definitions[:0]
   847  		}
   848  		wb.scratch = append(wb.scratch, wb.page...)
   849  		wb.swapPageAndScratchBuffers()
   850  	}
   851  }
   852  
   853  func (wb *writerBuffers) encode(page Page, enc encoding.Encoding) (err error) {
   854  	pageType := page.Type()
   855  	pageData := page.Data()
   856  	wb.page, err = pageType.Encode(wb.page[:0], pageData, enc)
   857  	return err
   858  }
   859  
   860  func (wb *writerBuffers) compress(codec compress.Codec) (err error) {
   861  	wb.scratch, err = codec.Encode(wb.scratch[:0], wb.page)
   862  	wb.swapPageAndScratchBuffers()
   863  	return err
   864  }
   865  
   866  func (wb *writerBuffers) swapPageAndScratchBuffers() {
   867  	wb.page, wb.scratch = wb.scratch, wb.page[:0]
   868  }
   869  
   870  type writerColumn struct {
   871  	pool  BufferPool
   872  	pages []io.ReadWriteSeeker
   873  
   874  	columnPath   columnPath
   875  	columnType   Type
   876  	columnIndex  ColumnIndexer
   877  	columnBuffer ColumnBuffer
   878  	columnFilter BloomFilterColumn
   879  	encoding     encoding.Encoding
   880  	compression  compress.Codec
   881  	dictionary   Dictionary
   882  
   883  	dataPageType       format.PageType
   884  	maxRepetitionLevel byte
   885  	maxDefinitionLevel byte
   886  
   887  	buffers *writerBuffers
   888  
   889  	header struct {
   890  		protocol thrift.CompactProtocol
   891  		encoder  thrift.Encoder
   892  	}
   893  
   894  	filter         []byte
   895  	numRows        int64
   896  	bufferIndex    int32
   897  	bufferSize     int32
   898  	writePageStats bool
   899  	isCompressed   bool
   900  	encodings      []format.Encoding
   901  
   902  	columnChunk *format.ColumnChunk
   903  	offsetIndex *format.OffsetIndex
   904  }
   905  
   906  func (c *writerColumn) reset() {
   907  	if c.columnBuffer != nil {
   908  		c.columnBuffer.Reset()
   909  	}
   910  	if c.columnIndex != nil {
   911  		c.columnIndex.Reset()
   912  	}
   913  	if c.dictionary != nil {
   914  		c.dictionary.Reset()
   915  	}
   916  	for _, page := range c.pages {
   917  		c.pool.PutBuffer(page)
   918  	}
   919  	for i := range c.pages {
   920  		c.pages[i] = nil
   921  	}
   922  	c.pages = c.pages[:0]
   923  	// Bloom filters may change in size between row groups, but we retain the
   924  	// buffer to avoid reallocating large memory blocks.
   925  	c.filter = c.filter[:0]
   926  	c.numRows = 0
   927  	// Reset the fields of column chunks that change between row groups,
   928  	// but keep the ones that remain unchanged.
   929  	c.columnChunk.MetaData.NumValues = 0
   930  	c.columnChunk.MetaData.TotalUncompressedSize = 0
   931  	c.columnChunk.MetaData.TotalCompressedSize = 0
   932  	c.columnChunk.MetaData.DataPageOffset = 0
   933  	c.columnChunk.MetaData.DictionaryPageOffset = 0
   934  	c.columnChunk.MetaData.Statistics = format.Statistics{}
   935  	c.columnChunk.MetaData.EncodingStats = c.columnChunk.MetaData.EncodingStats[:0]
   936  	c.columnChunk.MetaData.BloomFilterOffset = 0
   937  	c.offsetIndex.PageLocations = c.offsetIndex.PageLocations[:0]
   938  }
   939  
   940  func (c *writerColumn) totalRowCount() int64 {
   941  	n := c.numRows
   942  	if c.columnBuffer != nil {
   943  		n += int64(c.columnBuffer.Len())
   944  	}
   945  	return n
   946  }
   947  
   948  func (c *writerColumn) flush() (err error) {
   949  	if c.columnBuffer.Len() > 0 {
   950  		defer c.columnBuffer.Reset()
   951  		_, err = c.writeDataPage(c.columnBuffer.Page())
   952  	}
   953  	return err
   954  }
   955  
   956  func (c *writerColumn) flushFilterPages() error {
   957  	if c.columnFilter == nil {
   958  		return nil
   959  	}
   960  
   961  	// If there is a dictionary, it contains all the values that we need to
   962  	// write to the filter.
   963  	if dict := c.dictionary; dict != nil {
   964  		// Need to always attempt to resize the filter, as the writer might
   965  		// be reused after resetting which would have reset the length of
   966  		// the filter to 0.
   967  		c.resizeBloomFilter(int64(dict.Len()))
   968  		return c.writePageToFilter(dict.Page())
   969  	}
   970  
   971  	// When the filter was already allocated, pages have been written to it as
   972  	// they were seen by the column writer.
   973  	if len(c.filter) > 0 {
   974  		return nil
   975  	}
   976  
   977  	// When the filter was not allocated, the writer did not know how many
   978  	// values were going to be seen and therefore could not properly size the
   979  	// filter ahead of time. In this case, we read back all the pages that we
   980  	// have encoded and copy their values back to the filter.
   981  	//
   982  	// A prior implementation of the column writer used to create in-memory
   983  	// copies of the pages to avoid this decoding step; however, this unbounded
   984  	// allocation caused memory exhaustion in production applications. CPU being
   985  	// a somewhat more stretchable resource, we prefer spending time on this
   986  	// decoding step than having to trigger incident response when production
   987  	// systems are getting OOM-Killed.
   988  	c.resizeBloomFilter(c.columnChunk.MetaData.NumValues)
   989  
   990  	column := &Column{
   991  		// Set all the fields required by the decodeDataPage* methods.
   992  		typ:                c.columnType,
   993  		encoding:           c.encoding,
   994  		compression:        c.compression,
   995  		maxRepetitionLevel: c.maxRepetitionLevel,
   996  		maxDefinitionLevel: c.maxDefinitionLevel,
   997  		index:              int16(c.bufferIndex),
   998  	}
   999  
  1000  	rbuf, pool := getBufioReader(nil, 1024)
  1001  	pbuf := (*buffer)(nil)
  1002  	defer func() {
  1003  		putBufioReader(rbuf, pool)
  1004  		if pbuf != nil {
  1005  			pbuf.unref()
  1006  		}
  1007  	}()
  1008  
  1009  	decoder := thrift.NewDecoder(c.header.protocol.NewReader(rbuf))
  1010  
  1011  	for _, p := range c.pages {
  1012  		rbuf.Reset(p)
  1013  
  1014  		header := new(format.PageHeader)
  1015  		if err := decoder.Decode(header); err != nil {
  1016  			return err
  1017  		}
  1018  
  1019  		if pbuf != nil {
  1020  			pbuf.unref()
  1021  		}
  1022  		pbuf = buffers.get(int(header.CompressedPageSize))
  1023  		if _, err := io.ReadFull(rbuf, pbuf.data); err != nil {
  1024  			return err
  1025  		}
  1026  		if _, err := p.Seek(0, io.SeekStart); err != nil {
  1027  			return err
  1028  		}
  1029  
  1030  		var page Page
  1031  		var err error
  1032  
  1033  		switch header.Type {
  1034  		case format.DataPage:
  1035  			page, err = column.decodeDataPageV1(DataPageHeaderV1{header.DataPageHeader}, pbuf, nil, header.UncompressedPageSize)
  1036  		case format.DataPageV2:
  1037  			page, err = column.decodeDataPageV2(DataPageHeaderV2{header.DataPageHeaderV2}, pbuf, nil, header.UncompressedPageSize)
  1038  		}
  1039  		if page != nil {
  1040  			err = c.writePageToFilter(page)
  1041  			Release(page)
  1042  		}
  1043  		if err != nil {
  1044  			return err
  1045  		}
  1046  	}
  1047  
  1048  	return nil
  1049  }
  1050  
  1051  func (c *writerColumn) resizeBloomFilter(numValues int64) {
  1052  	filterSize := c.columnFilter.Size(numValues)
  1053  	if cap(c.filter) < filterSize {
  1054  		c.filter = make([]byte, filterSize)
  1055  	} else {
  1056  		c.filter = c.filter[:filterSize]
  1057  		for i := range c.filter {
  1058  			c.filter[i] = 0
  1059  		}
  1060  	}
  1061  }
  1062  
  1063  func (c *writerColumn) newColumnBuffer() ColumnBuffer {
  1064  	column := c.columnType.NewColumnBuffer(int(c.bufferIndex), c.columnType.EstimateNumValues(int(c.bufferSize)))
  1065  	switch {
  1066  	case c.maxRepetitionLevel > 0:
  1067  		column = newRepeatedColumnBuffer(column, c.maxRepetitionLevel, c.maxDefinitionLevel, nullsGoLast)
  1068  	case c.maxDefinitionLevel > 0:
  1069  		column = newOptionalColumnBuffer(column, c.maxDefinitionLevel, nullsGoLast)
  1070  	}
  1071  	return column
  1072  }
  1073  
  1074  func (c *writerColumn) writeRows(rows []Value) error {
  1075  	if c.columnBuffer == nil {
  1076  		// Lazily create the row group column so we don't need to allocate it if
  1077  		// rows are not written individually to the column.
  1078  		c.columnBuffer = c.newColumnBuffer()
  1079  	}
  1080  	if _, err := c.columnBuffer.WriteValues(rows); err != nil {
  1081  		return err
  1082  	}
  1083  	if c.columnBuffer.Size() >= int64(c.bufferSize) {
  1084  		return c.flush()
  1085  	}
  1086  	return nil
  1087  }
  1088  
  1089  func (c *writerColumn) WriteValues(values []Value) (numValues int, err error) {
  1090  	if c.columnBuffer == nil {
  1091  		c.columnBuffer = c.newColumnBuffer()
  1092  	}
  1093  	return c.columnBuffer.WriteValues(values)
  1094  }
  1095  
  1096  func (c *writerColumn) writeBloomFilter(w io.Writer) error {
  1097  	e := thrift.NewEncoder(c.header.protocol.NewWriter(w))
  1098  	h := bloomFilterHeader(c.columnFilter)
  1099  	h.NumBytes = int32(len(c.filter))
  1100  	if err := e.Encode(&h); err != nil {
  1101  		return err
  1102  	}
  1103  	_, err := w.Write(c.filter)
  1104  	return err
  1105  }
  1106  
  1107  func (c *writerColumn) writeDataPage(page Page) (int64, error) {
  1108  	numValues := page.NumValues()
  1109  	if numValues == 0 {
  1110  		return 0, nil
  1111  	}
  1112  
  1113  	buf := c.buffers
  1114  	buf.reset()
  1115  
  1116  	if c.maxRepetitionLevel > 0 {
  1117  		buf.encodeRepetitionLevels(page, c.maxRepetitionLevel)
  1118  	}
  1119  	if c.maxDefinitionLevel > 0 {
  1120  		buf.encodeDefinitionLevels(page, c.maxDefinitionLevel)
  1121  	}
  1122  
  1123  	if err := buf.encode(page, c.encoding); err != nil {
  1124  		return 0, fmt.Errorf("encoding parquet data page: %w", err)
  1125  	}
  1126  	if c.dataPageType == format.DataPage {
  1127  		buf.prependLevelsToDataPageV1(c.maxDefinitionLevel, c.maxDefinitionLevel)
  1128  	}
  1129  
  1130  	uncompressedPageSize := buf.size()
  1131  	if c.isCompressed {
  1132  		if err := buf.compress(c.compression); err != nil {
  1133  			return 0, fmt.Errorf("compressing parquet data page: %w", err)
  1134  		}
  1135  	}
  1136  
  1137  	if page.Dictionary() == nil && len(c.filter) > 0 {
  1138  		// When the writer knows the number of values in advance (e.g. when
  1139  		// writing a full row group), the filter encoding is set and the page
  1140  		// can be directly applied to the filter, which minimizes memory usage
  1141  		// since there is no need to buffer the values in order to determine
  1142  		// the size of the filter.
  1143  		if err := c.writePageToFilter(page); err != nil {
  1144  			return 0, err
  1145  		}
  1146  	}
  1147  
  1148  	statistics := format.Statistics{}
  1149  	if c.writePageStats {
  1150  		statistics = c.makePageStatistics(page)
  1151  	}
  1152  
  1153  	pageHeader := &format.PageHeader{
  1154  		Type:                 c.dataPageType,
  1155  		UncompressedPageSize: int32(uncompressedPageSize),
  1156  		CompressedPageSize:   int32(buf.size()),
  1157  		CRC:                  int32(buf.crc32()),
  1158  	}
  1159  
  1160  	numRows := page.NumRows()
  1161  	numNulls := page.NumNulls()
  1162  	switch c.dataPageType {
  1163  	case format.DataPage:
  1164  		pageHeader.DataPageHeader = &format.DataPageHeader{
  1165  			NumValues:               int32(numValues),
  1166  			Encoding:                c.encoding.Encoding(),
  1167  			DefinitionLevelEncoding: format.RLE,
  1168  			RepetitionLevelEncoding: format.RLE,
  1169  			Statistics:              statistics,
  1170  		}
  1171  	case format.DataPageV2:
  1172  		pageHeader.DataPageHeaderV2 = &format.DataPageHeaderV2{
  1173  			NumValues:                  int32(numValues),
  1174  			NumNulls:                   int32(numNulls),
  1175  			NumRows:                    int32(numRows),
  1176  			Encoding:                   c.encoding.Encoding(),
  1177  			DefinitionLevelsByteLength: int32(len(buf.definitions)),
  1178  			RepetitionLevelsByteLength: int32(len(buf.repetitions)),
  1179  			IsCompressed:               &c.isCompressed,
  1180  			Statistics:                 statistics,
  1181  		}
  1182  	}
  1183  
  1184  	buf.header.Reset()
  1185  	if err := c.header.encoder.Encode(pageHeader); err != nil {
  1186  		return 0, err
  1187  	}
  1188  
  1189  	size := int64(buf.header.Len()) +
  1190  		int64(len(buf.repetitions)) +
  1191  		int64(len(buf.definitions)) +
  1192  		int64(len(buf.page))
  1193  
  1194  	err := c.writePageTo(size, func(output io.Writer) (written int64, err error) {
  1195  		for _, data := range [...][]byte{
  1196  			buf.header.Bytes(),
  1197  			buf.repetitions,
  1198  			buf.definitions,
  1199  			buf.page,
  1200  		} {
  1201  			wn, err := output.Write(data)
  1202  			written += int64(wn)
  1203  			if err != nil {
  1204  				return written, err
  1205  			}
  1206  		}
  1207  		return written, nil
  1208  	})
  1209  	if err != nil {
  1210  		return 0, err
  1211  	}
  1212  
  1213  	c.recordPageStats(int32(buf.header.Len()), pageHeader, page)
  1214  	return numValues, nil
  1215  }
  1216  
  1217  func (c *writerColumn) writeDictionaryPage(output io.Writer, dict Dictionary) (err error) {
  1218  	buf := c.buffers
  1219  	buf.reset()
  1220  
  1221  	if err := buf.encode(dict.Page(), &Plain); err != nil {
  1222  		return fmt.Errorf("writing parquet dictionary page: %w", err)
  1223  	}
  1224  
  1225  	uncompressedPageSize := buf.size()
  1226  	if isCompressed(c.compression) {
  1227  		if err := buf.compress(c.compression); err != nil {
  1228  			return fmt.Errorf("copmressing parquet dictionary page: %w", err)
  1229  		}
  1230  	}
  1231  
  1232  	pageHeader := &format.PageHeader{
  1233  		Type:                 format.DictionaryPage,
  1234  		UncompressedPageSize: int32(uncompressedPageSize),
  1235  		CompressedPageSize:   int32(buf.size()),
  1236  		CRC:                  int32(buf.crc32()),
  1237  		DictionaryPageHeader: &format.DictionaryPageHeader{
  1238  			NumValues: int32(dict.Len()),
  1239  			Encoding:  format.Plain,
  1240  			IsSorted:  false,
  1241  		},
  1242  	}
  1243  
  1244  	header := &c.buffers.header
  1245  	header.Reset()
  1246  	if err := c.header.encoder.Encode(pageHeader); err != nil {
  1247  		return err
  1248  	}
  1249  	if _, err := output.Write(header.Bytes()); err != nil {
  1250  		return err
  1251  	}
  1252  	if _, err := output.Write(buf.page); err != nil {
  1253  		return err
  1254  	}
  1255  	c.recordPageStats(int32(header.Len()), pageHeader, nil)
  1256  	return nil
  1257  }
  1258  
  1259  func (w *writerColumn) writePageToFilter(page Page) (err error) {
  1260  	pageType := page.Type()
  1261  	pageData := page.Data()
  1262  	w.filter, err = pageType.Encode(w.filter, pageData, w.columnFilter.Encoding())
  1263  	return err
  1264  }
  1265  
  1266  func (c *writerColumn) writePageTo(size int64, writeTo func(io.Writer) (int64, error)) error {
  1267  	buffer := c.pool.GetBuffer()
  1268  	defer func() {
  1269  		if buffer != nil {
  1270  			c.pool.PutBuffer(buffer)
  1271  		}
  1272  	}()
  1273  	written, err := writeTo(buffer)
  1274  	if err != nil {
  1275  		return err
  1276  	}
  1277  	if written != size {
  1278  		return fmt.Errorf("writing parquet column page expected %dB but got %dB: %w", size, written, io.ErrShortWrite)
  1279  	}
  1280  	offset, err := buffer.Seek(0, io.SeekStart)
  1281  	if err != nil {
  1282  		return err
  1283  	}
  1284  	if offset != 0 {
  1285  		return fmt.Errorf("resetting parquet page buffer to the start expected offset zero but got %d", offset)
  1286  	}
  1287  	c.pages, buffer = append(c.pages, buffer), nil
  1288  	return nil
  1289  }
  1290  
  1291  func (c *writerColumn) makePageStatistics(page Page) format.Statistics {
  1292  	numNulls := page.NumNulls()
  1293  	minValue, maxValue, _ := page.Bounds()
  1294  	minValueBytes := minValue.Bytes()
  1295  	maxValueBytes := maxValue.Bytes()
  1296  	return format.Statistics{
  1297  		Min:       minValueBytes, // deprecated
  1298  		Max:       maxValueBytes, // deprecated
  1299  		NullCount: numNulls,
  1300  		MinValue:  minValueBytes,
  1301  		MaxValue:  maxValueBytes,
  1302  	}
  1303  }
  1304  
  1305  func (c *writerColumn) recordPageStats(headerSize int32, header *format.PageHeader, page Page) {
  1306  	uncompressedSize := headerSize + header.UncompressedPageSize
  1307  	compressedSize := headerSize + header.CompressedPageSize
  1308  
  1309  	if page != nil {
  1310  		numNulls := page.NumNulls()
  1311  		numValues := page.NumValues()
  1312  		minValue, maxValue, pageHasBounds := page.Bounds()
  1313  		c.columnIndex.IndexPage(numValues, numNulls, minValue, maxValue)
  1314  		c.columnChunk.MetaData.NumValues += numValues
  1315  		c.columnChunk.MetaData.Statistics.NullCount += numNulls
  1316  
  1317  		if pageHasBounds {
  1318  			var existingMaxValue, existingMinValue Value
  1319  
  1320  			if c.columnChunk.MetaData.Statistics.MaxValue != nil && c.columnChunk.MetaData.Statistics.MinValue != nil {
  1321  				existingMaxValue = c.columnType.Kind().Value(c.columnChunk.MetaData.Statistics.MaxValue)
  1322  				existingMinValue = c.columnType.Kind().Value(c.columnChunk.MetaData.Statistics.MinValue)
  1323  			}
  1324  
  1325  			if existingMaxValue.isNull() || c.columnType.Compare(maxValue, existingMaxValue) > 0 {
  1326  				c.columnChunk.MetaData.Statistics.MaxValue = maxValue.Bytes()
  1327  			}
  1328  
  1329  			if existingMinValue.isNull() || c.columnType.Compare(minValue, existingMinValue) < 0 {
  1330  				c.columnChunk.MetaData.Statistics.MinValue = minValue.Bytes()
  1331  			}
  1332  		}
  1333  
  1334  		c.offsetIndex.PageLocations = append(c.offsetIndex.PageLocations, format.PageLocation{
  1335  			Offset:             c.columnChunk.MetaData.TotalCompressedSize,
  1336  			CompressedPageSize: compressedSize,
  1337  			FirstRowIndex:      c.numRows,
  1338  		})
  1339  
  1340  		c.numRows += page.NumRows()
  1341  	}
  1342  
  1343  	pageType := header.Type
  1344  	encoding := format.Encoding(-1)
  1345  	switch pageType {
  1346  	case format.DataPageV2:
  1347  		encoding = header.DataPageHeaderV2.Encoding
  1348  	case format.DataPage:
  1349  		encoding = header.DataPageHeader.Encoding
  1350  	case format.DictionaryPage:
  1351  		encoding = header.DictionaryPageHeader.Encoding
  1352  	}
  1353  
  1354  	c.columnChunk.MetaData.TotalUncompressedSize += int64(uncompressedSize)
  1355  	c.columnChunk.MetaData.TotalCompressedSize += int64(compressedSize)
  1356  	c.columnChunk.MetaData.EncodingStats = addPageEncodingStats(c.columnChunk.MetaData.EncodingStats, format.PageEncodingStats{
  1357  		PageType: pageType,
  1358  		Encoding: encoding,
  1359  		Count:    1,
  1360  	})
  1361  }
  1362  
  1363  func addEncoding(encodings []format.Encoding, add format.Encoding) []format.Encoding {
  1364  	for _, enc := range encodings {
  1365  		if enc == add {
  1366  			return encodings
  1367  		}
  1368  	}
  1369  	return append(encodings, add)
  1370  }
  1371  
  1372  func addPageEncodingStats(stats []format.PageEncodingStats, pages ...format.PageEncodingStats) []format.PageEncodingStats {
  1373  addPages:
  1374  	for _, add := range pages {
  1375  		for i, st := range stats {
  1376  			if st.PageType == add.PageType && st.Encoding == add.Encoding {
  1377  				stats[i].Count += add.Count
  1378  				continue addPages
  1379  			}
  1380  		}
  1381  		stats = append(stats, add)
  1382  	}
  1383  	return stats
  1384  }
  1385  
  1386  func sortPageEncodings(encodings []format.Encoding) {
  1387  	sort.Slice(encodings, func(i, j int) bool {
  1388  		return encodings[i] < encodings[j]
  1389  	})
  1390  }
  1391  
  1392  func sortPageEncodingStats(stats []format.PageEncodingStats) {
  1393  	sort.Slice(stats, func(i, j int) bool {
  1394  		s1 := &stats[i]
  1395  		s2 := &stats[j]
  1396  		if s1.PageType != s2.PageType {
  1397  			return s1.PageType < s2.PageType
  1398  		}
  1399  		return s1.Encoding < s2.Encoding
  1400  	})
  1401  }
  1402  
  1403  type offsetTrackingWriter struct {
  1404  	writer io.Writer
  1405  	offset int64
  1406  }
  1407  
  1408  func (w *offsetTrackingWriter) Reset(writer io.Writer) {
  1409  	w.writer = writer
  1410  	w.offset = 0
  1411  }
  1412  
  1413  func (w *offsetTrackingWriter) Write(b []byte) (int, error) {
  1414  	n, err := w.writer.Write(b)
  1415  	w.offset += int64(n)
  1416  	return n, err
  1417  }
  1418  
  1419  func (w *offsetTrackingWriter) WriteString(s string) (int, error) {
  1420  	n, err := io.WriteString(w.writer, s)
  1421  	w.offset += int64(n)
  1422  	return n, err
  1423  }
  1424  
  1425  func (w *offsetTrackingWriter) ReadFrom(r io.Reader) (int64, error) {
  1426  	// io.Copy will make use of io.ReaderFrom if w.writer implements it.
  1427  	n, err := io.Copy(w.writer, r)
  1428  	w.offset += n
  1429  	return n, err
  1430  }
  1431  
  1432  var (
  1433  	_ RowWriterWithSchema = (*Writer)(nil)
  1434  	_ RowReaderFrom       = (*Writer)(nil)
  1435  	_ RowGroupWriter      = (*Writer)(nil)
  1436  
  1437  	_ RowWriter   = (*writer)(nil)
  1438  	_ ValueWriter = (*writer)(nil)
  1439  
  1440  	_ ValueWriter = (*writerColumn)(nil)
  1441  
  1442  	_ io.ReaderFrom   = (*offsetTrackingWriter)(nil)
  1443  	_ io.StringWriter = (*offsetTrackingWriter)(nil)
  1444  )