github.com/apache/arrow/go/v14@v14.0.2/parquet/file/column_writer.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 "bytes" 21 "encoding/binary" 22 "io" 23 24 "github.com/apache/arrow/go/v14/arrow" 25 "github.com/apache/arrow/go/v14/arrow/array" 26 "github.com/apache/arrow/go/v14/arrow/bitutil" 27 "github.com/apache/arrow/go/v14/arrow/memory" 28 "github.com/apache/arrow/go/v14/parquet" 29 "github.com/apache/arrow/go/v14/parquet/internal/encoding" 30 "github.com/apache/arrow/go/v14/parquet/metadata" 31 "github.com/apache/arrow/go/v14/parquet/schema" 32 ) 33 34 //go:generate go run ../../arrow/_tools/tmpl/main.go -i -data=../internal/encoding/physical_types.tmpldata column_writer_types.gen.go.tmpl 35 36 // ColumnChunkWriter is the base interface for all columnwriters. To directly write 37 // data to the column, you need to assert it to the correctly typed ColumnChunkWriter 38 // instance, such as Int32ColumnWriter. 39 type ColumnChunkWriter interface { 40 // Close ends this column and returns the number of bytes written 41 Close() error 42 // Type returns the underlying physical parquet type for this column 43 Type() parquet.Type 44 // Descr returns the column information for this writer 45 Descr() *schema.Column 46 // RowsWritten returns the number of rows that have so far been written with this writer 47 RowsWritten() int 48 // TotalCompressedBytes returns the number of bytes, after compression, that have been written so far 49 TotalCompressedBytes() int64 50 // TotalBytesWritten includes the bytes for writing dictionary pages, while TotalCompressedBytes is 51 // just the data and page headers 52 TotalBytesWritten() int64 53 // Properties returns the current WriterProperties in use for this writer 54 Properties() *parquet.WriterProperties 55 // CurrentEncoder returns the current encoder that is being used 56 // to encode new data written to this column 57 CurrentEncoder() encoding.TypedEncoder 58 // FallbackToPlain forces a dictionary encoded column writer to 59 // fallback to plain encoding, first flushing out any data it has 60 // and then changing the encoder to use plain encoding from 61 // here on out. 62 // 63 // This is automatically called if the dictionary reaches the 64 // limit in the write properties or under specific conditions. 65 // 66 // Has no effect if the column is not currently dictionary encoded. 67 FallbackToPlain() 68 // PageStatistics returns the current page statistics for this 69 // column writer. May be nil if stats are not enabled. 70 PageStatistics() metadata.TypedStatistics 71 // WriteDictIndices writes an arrow array of dictionary indices 72 // to this column. This should only be called by pqarrow or 73 // if you *really* know what you're doing. 74 WriteDictIndices(arrow.Array, []int16, []int16) error 75 76 LevelInfo() LevelInfo 77 SetBitsBuffer(*memory.Buffer) 78 HasBitsBuffer() bool 79 } 80 81 func computeLevelInfo(descr *schema.Column) (info LevelInfo) { 82 info.DefLevel = descr.MaxDefinitionLevel() 83 info.RepLevel = descr.MaxRepetitionLevel() 84 85 minSpacedDefLevel := descr.MaxDefinitionLevel() 86 n := descr.SchemaNode() 87 for n != nil && n.RepetitionType() != parquet.Repetitions.Repeated { 88 if n.RepetitionType() == parquet.Repetitions.Optional { 89 minSpacedDefLevel-- 90 } 91 n = n.Parent() 92 } 93 info.RepeatedAncestorDefLevel = minSpacedDefLevel 94 return 95 } 96 97 type columnWriter struct { 98 metaData *metadata.ColumnChunkMetaDataBuilder 99 descr *schema.Column 100 101 // scratch buffer if validity bits need to be recalculated 102 bitsBuffer *memory.Buffer 103 levelInfo LevelInfo 104 pager PageWriter 105 hasDict bool 106 encoding parquet.Encoding 107 props *parquet.WriterProperties 108 defEncoder encoding.LevelEncoder 109 repEncoder encoding.LevelEncoder 110 mem memory.Allocator 111 112 pageStatistics metadata.TypedStatistics 113 chunkStatistics metadata.TypedStatistics 114 115 // total number of values stored in the current data page. this is the maximum 116 // of the number of encoded def levels or encoded values. for 117 // non-repeated, required columns, this is equal to the number of encoded 118 // values. For repeated or optional values, there may be fewer data values 119 // than levels, and this tells you how many encoded levels there are in that case 120 numBufferedValues int64 121 122 // total number of rows stored in the current data page. This may be larger 123 // than numBufferedValues when writing a column with repeated values. This is 124 // the number of rows written since the last time we flushed a page. 125 numBufferedRows int 126 127 // the total number of stored values in the current page. for repeated or optional 128 // values. this number may be lower than numBuffered 129 numDataValues int64 130 131 rowsWritten int 132 totalBytesWritten int64 133 // records the current number of compressed bytes in a column 134 totalCompressedBytes int64 135 closed bool 136 fallbackToNonDict bool 137 138 pages []DataPage 139 140 defLevelSink *encoding.PooledBufferWriter 141 repLevelSink *encoding.PooledBufferWriter 142 143 uncompressedData bytes.Buffer 144 compressedTemp *bytes.Buffer 145 146 currentEncoder encoding.TypedEncoder 147 } 148 149 func newColumnWriterBase(metaData *metadata.ColumnChunkMetaDataBuilder, pager PageWriter, useDict bool, enc parquet.Encoding, props *parquet.WriterProperties) columnWriter { 150 ret := columnWriter{ 151 metaData: metaData, 152 descr: metaData.Descr(), 153 levelInfo: computeLevelInfo(metaData.Descr()), 154 pager: pager, 155 hasDict: useDict, 156 encoding: enc, 157 props: props, 158 mem: props.Allocator(), 159 defLevelSink: encoding.NewPooledBufferWriter(0), 160 repLevelSink: encoding.NewPooledBufferWriter(0), 161 } 162 if pager.HasCompressor() { 163 ret.compressedTemp = new(bytes.Buffer) 164 } 165 if props.StatisticsEnabledFor(ret.descr.Path()) && ret.descr.SortOrder() != schema.SortUNKNOWN { 166 ret.pageStatistics = metadata.NewStatistics(ret.descr, props.Allocator()) 167 ret.chunkStatistics = metadata.NewStatistics(ret.descr, props.Allocator()) 168 } 169 170 ret.defEncoder.Init(parquet.Encodings.RLE, ret.descr.MaxDefinitionLevel(), ret.defLevelSink) 171 ret.repEncoder.Init(parquet.Encodings.RLE, ret.descr.MaxRepetitionLevel(), ret.repLevelSink) 172 173 ret.reset() 174 175 return ret 176 } 177 178 func (w *columnWriter) CurrentEncoder() encoding.TypedEncoder { return w.currentEncoder } 179 func (w *columnWriter) HasBitsBuffer() bool { return w.bitsBuffer != nil } 180 func (w *columnWriter) SetBitsBuffer(buf *memory.Buffer) { w.bitsBuffer = buf } 181 func (w *columnWriter) PageStatistics() metadata.TypedStatistics { return w.pageStatistics } 182 func (w *columnWriter) LevelInfo() LevelInfo { return w.levelInfo } 183 184 func (w *columnWriter) Type() parquet.Type { 185 return w.descr.PhysicalType() 186 } 187 188 func (w *columnWriter) Descr() *schema.Column { 189 return w.descr 190 } 191 192 func (w *columnWriter) Properties() *parquet.WriterProperties { 193 return w.props 194 } 195 196 func (w *columnWriter) TotalCompressedBytes() int64 { 197 return w.totalCompressedBytes 198 } 199 200 func (w *columnWriter) TotalBytesWritten() int64 { 201 return w.totalBytesWritten 202 } 203 204 func (w *columnWriter) RowsWritten() int { 205 return w.rowsWritten + w.numBufferedRows 206 } 207 208 func (w *columnWriter) WriteDataPage(page DataPage) error { 209 written, err := w.pager.WriteDataPage(page) 210 w.totalBytesWritten += written 211 return err 212 } 213 214 func (w *columnWriter) WriteDefinitionLevels(levels []int16) { 215 w.defEncoder.EncodeNoFlush(levels) 216 } 217 218 func (w *columnWriter) WriteRepetitionLevels(levels []int16) { 219 w.repEncoder.EncodeNoFlush(levels) 220 } 221 222 func (w *columnWriter) reset() { 223 w.defLevelSink.Reset(0) 224 w.repLevelSink.Reset(0) 225 226 if w.props.DataPageVersion() == parquet.DataPageV1 { 227 // offset the buffers to make room to record the number of levels at the 228 // beginning of each after we've encoded them with RLE 229 if w.descr.MaxDefinitionLevel() > 0 { 230 w.defLevelSink.SetOffset(arrow.Uint32SizeBytes) 231 } 232 if w.descr.MaxRepetitionLevel() > 0 { 233 w.repLevelSink.SetOffset(arrow.Uint32SizeBytes) 234 } 235 } 236 237 w.defEncoder.Reset(w.descr.MaxDefinitionLevel()) 238 w.repEncoder.Reset(w.descr.MaxRepetitionLevel()) 239 } 240 241 func (w *columnWriter) concatBuffers(defLevelsSize, repLevelsSize int32, values []byte, wr io.Writer) { 242 wr.Write(w.repLevelSink.Bytes()[:repLevelsSize]) 243 wr.Write(w.defLevelSink.Bytes()[:defLevelsSize]) 244 wr.Write(values) 245 } 246 247 func (w *columnWriter) EstimatedBufferedValueBytes() int64 { 248 return w.currentEncoder.EstimatedDataEncodedSize() 249 } 250 251 func (w *columnWriter) commitWriteAndCheckPageLimit(numLevels, numValues int64) error { 252 w.numBufferedValues += numLevels 253 w.numDataValues += numValues 254 255 enc := w.currentEncoder.EstimatedDataEncodedSize() 256 if enc >= w.props.DataPageSize() { 257 return w.FlushCurrentPage() 258 } 259 return nil 260 } 261 262 func (w *columnWriter) FlushCurrentPage() error { 263 var ( 264 defLevelsRLESize int32 = 0 265 repLevelsRLESize int32 = 0 266 ) 267 268 values, err := w.currentEncoder.FlushValues() 269 if err != nil { 270 return err 271 } 272 defer values.Release() 273 274 isV1DataPage := w.props.DataPageVersion() == parquet.DataPageV1 275 if w.descr.MaxDefinitionLevel() > 0 { 276 w.defEncoder.Flush() 277 w.defLevelSink.SetOffset(0) 278 sz := w.defEncoder.Len() 279 if isV1DataPage { 280 sz += arrow.Uint32SizeBytes 281 binary.LittleEndian.PutUint32(w.defLevelSink.Bytes(), uint32(w.defEncoder.Len())) 282 } 283 defLevelsRLESize = int32(sz) 284 } 285 286 if w.descr.MaxRepetitionLevel() > 0 { 287 w.repEncoder.Flush() 288 w.repLevelSink.SetOffset(0) 289 if isV1DataPage { 290 binary.LittleEndian.PutUint32(w.repLevelSink.Bytes(), uint32(w.repEncoder.Len())) 291 } 292 repLevelsRLESize = int32(w.repLevelSink.Len()) 293 } 294 295 uncompressed := defLevelsRLESize + repLevelsRLESize + int32(values.Len()) 296 if isV1DataPage { 297 err = w.buildDataPageV1(defLevelsRLESize, repLevelsRLESize, uncompressed, values.Bytes()) 298 } else { 299 err = w.buildDataPageV2(defLevelsRLESize, repLevelsRLESize, uncompressed, values.Bytes()) 300 } 301 302 w.reset() 303 w.rowsWritten += w.numBufferedRows 304 w.numBufferedValues, w.numDataValues, w.numBufferedRows = 0, 0, 0 305 return err 306 } 307 308 func (w *columnWriter) buildDataPageV1(defLevelsRLESize, repLevelsRLESize, uncompressed int32, values []byte) error { 309 w.uncompressedData.Reset() 310 w.uncompressedData.Grow(int(uncompressed)) 311 w.concatBuffers(defLevelsRLESize, repLevelsRLESize, values, &w.uncompressedData) 312 313 pageStats, err := w.getPageStatistics() 314 if err != nil { 315 return err 316 } 317 pageStats.ApplyStatSizeLimits(int(w.props.MaxStatsSizeFor(w.descr.Path()))) 318 pageStats.Signed = schema.SortSIGNED == w.descr.SortOrder() 319 w.resetPageStatistics() 320 321 var data []byte 322 if w.pager.HasCompressor() { 323 w.compressedTemp.Reset() 324 data = w.pager.Compress(w.compressedTemp, w.uncompressedData.Bytes()) 325 } else { 326 data = w.uncompressedData.Bytes() 327 } 328 329 // write the page to sink eagerly if there's no dictionary or if dictionary encoding has fallen back 330 if w.hasDict && !w.fallbackToNonDict { 331 pageSlice := make([]byte, len(data)) 332 copy(pageSlice, data) 333 page := NewDataPageV1WithStats(memory.NewBufferBytes(pageSlice), int32(w.numBufferedValues), w.encoding, parquet.Encodings.RLE, parquet.Encodings.RLE, uncompressed, pageStats) 334 w.totalCompressedBytes += int64(page.buf.Len()) // + size of Pageheader 335 w.pages = append(w.pages, page) 336 } else { 337 w.totalCompressedBytes += int64(len(data)) 338 dp := NewDataPageV1WithStats(memory.NewBufferBytes(data), int32(w.numBufferedValues), w.encoding, parquet.Encodings.RLE, parquet.Encodings.RLE, uncompressed, pageStats) 339 defer dp.Release() 340 return w.WriteDataPage(dp) 341 } 342 return nil 343 } 344 345 func (w *columnWriter) buildDataPageV2(defLevelsRLESize, repLevelsRLESize, uncompressed int32, values []byte) error { 346 var data []byte 347 if w.pager.HasCompressor() { 348 w.compressedTemp.Reset() 349 data = w.pager.Compress(w.compressedTemp, values) 350 } else { 351 data = values 352 } 353 354 // concatenate uncompressed levels and the possibly compressed values 355 var combined bytes.Buffer 356 combined.Grow(int(defLevelsRLESize + repLevelsRLESize + int32(len(data)))) 357 w.concatBuffers(defLevelsRLESize, repLevelsRLESize, data, &combined) 358 359 pageStats, err := w.getPageStatistics() 360 if err != nil { 361 return err 362 } 363 pageStats.ApplyStatSizeLimits(int(w.props.MaxStatsSizeFor(w.descr.Path()))) 364 pageStats.Signed = schema.SortSIGNED == w.descr.SortOrder() 365 w.resetPageStatistics() 366 367 numValues := int32(w.numBufferedValues) 368 numRows := int32(w.numBufferedRows) 369 nullCount := int32(pageStats.NullCount) 370 defLevelsByteLen := int32(defLevelsRLESize) 371 repLevelsByteLen := int32(repLevelsRLESize) 372 373 page := NewDataPageV2WithStats(memory.NewBufferBytes(combined.Bytes()), numValues, nullCount, numRows, w.encoding, 374 defLevelsByteLen, repLevelsByteLen, uncompressed, w.pager.HasCompressor(), pageStats) 375 if w.hasDict && !w.fallbackToNonDict { 376 w.totalCompressedBytes += int64(page.buf.Len()) // + sizeof pageheader 377 w.pages = append(w.pages, page) 378 } else { 379 w.totalCompressedBytes += int64(combined.Len()) 380 defer page.Release() 381 return w.WriteDataPage(page) 382 } 383 return nil 384 } 385 386 func (w *columnWriter) FlushBufferedDataPages() (err error) { 387 if w.numBufferedValues > 0 { 388 if err = w.FlushCurrentPage(); err != nil { 389 return err 390 } 391 } 392 393 for _, p := range w.pages { 394 defer p.Release() 395 if err = w.WriteDataPage(p); err != nil { 396 return err 397 } 398 } 399 w.pages = w.pages[:0] 400 w.totalCompressedBytes = 0 401 return 402 } 403 404 func (w *columnWriter) writeLevels(numValues int64, defLevels, repLevels []int16) int64 { 405 toWrite := int64(0) 406 // if the field is required and non-repeated, no definition levels 407 if defLevels != nil && w.descr.MaxDefinitionLevel() > 0 { 408 for _, v := range defLevels[:numValues] { 409 if v == w.descr.MaxDefinitionLevel() { 410 toWrite++ 411 } 412 } 413 w.WriteDefinitionLevels(defLevels[:numValues]) 414 } else { 415 toWrite = numValues 416 } 417 418 if repLevels != nil && w.descr.MaxRepetitionLevel() > 0 { 419 // a row could include more than one value 420 //count the occasions where we start a new row 421 for _, v := range repLevels[:numValues] { 422 if v == 0 { 423 w.numBufferedRows++ 424 } 425 } 426 427 w.WriteRepetitionLevels(repLevels[:numValues]) 428 } else { 429 // each value is exactly 1 row 430 w.numBufferedRows += int(numValues) 431 } 432 return toWrite 433 } 434 435 func (w *columnWriter) writeLevelsSpaced(numLevels int64, defLevels, repLevels []int16) { 436 if w.descr.MaxDefinitionLevel() > 0 { 437 w.WriteDefinitionLevels(defLevels[:numLevels]) 438 } 439 440 if w.descr.MaxRepetitionLevel() > 0 { 441 for _, v := range repLevels { 442 if v == 0 { 443 w.numBufferedRows++ 444 } 445 } 446 w.WriteRepetitionLevels(repLevels[:numLevels]) 447 } else { 448 w.numBufferedRows += int(numLevels) 449 } 450 } 451 452 func (w *columnWriter) WriteDictionaryPage() error { 453 dictEncoder := w.currentEncoder.(encoding.DictEncoder) 454 buffer := memory.NewResizableBuffer(w.mem) 455 buffer.Resize(dictEncoder.DictEncodedSize()) 456 dictEncoder.WriteDict(buffer.Bytes()) 457 defer buffer.Release() 458 459 page := NewDictionaryPage(buffer, int32(dictEncoder.NumEntries()), w.props.DictionaryPageEncoding()) 460 written, err := w.pager.WriteDictionaryPage(page) 461 w.totalBytesWritten += written 462 return err 463 } 464 465 type batchWriteInfo struct { 466 batchNum int64 467 nullCount int64 468 } 469 470 func (b batchWriteInfo) numSpaced() int64 { return b.batchNum + b.nullCount } 471 472 // this will always update the three output params 473 // outValsToWrite, outSpacedValsToWrite, and NullCount. Additionally 474 // it will update the validity bitmap if required (i.e. if at least one 475 // level of nullable structs directly precede the leaf node) 476 func (w *columnWriter) maybeCalculateValidityBits(defLevels []int16, batchSize int64) (out batchWriteInfo) { 477 if w.bitsBuffer == nil { 478 if w.levelInfo.DefLevel == 0 { 479 // in this case def levels should be null and we only 480 // need to output counts which will always be equal to 481 // the batch size passed in (max def level == 0 indicates 482 // there cannot be repeated or null fields) 483 out.batchNum = batchSize 484 out.nullCount = 0 485 } else { 486 var ( 487 toWrite int64 488 spacedToWrite int64 489 ) 490 for i := int64(0); i < batchSize; i++ { 491 if defLevels[i] == w.levelInfo.DefLevel { 492 toWrite++ 493 } 494 if defLevels[i] >= w.levelInfo.RepeatedAncestorDefLevel { 495 spacedToWrite++ 496 } 497 } 498 out.batchNum += toWrite 499 out.nullCount = spacedToWrite - toWrite 500 } 501 return 502 } 503 504 // shrink to fit possible causes another allocation 505 newBitmapSize := bitutil.BytesForBits(batchSize) 506 if newBitmapSize != int64(w.bitsBuffer.Len()) { 507 w.bitsBuffer.ResizeNoShrink(int(newBitmapSize)) 508 } 509 510 io := ValidityBitmapInputOutput{ 511 ValidBits: w.bitsBuffer.Bytes(), 512 ReadUpperBound: batchSize, 513 } 514 DefLevelsToBitmap(defLevels[:batchSize], w.levelInfo, &io) 515 out.batchNum = io.Read - io.NullCount 516 out.nullCount = io.NullCount 517 return 518 } 519 520 func (w *columnWriter) getPageStatistics() (enc metadata.EncodedStatistics, err error) { 521 if w.pageStatistics != nil { 522 enc, err = w.pageStatistics.Encode() 523 } 524 return 525 } 526 527 func (w *columnWriter) getChunkStatistics() (enc metadata.EncodedStatistics, err error) { 528 if w.chunkStatistics != nil { 529 enc, err = w.chunkStatistics.Encode() 530 } 531 return 532 } 533 534 func (w *columnWriter) resetPageStatistics() { 535 if w.chunkStatistics != nil { 536 w.chunkStatistics.Merge(w.pageStatistics) 537 w.pageStatistics.Reset() 538 } 539 } 540 541 func (w *columnWriter) Close() (err error) { 542 if !w.closed { 543 w.closed = true 544 if w.hasDict && !w.fallbackToNonDict { 545 w.WriteDictionaryPage() 546 } 547 548 if err = w.FlushBufferedDataPages(); err != nil { 549 return err 550 } 551 552 // ensure we release and reset everything even if we 553 // error out from the chunk statistics handling 554 defer func() { 555 w.defLevelSink.Reset(0) 556 w.repLevelSink.Reset(0) 557 if w.bitsBuffer != nil { 558 w.bitsBuffer.Release() 559 w.bitsBuffer = nil 560 } 561 562 w.currentEncoder.Release() 563 w.currentEncoder = nil 564 }() 565 566 var chunkStats metadata.EncodedStatistics 567 chunkStats, err = w.getChunkStatistics() 568 if err != nil { 569 return err 570 } 571 572 chunkStats.ApplyStatSizeLimits(int(w.props.MaxStatsSizeFor(w.descr.Path()))) 573 chunkStats.Signed = schema.SortSIGNED == w.descr.SortOrder() 574 575 if w.rowsWritten > 0 && chunkStats.IsSet() { 576 w.metaData.SetStats(chunkStats) 577 } 578 err = w.pager.Close(w.hasDict, w.fallbackToNonDict) 579 } 580 return err 581 } 582 583 func (w *columnWriter) doBatches(total int64, repLevels []int16, action func(offset, batch int64)) { 584 batchSize := w.props.WriteBatchSize() 585 // if we're writing V1 data pages, have no replevels or the max replevel is 0 then just 586 // use the regular doBatches function 587 if w.props.DataPageVersion() == parquet.DataPageV1 || repLevels == nil || w.descr.MaxRepetitionLevel() == 0 { 588 doBatches(total, batchSize, action) 589 return 590 } 591 592 // if we get here that means we have repetition levels to write and we're writing 593 // V2 data pages. since we check whether to flush after each batch we write 594 // if we ensure all the batches begin and end on row boundaries we can avoid 595 // complex logic inside of our flushing or batch writing functions. 596 // the WriteBatch function recovers from panics so we can just panic here on a failure 597 // and it'll get caught by the WriteBatch functions above it 598 if int64(len(repLevels)) < total { 599 // if we're writing repLevels there has to be at least enough in the slice 600 // to write the total number that we're being asked to write 601 panic("columnwriter: not enough repetition levels for batch to write") 602 } 603 604 if repLevels[0] != 0 { 605 panic("columnwriter: batch writing for V2 data pages must start at a row boundary") 606 } 607 608 // loop by batchSize, but make sure we're ending/starting each batch on a row boundary 609 var ( 610 batchStart, batch int64 611 ) 612 for batchStart = 0; batchStart+batchSize < int64(len(repLevels)); batchStart += batch { 613 // check one past the last value of the batch for if it's a new row 614 // if it's not, shrink the batch and feel back to the beginning of a 615 // previous row boundary to end on 616 batch = batchSize 617 for ; repLevels[batchStart+batch] != 0; batch-- { 618 } 619 // batchStart <--> batch now begins and ends on a row boundary! 620 action(batchStart, batch) 621 } 622 action(batchStart, int64(len(repLevels))-batchStart) 623 } 624 625 func doBatches(total, batchSize int64, action func(offset, batch int64)) { 626 numBatches := total / batchSize 627 for i := int64(0); i < numBatches; i++ { 628 action(i*batchSize, batchSize) 629 } 630 if total%batchSize > 0 { 631 action(numBatches*batchSize, total%batchSize) 632 } 633 } 634 635 func levelSliceOrNil(rep []int16, offset, batch int64) []int16 { 636 if rep == nil { 637 return nil 638 } 639 return rep[offset : batch+offset] 640 } 641 642 //lint:ignore U1000 maybeReplaceValidity 643 func (w *columnWriter) maybeReplaceValidity(values arrow.Array, newNullCount int64) arrow.Array { 644 if w.bitsBuffer == nil { 645 values.Retain() 646 return values 647 } 648 649 if len(values.Data().Buffers()) == 0 { 650 values.Retain() 651 return values 652 } 653 654 buffers := make([]*memory.Buffer, len(values.Data().Buffers())) 655 copy(buffers, values.Data().Buffers()) 656 // bitsBuffer should already be the offset slice of the validity bits 657 // we want so we don't need to manually slice the validity buffer 658 buffers[0] = w.bitsBuffer 659 660 if values.Data().Offset() > 0 { 661 data := values.Data() 662 buffers[1] = memory.NewBufferBytes(data.Buffers()[1].Bytes()[data.Offset()*arrow.Int32SizeBytes : data.Len()*arrow.Int32SizeBytes]) 663 } 664 665 data := array.NewData(values.DataType(), values.Len(), buffers, nil, int(newNullCount), 0) 666 defer data.Release() 667 return array.MakeFromData(data) 668 }