github.com/vc42/parquet-go@v0.0.0-20240320194221-1a9adb5f23f5/column_chunk.go (about)

     1  package parquet
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  )
     7  
     8  // The ColumnChunk interface represents individual columns of a row group.
     9  type ColumnChunk interface {
    10  	// Returns the column type.
    11  	Type() Type
    12  
    13  	// Returns the index of this column in its parent row group.
    14  	Column() int
    15  
    16  	// Returns a reader exposing the pages of the column.
    17  	Pages() Pages
    18  
    19  	// Returns the components of the page index for this column chunk,
    20  	// containing details about the content and location of pages within the
    21  	// chunk.
    22  	//
    23  	// Note that the returned value may be the same across calls to these
    24  	// methods, programs must treat those as read-only.
    25  	//
    26  	// If the column chunk does not have a page index, the methods return nil.
    27  	ColumnIndex() ColumnIndex
    28  	OffsetIndex() OffsetIndex
    29  	BloomFilter() BloomFilter
    30  
    31  	// Returns the number of values in the column chunk.
    32  	//
    33  	// This quantity may differ from the number of rows in the parent row group
    34  	// because repeated columns may hold zero or more values per row.
    35  	NumValues() int64
    36  }
    37  
    38  type pageAndValueWriter interface {
    39  	PageWriter
    40  	ValueWriter
    41  }
    42  
    43  type columnChunkReader struct {
    44  	// These two fields must be configured to initialize the reader.
    45  	buffer []Value     // buffer holding values read from the pages
    46  	offset int         // offset of the next value in the buffer
    47  	reader Pages       // reader of column pages
    48  	values ValueReader // reader for values from the current page
    49  }
    50  
    51  func (r *columnChunkReader) buffered() int {
    52  	return len(r.buffer) - r.offset
    53  }
    54  
    55  func (r *columnChunkReader) reset() {
    56  	clearValues(r.buffer)
    57  	r.buffer = r.buffer[:0]
    58  	r.offset = 0
    59  	r.values = nil
    60  }
    61  
    62  func (r *columnChunkReader) close() (err error) {
    63  	r.reset()
    64  	return r.reader.Close()
    65  }
    66  
    67  func (r *columnChunkReader) seekToRow(rowIndex int64) error {
    68  	// TODO: there are a few optimizations we can make here:
    69  	// * is the row buffered already? => advance the offset
    70  	// * is the row in the current page? => seek in values
    71  	r.reset()
    72  	return r.reader.SeekToRow(rowIndex)
    73  }
    74  
    75  func (r *columnChunkReader) readValues() error {
    76  	if r.offset < len(r.buffer) {
    77  		return nil
    78  	}
    79  	if r.values == nil {
    80  		for {
    81  			p, err := r.reader.ReadPage()
    82  			if err != nil {
    83  				return err
    84  			}
    85  			if p.NumValues() > 0 {
    86  				r.values = p.Values()
    87  				break
    88  			}
    89  		}
    90  	}
    91  	n, err := r.values.ReadValues(r.buffer[:cap(r.buffer)])
    92  	if errors.Is(err, io.EOF) {
    93  		r.values = nil
    94  	}
    95  	if n > 0 {
    96  		err = nil
    97  	}
    98  	r.buffer = r.buffer[:n]
    99  	r.offset = 0
   100  	return err
   101  }
   102  
   103  /*
   104  func (r *columnChunkReader) writeBufferedRowsTo(w pageAndValueWriter, rowCount int64) (numRows int64, err error) {
   105  	if rowCount == 0 {
   106  		return 0, nil
   107  	}
   108  
   109  	for {
   110  		for r.offset < len(r.buffer) {
   111  			values := r.buffer[r.offset:]
   112  			// We can only determine that the full row has been consumed if we
   113  			// have more values in the buffer, and the next value is the start
   114  			// of a new row. Otherwise, we have to load more values from the
   115  			// page, which may yield EOF if all values have been consumed, in
   116  			// which case we know that we have read the full row, and otherwise
   117  			// we will enter this check again on the next loop iteration.
   118  			if numRows == rowCount {
   119  				if values[0].repetitionLevel == 0 {
   120  					return numRows, nil
   121  				}
   122  				values, _ = splitRowValues(values)
   123  			} else {
   124  				values = limitRowValues(values, int(rowCount-numRows))
   125  			}
   126  
   127  			n, err := w.WriteValues(values)
   128  			numRows += int64(countRowsOf(values[:n]))
   129  			r.offset += n
   130  			if err != nil {
   131  				return numRows, err
   132  			}
   133  		}
   134  
   135  		if err := r.readValuesFromCurrentPage(); err != nil {
   136  			if err == io.EOF {
   137  				err = nil
   138  			}
   139  			return numRows, err
   140  		}
   141  	}
   142  }
   143  
   144  func (r *columnChunkReader) writeRowsTo(w pageAndValueWriter, limit int64) (numRows int64, err error) {
   145  	for numRows < limit {
   146  		if r.values != nil {
   147  			n, err := r.writeBufferedRowsTo(w, numRows-limit)
   148  			numRows += n
   149  			if err != nil || numRows == limit {
   150  				return numRows, err
   151  			}
   152  		}
   153  
   154  		r.buffer = r.buffer[:0]
   155  		r.offset = 0
   156  
   157  		for numRows < limit {
   158  			p, err := r.reader.ReadPage()
   159  			if err != nil {
   160  				return numRows, err
   161  			}
   162  
   163  			pageRows := int64(p.NumRows())
   164  			// When the page is fully contained in the remaining range of rows
   165  			// that we intend to copy, we can use an optimized page copy rather
   166  			// than writing rows one at a time.
   167  			//
   168  			// Data pages v1 do not expose the number of rows available, which
   169  			// means we cannot take the optimized page copy path in those cases.
   170  			if pageRows == 0 || int64(pageRows) > limit {
   171  				r.values = p.Values()
   172  				err := r.readValuesFromCurrentPage()
   173  				if err == nil {
   174  					// More values have been buffered, break out of the inner loop
   175  					// to go back to the beginning of the outer loop and write
   176  					// buffered values to the output.
   177  					break
   178  				}
   179  				if errors.Is(err, io.EOF) {
   180  					// The page contained no values? Unclear if this is valid but
   181  					// we can handle it by reading the next page.
   182  					r.values = nil
   183  					continue
   184  				}
   185  				return numRows, err
   186  			}
   187  
   188  			if _, err := w.WritePage(p); err != nil {
   189  				return numRows, err
   190  			}
   191  
   192  			numRows += pageRows
   193  		}
   194  	}
   195  	return numRows, nil
   196  }
   197  */
   198  
   199  type readRowsFunc func([]Row, byte, []columnChunkReader) (int, error)
   200  
   201  func readRowsFuncOf(node Node, columnIndex int, repetitionDepth byte) (int, readRowsFunc) {
   202  	var read readRowsFunc
   203  
   204  	if node.Repeated() {
   205  		repetitionDepth++
   206  	}
   207  
   208  	if node.Leaf() {
   209  		columnIndex, read = readRowsFuncOfLeaf(columnIndex, repetitionDepth)
   210  	} else {
   211  		columnIndex, read = readRowsFuncOfGroup(node, columnIndex, repetitionDepth)
   212  	}
   213  
   214  	if node.Repeated() {
   215  		read = readRowsFuncOfRepeated(read, repetitionDepth)
   216  	}
   217  
   218  	return columnIndex, read
   219  }
   220  
   221  //go:noinline
   222  func readRowsFuncOfRepeated(read readRowsFunc, repetitionDepth byte) readRowsFunc {
   223  	return func(rows []Row, repetitionLevel byte, columns []columnChunkReader) (int, error) {
   224  		for i := range rows {
   225  			// Repeated columns have variable number of values, we must process
   226  			// them one row at a time because we cannot predict how many values
   227  			// need to be consumed in each iteration.
   228  			row := rows[i : i+1]
   229  
   230  			// The first pass looks for values marking the beginning of a row by
   231  			// having a repetition level equal to the current level.
   232  			n, err := read(row, repetitionLevel, columns)
   233  			if err != nil {
   234  				// The error here may likely be io.EOF, the read function may
   235  				// also have successfully read a row, which is indicated by a
   236  				// non-zero count. In this case, we increment the index to
   237  				// indicate to the caller than rows up to i+1 have been read.
   238  				if n > 0 {
   239  					i++
   240  				}
   241  				return i, err
   242  			}
   243  
   244  			// The read function may return no errors and also read no rows in
   245  			// case where it had more values to read but none corresponded to
   246  			// the current repetition level. This is an indication that we will
   247  			// not be able to read more rows at this stage, we must return to
   248  			// the caller to let it set the repetition level to its current
   249  			// depth, which may allow us to read more values when called again.
   250  			if n == 0 {
   251  				return i, nil
   252  			}
   253  
   254  			// When we reach this stage, we have successfully read the first
   255  			// values of a row of repeated columns. We continue consuming more
   256  			// repeated values until we get the indication that we consumed
   257  			// them all (the read function returns zero and no errors).
   258  			for {
   259  				n, err := read(row, repetitionDepth, columns)
   260  				if err != nil {
   261  					return i + 1, err
   262  				}
   263  				if n == 0 {
   264  					break
   265  				}
   266  			}
   267  		}
   268  		return len(rows), nil
   269  	}
   270  }
   271  
   272  //go:noinline
   273  func readRowsFuncOfGroup(node Node, columnIndex int, repetitionDepth byte) (int, readRowsFunc) {
   274  	fields := node.Fields()
   275  
   276  	if len(fields) == 0 {
   277  		return columnIndex, func(_ []Row, _ byte, _ []columnChunkReader) (int, error) {
   278  			return 0, io.EOF
   279  		}
   280  	}
   281  
   282  	if len(fields) == 1 {
   283  		// Small optimization for a somewhat common case of groups with a single
   284  		// column (like nested list elements for example); there is no need to
   285  		// loop over the group of a single element, we can simply skip to calling
   286  		// the inner read function.
   287  		return readRowsFuncOf(fields[0], columnIndex, repetitionDepth)
   288  	}
   289  
   290  	group := make([]readRowsFunc, len(fields))
   291  	for i := range group {
   292  		columnIndex, group[i] = readRowsFuncOf(fields[i], columnIndex, repetitionDepth)
   293  	}
   294  
   295  	return columnIndex, func(rows []Row, repetitionLevel byte, columns []columnChunkReader) (int, error) {
   296  		// When reading a group, we use the first column as an indicator of how
   297  		// may rows can be read during this call.
   298  		n, err := group[0](rows, repetitionLevel, columns)
   299  
   300  		if n > 0 {
   301  			// Read values for all rows that the group is able to consume.
   302  			// Getting io.EOF from calling the read functions indicate that
   303  			// we consumed all values of that particular column, but there may
   304  			// be more to read in other columns, therefore we must always read
   305  			// all columns and cannot stop on the first error.
   306  			for _, read := range group[1:] {
   307  				_, err2 := read(rows[:n], repetitionLevel, columns)
   308  				if err2 != nil && !errors.Is(err2, io.EOF) {
   309  					return 0, err2
   310  				}
   311  			}
   312  		}
   313  
   314  		return n, err
   315  	}
   316  }
   317  
   318  //go:noinline
   319  func readRowsFuncOfLeaf(columnIndex int, repetitionDepth byte) (int, readRowsFunc) {
   320  	var read readRowsFunc
   321  
   322  	if repetitionDepth == 0 {
   323  		read = func(rows []Row, _ byte, columns []columnChunkReader) (int, error) {
   324  			// When the repetition depth is zero, we know that there is exactly
   325  			// one value per row for this column, and therefore we can consume
   326  			// as many values as there are rows to fill.
   327  			col := &columns[columnIndex]
   328  
   329  			for n := 0; n < len(rows); {
   330  				if col.offset < len(col.buffer) {
   331  					rows[n] = append(rows[n], col.buffer[col.offset])
   332  					n++
   333  					col.offset++
   334  					continue
   335  				}
   336  				if err := col.readValues(); err != nil {
   337  					return n, err
   338  				}
   339  			}
   340  
   341  			return len(rows), nil
   342  		}
   343  	} else {
   344  		read = func(rows []Row, repetitionLevel byte, columns []columnChunkReader) (int, error) {
   345  			// When the repetition depth is not zero, we know that we will be
   346  			// called with a single row as input. We attempt to read at most one
   347  			// value of a single row and return to the caller.
   348  			col := &columns[columnIndex]
   349  
   350  			for {
   351  				if col.offset < len(col.buffer) {
   352  					if col.buffer[col.offset].repetitionLevel != repetitionLevel {
   353  						return 0, nil
   354  					}
   355  					rows[0] = append(rows[0], col.buffer[col.offset])
   356  					col.offset++
   357  					return 1, nil
   358  				}
   359  				if err := col.readValues(); err != nil {
   360  					return 0, err
   361  				}
   362  			}
   363  		}
   364  	}
   365  
   366  	return columnIndex + 1, read
   367  }