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 }