Skip to content

Arrow: Fix valuesRead accumulation in VectorizedDeltaEncodedValuesReader#17121

Open
ebyhr wants to merge 1 commit into
apache:mainfrom
ebyhr:ebi/parquet-vectorized
Open

Arrow: Fix valuesRead accumulation in VectorizedDeltaEncodedValuesReader#17121
ebyhr wants to merge 1 commit into
apache:mainfrom
ebyhr:ebi/parquet-vectorized

Conversation

@ebyhr

@ebyhr ebyhr commented Jul 7, 2026

Copy link
Copy Markdown
Member

VectorizedDeltaEncodedValuesReader.readValues resets valuesRead to the current batch size on every call (valuesRead = total - remaining) instead of accumulating it (+=). On a multi-batch page read the overflow guard valuesRead + total > totalValueCount sees a stale count and silently allows reads past the logical end of the page.

The bug does not throw - DELTA_BINARY_PACKED mini-blocks are zero-padded to a fixed size, so the reader decodes the padding zeros as valid deltas and emits plausible-looking but wrong values. For example, a page of [10, 20, ..., 100] read in batches of 6 and 4, then read once more, returns 110 - the next extrapolated delta step - with no error.

@github-actions github-actions Bot added the arrow label Jul 7, 2026
assertThat(batch2).containsExactly(70, 80, 90, 100);

assertThat(reader.totalValueCount()).isEqualTo(10);
assertThatThrownBy(() -> reader.readIntegers(1, 0))

@ebyhr ebyhr Jul 7, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reader.readIntegers(1, 0) returns [110] without this PR fix.

remaining -= loadedRows;
}
valuesRead = total - remaining;
valuesRead += total - remaining;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think remaining is always 0 here. The while loop exit criteria ensures that it is non-positive. InloadMiniBlockToOutput remaining is an upper bound for the return value loadedRows so remaining -= loadedRows cannot be negative either.

It could be only negative if valuesRead = 0 and total = 0. In this case it throws an exception now because we try to write the first value to a zero size array. So even if it's a valid use it's buggy anyway. I would add a check or early return if total is 0 or less.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just saw that the negative case is handled by #17121.

int currentRowId = rowId;
// First value
if (valuesRead == 0) {
outputWriter.write(vec, ((long) (currentRowId + valuesRead) * typeWidth), firstValue);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I noticed that valuesRead is always 0, so the code can be simplified here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants