github.com/apache/arrow/go/v14@v14.0.2/parquet/file/column_reader.go (about)

     1  // Licensed to the Apache Software Foundation (ASF) under one
     2  // or more contributor license agreements.  See the NOTICE file
     3  // distributed with this work for additional information
     4  // regarding copyright ownership.  The ASF licenses this file
     5  // to you under the Apache License, Version 2.0 (the
     6  // "License"); you may not use this file except in compliance
     7  // with the License.  You may obtain a copy of the License at
     8  //
     9  // http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package file
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/apache/arrow/go/v14/arrow/memory"
    24  	"github.com/apache/arrow/go/v14/internal/utils"
    25  	"github.com/apache/arrow/go/v14/parquet"
    26  	"github.com/apache/arrow/go/v14/parquet/internal/encoding"
    27  	"github.com/apache/arrow/go/v14/parquet/internal/encryption"
    28  	format "github.com/apache/arrow/go/v14/parquet/internal/gen-go/parquet"
    29  	"github.com/apache/arrow/go/v14/parquet/schema"
    30  	"golang.org/x/xerrors"
    31  )
    32  
    33  const (
    34  	// 4 MB is the default maximum page header size
    35  	defaultMaxPageHeaderSize = 4 * 1024 * 1024
    36  	// 16 KB is the default expected page header size
    37  	defaultPageHeaderSize = 16 * 1024
    38  )
    39  
    40  //go:generate go run ../../arrow/_tools/tmpl/main.go -i -data=../internal/encoding/physical_types.tmpldata column_reader_types.gen.go.tmpl
    41  
    42  func isDictIndexEncoding(e format.Encoding) bool {
    43  	return e == format.Encoding_RLE_DICTIONARY || e == format.Encoding_PLAIN_DICTIONARY
    44  }
    45  
    46  // CryptoContext is a context for keeping track of the current methods for decrypting.
    47  // It keeps track of the row group and column numbers along with references to the
    48  // decryptor objects.
    49  type CryptoContext struct {
    50  	StartDecryptWithDictionaryPage bool
    51  	RowGroupOrdinal                int16
    52  	ColumnOrdinal                  int16
    53  	MetaDecryptor                  encryption.Decryptor
    54  	DataDecryptor                  encryption.Decryptor
    55  }
    56  
    57  // ColumnChunkReader is the basic interface for all column readers. It will use
    58  // a page reader to read all the pages in a column chunk from a row group.
    59  //
    60  // To actually Read out the column data, you need to convert to the properly
    61  // typed ColumnChunkReader type such as *BooleanColumnReader etc.
    62  //
    63  // Some things to clarify when working with column readers:
    64  //
    65  // "Values" refers to the physical data values in a data page.
    66  //
    67  // This is separate from the number of "rows" in a column and the total number
    68  // of "elements" in a column because null values aren't stored physically in the
    69  // data page but are represented via definition levels, so the number of values
    70  // in a column can be less than the number of rows.
    71  //
    72  // The total number of "elements" in a column also differs because of potential
    73  // repeated fields, where you can have multiple values in the page which
    74  // together make up a single element (such as a list) or depending on the repetition
    75  // level and definition level, could represent an entire null list or just a null
    76  // element inside of a list.
    77  type ColumnChunkReader interface {
    78  	// HasNext returns whether there is more data to be read in this column
    79  	// and row group.
    80  	HasNext() bool
    81  	// Type returns the underlying physical type of the column
    82  	Type() parquet.Type
    83  	// Descriptor returns the column schema container
    84  	Descriptor() *schema.Column
    85  	// if HasNext returns false because of an error, this will return the error
    86  	// it encountered. Otherwise this will be nil if it's just the end of the
    87  	// column
    88  	Err() error
    89  	// Skip buffered values
    90  	consumeBufferedValues(int64)
    91  	// number of available buffered values that have not been decoded yet
    92  	// when this returns 0, you're at the end of a page.
    93  	numAvailValues() int64
    94  	// read the definition levels and return the number of definitions,
    95  	// and the number of values to be read (number of def levels == maxdef level)
    96  	// it also populates the passed in slice which should be sized appropriately.
    97  	readDefinitionLevels(levels []int16) (int, int64)
    98  	// read the repetition levels and return the number of repetition levels read
    99  	// also populates the passed in slice, which should be sized appropriately.
   100  	readRepetitionLevels(levels []int16) int
   101  	// a column is made up of potentially multiple pages across potentially multiple
   102  	// row groups. A PageReader allows looping through the pages in a single row group.
   103  	// When moving to another row group for reading, use setPageReader to re-use the
   104  	// column reader for reading the pages of the new row group.
   105  	pager() PageReader
   106  	// set a page reader into the columnreader so it can be reused.
   107  	//
   108  	// This will clear any current error in the reader but does not
   109  	// automatically read the first page of the page reader passed in until
   110  	// HasNext which will read in the next page.
   111  	setPageReader(PageReader)
   112  }
   113  
   114  type columnChunkReader struct {
   115  	descr             *schema.Column
   116  	rdr               PageReader
   117  	repetitionDecoder encoding.LevelDecoder
   118  	definitionDecoder encoding.LevelDecoder
   119  
   120  	curPage     Page
   121  	curEncoding format.Encoding
   122  	curDecoder  encoding.TypedDecoder
   123  
   124  	// number of currently buffered values in the current page
   125  	numBuffered int64
   126  	// the number of values we've decoded so far
   127  	numDecoded int64
   128  	mem        memory.Allocator
   129  	bufferPool *sync.Pool
   130  
   131  	decoders      map[format.Encoding]encoding.TypedDecoder
   132  	decoderTraits encoding.DecoderTraits
   133  
   134  	// is set when an error is encountered
   135  	err          error
   136  	defLvlBuffer []int16
   137  
   138  	newDictionary bool
   139  }
   140  
   141  // NewColumnReader returns a column reader for the provided column initialized with the given pagereader that will
   142  // provide the pages of data for this column. The type is determined from the column passed in.
   143  //
   144  // In addition to the page reader and allocator, a pointer to a shared sync.Pool is expected to provide buffers for temporary
   145  // usage to minimize allocations. The bufferPool should provide *memory.Buffer objects that can be resized as necessary, buffers
   146  // should have `ResizeNoShrink(0)` called on them before being put back into the pool.
   147  func NewColumnReader(descr *schema.Column, pageReader PageReader, mem memory.Allocator, bufferPool *sync.Pool) ColumnChunkReader {
   148  	base := columnChunkReader{descr: descr, rdr: pageReader, mem: mem, decoders: make(map[format.Encoding]encoding.TypedDecoder), bufferPool: bufferPool}
   149  	switch descr.PhysicalType() {
   150  	case parquet.Types.FixedLenByteArray:
   151  		base.decoderTraits = &encoding.FixedLenByteArrayDecoderTraits
   152  		return &FixedLenByteArrayColumnChunkReader{base}
   153  	case parquet.Types.Float:
   154  		base.decoderTraits = &encoding.Float32DecoderTraits
   155  		return &Float32ColumnChunkReader{base}
   156  	case parquet.Types.Double:
   157  		base.decoderTraits = &encoding.Float64DecoderTraits
   158  		return &Float64ColumnChunkReader{base}
   159  	case parquet.Types.ByteArray:
   160  		base.decoderTraits = &encoding.ByteArrayDecoderTraits
   161  		return &ByteArrayColumnChunkReader{base}
   162  	case parquet.Types.Int32:
   163  		base.decoderTraits = &encoding.Int32DecoderTraits
   164  		return &Int32ColumnChunkReader{base}
   165  	case parquet.Types.Int64:
   166  		base.decoderTraits = &encoding.Int64DecoderTraits
   167  		return &Int64ColumnChunkReader{base}
   168  	case parquet.Types.Int96:
   169  		base.decoderTraits = &encoding.Int96DecoderTraits
   170  		return &Int96ColumnChunkReader{base}
   171  	case parquet.Types.Boolean:
   172  		base.decoderTraits = &encoding.BooleanDecoderTraits
   173  		return &BooleanColumnChunkReader{base}
   174  	}
   175  	return nil
   176  }
   177  
   178  func (c *columnChunkReader) Err() error                    { return c.err }
   179  func (c *columnChunkReader) Type() parquet.Type            { return c.descr.PhysicalType() }
   180  func (c *columnChunkReader) Descriptor() *schema.Column    { return c.descr }
   181  func (c *columnChunkReader) consumeBufferedValues(n int64) { c.numDecoded += n }
   182  func (c *columnChunkReader) numAvailValues() int64         { return c.numBuffered - c.numDecoded }
   183  func (c *columnChunkReader) pager() PageReader             { return c.rdr }
   184  func (c *columnChunkReader) setPageReader(rdr PageReader) {
   185  	c.rdr, c.err = rdr, nil
   186  	c.decoders = make(map[format.Encoding]encoding.TypedDecoder)
   187  	c.numBuffered, c.numDecoded = 0, 0
   188  }
   189  
   190  func (c *columnChunkReader) getDefLvlBuffer(sz int64) []int16 {
   191  	if int64(len(c.defLvlBuffer)) < sz {
   192  		c.defLvlBuffer = make([]int16, sz)
   193  		return c.defLvlBuffer
   194  	}
   195  
   196  	return c.defLvlBuffer[:sz]
   197  }
   198  
   199  // HasNext returns whether there is more data to be read in this column
   200  // and row group.
   201  func (c *columnChunkReader) HasNext() bool {
   202  	if c.numBuffered == 0 || c.numDecoded == c.numBuffered {
   203  		return c.readNewPage() && c.numBuffered != 0
   204  	}
   205  	return true
   206  }
   207  
   208  func (c *columnChunkReader) configureDict(page *DictionaryPage) error {
   209  	enc := page.encoding
   210  	if enc == format.Encoding_PLAIN_DICTIONARY || enc == format.Encoding_PLAIN {
   211  		enc = format.Encoding_RLE_DICTIONARY
   212  	}
   213  
   214  	if _, ok := c.decoders[enc]; ok {
   215  		return xerrors.New("parquet: column chunk cannot have more than one dictionary.")
   216  	}
   217  
   218  	switch page.Encoding() {
   219  	case format.Encoding_PLAIN, format.Encoding_PLAIN_DICTIONARY:
   220  		dict := c.decoderTraits.Decoder(parquet.Encodings.Plain, c.descr, false, c.mem)
   221  		dict.SetData(int(page.NumValues()), page.Data())
   222  
   223  		decoder := c.decoderTraits.Decoder(parquet.Encodings.Plain, c.descr, true, c.mem).(encoding.DictDecoder)
   224  		decoder.SetDict(dict)
   225  		c.decoders[enc] = decoder
   226  	default:
   227  		return xerrors.New("parquet: dictionary index must be plain encoding")
   228  	}
   229  
   230  	c.newDictionary = true
   231  	c.curDecoder = c.decoders[enc]
   232  	return nil
   233  }
   234  
   235  // read a new page from the page reader
   236  func (c *columnChunkReader) readNewPage() bool {
   237  	for c.rdr.Next() { // keep going until we get a data page
   238  		c.curPage = c.rdr.Page()
   239  		if c.curPage == nil {
   240  			break
   241  		}
   242  
   243  		var lvlByteLen int64
   244  		switch p := c.curPage.(type) {
   245  		case *DictionaryPage:
   246  			if err := c.configureDict(p); err != nil {
   247  				c.err = err
   248  				return false
   249  			}
   250  			continue
   251  		case *DataPageV1:
   252  			lvlByteLen, c.err = c.initLevelDecodersV1(p, p.repLvlEncoding, p.defLvlEncoding)
   253  			if c.err != nil {
   254  				return false
   255  			}
   256  		case *DataPageV2:
   257  			lvlByteLen, c.err = c.initLevelDecodersV2(p)
   258  			if c.err != nil {
   259  				return false
   260  			}
   261  		default:
   262  			// we can skip non-data pages
   263  			continue
   264  		}
   265  
   266  		c.err = c.initDataDecoder(c.curPage, lvlByteLen)
   267  		return c.err == nil
   268  	}
   269  	c.err = c.rdr.Err()
   270  	return false
   271  }
   272  
   273  func (c *columnChunkReader) initLevelDecodersV2(page *DataPageV2) (int64, error) {
   274  	c.numBuffered = int64(page.nvals)
   275  	c.numDecoded = 0
   276  	buf := page.Data()
   277  	totalLvlLen := int64(page.repLvlByteLen) + int64(page.defLvlByteLen)
   278  
   279  	if totalLvlLen > int64(len(buf)) {
   280  		return totalLvlLen, xerrors.New("parquet: data page too small for levels (corrupt header?)")
   281  	}
   282  
   283  	if c.descr.MaxRepetitionLevel() > 0 {
   284  		c.repetitionDecoder.SetDataV2(page.repLvlByteLen, c.descr.MaxRepetitionLevel(), int(c.numBuffered), buf)
   285  	}
   286  	// ARROW-17453: Some writers will write repetition levels even when
   287  	// the max repetition level is 0, so we should respect the value
   288  	// in the page header regardless of whether MaxRepetitionLevel is 0
   289  	// or not.
   290  	buf = buf[page.repLvlByteLen:]
   291  
   292  	if c.descr.MaxDefinitionLevel() > 0 {
   293  		c.definitionDecoder.SetDataV2(page.defLvlByteLen, c.descr.MaxDefinitionLevel(), int(c.numBuffered), buf)
   294  	}
   295  
   296  	return totalLvlLen, nil
   297  }
   298  
   299  func (c *columnChunkReader) initLevelDecodersV1(page *DataPageV1, repLvlEncoding, defLvlEncoding format.Encoding) (int64, error) {
   300  	c.numBuffered = int64(page.nvals)
   301  	c.numDecoded = 0
   302  
   303  	buf := page.Data()
   304  	maxSize := len(buf)
   305  	levelsByteLen := int64(0)
   306  
   307  	// Data page layout: Repetition Levels - Definition Levels - encoded values.
   308  	// Levels are encoded as rle or bit-packed
   309  	if c.descr.MaxRepetitionLevel() > 0 {
   310  		repBytes, err := c.repetitionDecoder.SetData(parquet.Encoding(repLvlEncoding), c.descr.MaxRepetitionLevel(), int(c.numBuffered), buf)
   311  		if err != nil {
   312  			return levelsByteLen, err
   313  		}
   314  		buf = buf[repBytes:]
   315  		maxSize -= repBytes
   316  		levelsByteLen += int64(repBytes)
   317  	}
   318  
   319  	if c.descr.MaxDefinitionLevel() > 0 {
   320  		defBytes, err := c.definitionDecoder.SetData(parquet.Encoding(defLvlEncoding), c.descr.MaxDefinitionLevel(), int(c.numBuffered), buf)
   321  		if err != nil {
   322  			return levelsByteLen, err
   323  		}
   324  		levelsByteLen += int64(defBytes)
   325  		maxSize -= defBytes
   326  	}
   327  
   328  	return levelsByteLen, nil
   329  }
   330  
   331  func (c *columnChunkReader) initDataDecoder(page Page, lvlByteLen int64) error {
   332  	buf := page.Data()
   333  	if int64(len(buf)) < lvlByteLen {
   334  		return xerrors.New("parquet: page smaller than size of encoded levels")
   335  	}
   336  
   337  	buf = buf[lvlByteLen:]
   338  	encoding := page.Encoding()
   339  
   340  	if isDictIndexEncoding(encoding) {
   341  		encoding = format.Encoding_RLE_DICTIONARY
   342  	}
   343  
   344  	if decoder, ok := c.decoders[encoding]; ok {
   345  		c.curDecoder = decoder
   346  	} else {
   347  		switch encoding {
   348  		case format.Encoding_PLAIN,
   349  			format.Encoding_DELTA_BYTE_ARRAY,
   350  			format.Encoding_DELTA_LENGTH_BYTE_ARRAY,
   351  			format.Encoding_DELTA_BINARY_PACKED:
   352  			c.curDecoder = c.decoderTraits.Decoder(parquet.Encoding(encoding), c.descr, false, c.mem)
   353  			c.decoders[encoding] = c.curDecoder
   354  		case format.Encoding_RLE_DICTIONARY:
   355  			return xerrors.New("parquet: dictionary page must be before data page")
   356  		case format.Encoding_BYTE_STREAM_SPLIT:
   357  			return fmt.Errorf("parquet: unsupported data encoding %s", encoding)
   358  		default:
   359  			return fmt.Errorf("parquet: unknown encoding type %s", encoding)
   360  		}
   361  	}
   362  
   363  	c.curEncoding = encoding
   364  	c.curDecoder.SetData(int(c.numBuffered), buf)
   365  	return nil
   366  }
   367  
   368  // readDefinitionLevels decodes the definition levels from the page and returns
   369  // it returns the total number of levels that were decoded (and thus populated
   370  // in the passed in slice) and the number of physical values that exist to read
   371  // (the number of levels that are equal to the max definition level).
   372  //
   373  // If the max definition level is 0, the assumption is that there no nulls in the
   374  // column and therefore no definition levels to read, so it will always return 0, 0
   375  func (c *columnChunkReader) readDefinitionLevels(levels []int16) (totalDecoded int, valuesToRead int64) {
   376  	if c.descr.MaxDefinitionLevel() == 0 {
   377  		return 0, 0
   378  	}
   379  
   380  	return c.definitionDecoder.Decode(levels)
   381  }
   382  
   383  // readRepetitionLevels decodes the repetition levels from the page and returns
   384  // the total number of values decoded (and thus populated in the passed in levels
   385  // slice).
   386  //
   387  // If max repetition level is 0, it is assumed there are no repetition levels,
   388  // and thus will always return 0.
   389  func (c *columnChunkReader) readRepetitionLevels(levels []int16) int {
   390  	if c.descr.MaxRepetitionLevel() == 0 {
   391  		return 0
   392  	}
   393  
   394  	nlevels, _ := c.repetitionDecoder.Decode(levels)
   395  	return nlevels
   396  }
   397  
   398  // determineNumToRead reads the definition levels (and optionally populates the repetition levels)
   399  // in order to determine how many values need to be read to fulfill this batch read.
   400  //
   401  // batchLen is the number of values it is desired to read. defLvls must be either nil (in which case
   402  // a buffer will be used) or must be at least batchLen in length to be safe. repLvls should be either nil
   403  // (in which case it is ignored) or should be at least batchLen in length to be safe.
   404  //
   405  // In the return values: ndef is the number of definition levels that were actually read in which will
   406  // typically be the minimum of batchLen and numAvailValues.
   407  // toRead is the number of physical values that should be read in based on the definition levels (the number
   408  // of definition levels that were equal to maxDefinitionLevel). and err being either nil or any error encountered
   409  func (c *columnChunkReader) determineNumToRead(batchLen int64, defLvls, repLvls []int16) (ndefs int, toRead int64, err error) {
   410  	if !c.HasNext() {
   411  		return 0, 0, c.err
   412  	}
   413  
   414  	size := utils.Min(batchLen, c.numBuffered-c.numDecoded)
   415  
   416  	if c.descr.MaxDefinitionLevel() > 0 {
   417  		if defLvls == nil {
   418  			defLvls = c.getDefLvlBuffer(size)
   419  		}
   420  		ndefs, toRead = c.readDefinitionLevels(defLvls[:size])
   421  	} else {
   422  		toRead = size
   423  	}
   424  
   425  	if c.descr.MaxRepetitionLevel() > 0 && repLvls != nil {
   426  		nreps := c.readRepetitionLevels(repLvls[:size])
   427  		if defLvls != nil && ndefs != nreps {
   428  			err = xerrors.New("parquet: number of decoded rep/def levels did not match")
   429  		}
   430  	}
   431  	return
   432  }
   433  
   434  // skipValues some number of rows using readFn as the function to read the data and throw it away.
   435  // If we can skipValues a whole page based on its metadata, then we do so, otherwise we read the
   436  // page until we have skipped the number of rows desired.
   437  func (c *columnChunkReader) skipValues(nvalues int64, readFn func(batch int64, buf []byte) (int64, error)) (int64, error) {
   438  	var err error
   439  	toskip := nvalues
   440  	for c.HasNext() && toskip > 0 {
   441  		// if number to skip is more than the number of undecoded values, skip the page
   442  		if toskip > (c.numBuffered - c.numDecoded) {
   443  			toskip -= c.numBuffered - c.numDecoded
   444  			c.numDecoded = c.numBuffered
   445  		} else {
   446  			var (
   447  				batchSize int64 = 1024
   448  				valsRead  int64 = 0
   449  			)
   450  
   451  			scratch := c.bufferPool.Get().(*memory.Buffer)
   452  			defer func() {
   453  				scratch.ResizeNoShrink(0)
   454  				c.bufferPool.Put(scratch)
   455  			}()
   456  			bufMult := 1
   457  			if c.descr.PhysicalType() == parquet.Types.Boolean {
   458  				// for bools, BytesRequired returns 1 byte per 8 bool, but casting []byte to []bool requires 1 byte per 1 bool
   459  				bufMult = 8
   460  			}
   461  			scratch.Reserve(c.decoderTraits.BytesRequired(int(batchSize) * bufMult))
   462  
   463  			for {
   464  				batchSize = utils.Min(batchSize, toskip)
   465  				valsRead, err = readFn(batchSize, scratch.Buf())
   466  				toskip -= valsRead
   467  				if valsRead <= 0 || toskip <= 0 || err != nil {
   468  					break
   469  				}
   470  			}
   471  		}
   472  	}
   473  	if c.err != nil {
   474  		err = c.err
   475  	}
   476  	return nvalues - toskip, err
   477  }
   478  
   479  type readerFunc func(int64, int64) (int, error)
   480  
   481  // base function for reading a batch of values, this will read until it either reads in batchSize values or
   482  // it hits the end of the column chunk, including reading multiple pages.
   483  //
   484  // totalValues is the total number of values which were read in, and thus would be the total number
   485  // of definition levels and repetition levels which were populated (if they were non-nil). totalRead
   486  // is the number of physical values that were read in (ie: the number of non-null values)
   487  func (c *columnChunkReader) readBatch(batchSize int64, defLvls, repLvls []int16, readFn readerFunc) (totalLvls int64, totalRead int, err error) {
   488  	var (
   489  		read   int
   490  		defs   []int16
   491  		reps   []int16
   492  		ndefs  int
   493  		toRead int64
   494  	)
   495  
   496  	for c.HasNext() && totalLvls < batchSize && err == nil {
   497  		if defLvls != nil {
   498  			defs = defLvls[totalLvls:]
   499  		}
   500  		if repLvls != nil {
   501  			reps = repLvls[totalLvls:]
   502  		}
   503  		ndefs, toRead, err = c.determineNumToRead(batchSize-totalLvls, defs, reps)
   504  		if err != nil {
   505  			return totalLvls, totalRead, err
   506  		}
   507  
   508  		read, err = readFn(int64(totalRead), toRead)
   509  		// the total number of values processed here is the maximum of
   510  		// the number of definition levels or the number of physical values read.
   511  		// if this is a required field, ndefs will be 0 since there is no definition
   512  		// levels stored with it and `read` will be the number of values, otherwise
   513  		// we use ndefs since it will be equal to or greater than read.
   514  		totalVals := int64(utils.MaxInt(ndefs, read))
   515  		c.consumeBufferedValues(totalVals)
   516  
   517  		totalLvls += totalVals
   518  		totalRead += read
   519  	}
   520  	return totalLvls, totalRead, err
   521  }