package parquetimport ()var (ErrMissingColumnIndex = errors.New("missing column index")ErrMissingOffsetIndex = errors.New("missing offset index"))// The ColumnChunk interface represents individual columns of a row group.typeColumnChunkinterface {// Returns the column type.Type() Type// Returns the index of this column in its parent row group.Column() int// Returns a reader exposing the pages of the column.Pages() Pages// Returns the components of the page index for this column chunk, // containing details about the content and location of pages within the // chunk. // // Note that the returned value may be the same across calls to these // methods, programs must treat those as read-only. // // If the column chunk does not have a column or offset index, the methods return // ErrMissingColumnIndex or ErrMissingOffsetIndex respectively. // // Prior to v0.20, these methods did not return an error because the page index // for a file was either fully read when the file was opened, or skipped // completely using the parquet.SkipPageIndex option. Version v0.20 introduced a // change that the page index can be read on-demand at any time, even if a file // was opened with the parquet.SkipPageIndex option. Since reading the page index // can fail, these methods now return an error.ColumnIndex() (ColumnIndex, error)OffsetIndex() (OffsetIndex, error)BloomFilter() BloomFilter// Returns the number of values in the column chunk. // // This quantity may differ from the number of rows in the parent row group // because repeated columns may hold zero or more values per row.NumValues() int64}type pageAndValueWriter interface {PageWriterValueWriter}type readRowsFunc func(*rowGroupRows, []Row, byte) (int, error)func readRowsFuncOf( Node, int, byte) (int, readRowsFunc) {varreadRowsFuncif .Repeated() { ++ }if .Leaf() { , = readRowsFuncOfLeaf(, ) } else { , = readRowsFuncOfGroup(, , ) }if .Repeated() { = readRowsFuncOfRepeated(, ) }return , }//go:noinlinefunc readRowsFuncOfRepeated( readRowsFunc, byte) readRowsFunc {returnfunc( *rowGroupRows, []Row, byte) (int, error) {for := range {// Repeated columns have variable number of values, we must process // them one row at a time because we cannot predict how many values // need to be consumed in each iteration. := [ : +1]// The first pass looks for values marking the beginning of a row by // having a repetition level equal to the current level. , := (, , )if != nil {// The error here may likely be io.EOF, the read function may // also have successfully read a row, which is indicated by a // non-zero count. In this case, we increment the index to // indicate to the caller than rows up to i+1 have been read.if > 0 { ++ }return , }// The read function may return no errors and also read no rows in // case where it had more values to read but none corresponded to // the current repetition level. This is an indication that we will // not be able to read more rows at this stage, we must return to // the caller to let it set the repetition level to its current // depth, which may allow us to read more values when called again.if == 0 {return , nil }// When we reach this stage, we have successfully read the first // values of a row of repeated columns. We continue consuming more // repeated values until we get the indication that we consumed // them all (the read function returns zero and no errors).for { , := (, , )if != nil {return + 1, }if == 0 {break } } }returnlen(), nil }}//go:noinlinefunc readRowsFuncOfGroup( Node, int, byte) (int, readRowsFunc) { := .Fields()iflen() == 0 {return , func(*rowGroupRows, []Row, byte) (int, error) {return0, io.EOF } }iflen() == 1 {// Small optimization for a somewhat common case of groups with a single // column (like nested list elements for example); there is no need to // loop over the group of a single element, we can simply skip to calling // the inner read function.returnreadRowsFuncOf([0], , ) } := make([]readRowsFunc, len())for := range { , [] = readRowsFuncOf([], , ) }return , func( *rowGroupRows, []Row, byte) (int, error) {// When reading a group, we use the first column as an indicator of how // may rows can be read during this call. , := [0](, , )if > 0 {// Read values for all rows that the group is able to consume. // Getting io.EOF from calling the read functions indicate that // we consumed all values of that particular column, but there may // be more to read in other columns, therefore we must always read // all columns and cannot stop on the first error.for , := range [1:] { , := (, [:], )if != nil && != io.EOF {return0, } } }return , }}//go:noinlinefunc readRowsFuncOfLeaf( int, byte) (int, readRowsFunc) {varreadRowsFuncif == 0 { = func( *rowGroupRows, []Row, byte) (int, error) {// When the repetition depth is zero, we know that there is exactly // one value per row for this column, and therefore we can consume // as many values as there are rows to fill. := &.columns[] := .buffer()for := range {if .offset == .length { , := .values.ReadValues() .offset = 0 .length = int32()if == 0 && != nil {return0, } } [] = append([], [.offset]) .offset++ }returnlen(), nil } } else { = func( *rowGroupRows, []Row, byte) (int, error) {// When the repetition depth is not zero, we know that we will be // called with a single row as input. We attempt to read at most one // value of a single row and return to the caller. := &.columns[] := .buffer()if .offset == .length { , := .values.ReadValues() .offset = 0 .length = int32()if == 0 && != nil {return0, } }if [.offset].repetitionLevel != {return0, nil } [0] = append([0], [.offset]) .offset++return1, nil } }return + 1, }
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.