fix(parquet): clamp repLevels in byte-array DataPageV2 row-boundary alignment#919
Open
zeroshade wants to merge 1 commit into
Open
Conversation
…lignment The ByteArray and FixedLenByteArray column writers run their own adaptive batching loop in WriteBatch and WriteBatchSpacedWithError. apache#883 added DataPageV2 row-boundary alignment there by calling alignBatchToRowBoundary with the caller's repLevels slice, but did not clamp it to the number of levels being written (n is derived from defLevels/values, not repLevels). columnWriter.doBatches, used by the numeric and boolean paths, already clamps repLevels to the write length for exactly this reason. When a caller passes repLevels longer than n, the byte-array paths could grow the final batch of a wide repeated row past n, slicing defLevels/values out of range (recovered as an opaque error) or spilling extra levels into the column. Clamp repLevels to n in both byte-array batching paths, mirroring the guards in columnWriter.doBatches (length check, empty-input short-circuit, clamp, row-boundary start check). The change is made in column_writer_types.gen.go.tmpl and regenerated. Adds TestWriteBatchByteArrayV2OversizedRepLevels covering WriteBatch and WriteBatchSpacedWithError for both ByteArray and FixedLenByteArray with an oversized repLevels slice; each case fails without the fix (slice bounds out of range) and passes with it.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a DataPageV2 row-boundary alignment bug specific to BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY column writers when callers pass a repLevels slice longer than the effective write length (n derived from defLevels/values). It makes the byte-array batching loops clamp repLevels to n before invoking row-boundary alignment, matching the existing safeguards in columnWriter.doBatches used by other types.
Changes:
- Clamp
repLevelston(and enforce row-boundary start) in the byte-arrayWriteBatchadaptive batching loop. - Apply the same
repLevelsclamping in the byte-arrayWriteBatchSpacedWithErroradaptive batching loop. - Add a regression test covering oversized
repLevelsfor bothByteArrayandFixedLenByteArray, for both write paths, under DataPageV2.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| parquet/file/column_writer_v2_row_boundary_test.go | Adds a regression test ensuring oversized repLevels does not cause out-of-range errors or spilled values for byte-array writers under DataPageV2. |
| parquet/file/column_writer_types.gen.go.tmpl | Clamps repLevels to n in the byte-array write loops before row-boundary alignment, mirroring columnWriter.doBatches. |
| parquet/file/column_writer_types.gen.go | Regenerates typed writers to include the new repLevels clamping logic for ByteArray and FixedLenByteArray. |
Files not reviewed (1)
- parquet/file/column_writer_types.gen.go: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lidavidm
approved these changes
Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rationale for this change
The
ByteArrayandFixedLenByteArraycolumn writers run their own adaptivebatching loop in
WriteBatchandWriteBatchSpacedWithError. #883 addedDataPageV2 row-boundary alignment to those loops by calling
alignBatchToRowBoundary(repLevels, levelOffset, batch), but passed the caller'sfull
repLevelsslice. The write lengthnin those loops is derived fromdefLevels/values, notrepLevels, and nothing requireslen(repLevels) == n.columnWriter.doBatches(used by the numeric and boolean paths) already clampsrepLevels = repLevels[:total]for exactly this reason — its comment notes "acaller passing a repLevels slice longer than total must not spill the extra
levels into the column." The byte-array paths did not.
When a caller passes a
repLevelsslice longer thann,alignBatchToRowBoundary(which grows/inspects usinglen(repLevels)) can growthe final batch of a wide repeated row past
n. The writer then slicesdefLevels/valuesout of range — recovered as an opaque error — or, withoversized backing buffers, spills extra levels/values into the column.
len(repLevels) == nis safe (the helper returns early at the tail); the bugrequires
len(repLevels) > n, which is reachable through the low-leveltyped-writer API for repeated columns under DataPageV2.
What changes are included in this PR?
column_writer_types.gen.go.tmpl, both{{if .isByteArray}}blocks(
WriteBatchandWriteBatchSpacedWithError) now clamprepLevelstonbefore the batching loop, mirroring the guards already in
columnWriter.doBatches: length check, empty-input short-circuit,repLevels = repLevels[:n], and arepLevels[0] == 0row-boundary start check.column_writer_types.gen.go(4 sites:ByteArrayandFixedLenByteArray×WriteBatch/WriteBatchSpacedWithError).Are these changes tested?
Yes.
TestWriteBatchByteArrayV2OversizedRepLevelswrites a repeatedByteArrayand
FixedLenByteArraycolumn under DataPageV2 (dictionary disabled, small batchsize) through both
WriteBatchandWriteBatchSpacedWithError, passing arepLevelsslice longer thanlen(defLevels)with a wide final row. It assertsthe write does not error, exactly the requested values are written (no spill),
and the values round-trip. Each of the four cases fails without the fix
(
slice bounds out of range [:7] with capacity 6) and passes with it../parquet/file/...and./parquet/pqarrow/...pass (PARQUET_TEST_DATAset).Are there any user-facing changes?
No API changes. Repeated
BYTE_ARRAY/FIXED_LEN_BYTE_ARRAYcolumns writtenunder DataPageV2 with a
repLevelsslice longer than the value/def-level countno longer risk an out-of-range write error or spilled levels; the byte-array
paths now match the numeric/boolean paths.