Skip to content

GH-1190: Reserve view slots for empty view vector values - #1192

Open
goutamadwant wants to merge 3 commits into
apache:mainfrom
goutamadwant:fix/GH-1190-view-buffer-empty-values
Open

GH-1190: Reserve view slots for empty view vector values#1192
goutamadwant wants to merge 3 commits into
apache:mainfrom
goutamadwant:fix/GH-1190-view-buffer-empty-values

Conversation

@goutamadwant

Copy link
Copy Markdown

What's Changed

Fixes BaseVariableWidthViewVector.handleSafe so setSafe reserves a full 16-byte view slot for the target index even when the value length is zero.

Adds coverage for empty values written through the first view-buffer boundary for both ViewVarCharVector and ViewVarBinaryVector.

Tests

  • mvn -pl vector -am -Dtest=TestVariableWidthViewVector#testSetSafeEmptyValueAtViewBufferBoundary -Dsurefire.failIfNoSpecifiedTests=false test
  • mvn -pl vector -am -Dtest=TestVariableWidthViewVector -Dsurefire.failIfNoSpecifiedTests=false test

Closes#1190.

@github-actions

This comment has been minimized.

@mbutrovichmbutrovich left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks @goutamadwant, this looks like what I was going to do! One coverage gap worth closing: handleSafe(index, 0) is also hit by setNull (1195) and fillEmpties via setValueCount (1022), so the bug was reachable without ever writing an empty string. The fix covers those too, but the test only exercises setSafe(i, new byte[0]). Suggest adding the two tests below. Non-blocking.

protected final void handleSafe(int index, int dataLength) {
final long targetCapacity = roundUpToMultipleOf16((long) index * ELEMENT_SIZE + dataLength);
// The view buffer stores one fixed-width view per value; payload bytes are allocated separately.
final long targetCapacity = roundUpToMultipleOf16(((long) index + 1) * ELEMENT_SIZE);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correct. Reserves the full slot regardless of payload; ((long) index + 1) avoids overflow. roundUpToMultipleOf16 is a no-op here but fine to keep.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for reviewing @mbutrovich . I kept this code path unchanged since it reserves the full view slot regardless of payload size and the cast keeps the sizing expression safe before the multiplication. Let me know if there any other thoughts.


@ParameterizedTest
@MethodSource({"vectorCreatorProvider"})
public void testSetSafeEmptyValueAtViewBufferBoundary(

@mbutrovichmbutrovichJul 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only covers the empty-value path. setNull and fillEmpties/setValueCount reach the same handleSafe(index, 0). Suggest adding tests for those.

setValueCount trailing gap (silent trigger, no empty string written)

@ParameterizedTest@MethodSource({"vectorCreatorProvider"})
publicvoidtestSetValueCountFillsEmptiesAtViewBufferBoundary(
Function<BufferAllocator, BaseVariableWidthViewVector> vectorCreator) {
try (finalBaseVariableWidthViewVectorvector = vectorCreator.apply(allocator)) {
vector.allocateNew();
finalintvalueCapacity = vector.getValueCapacity();
vector.setSafe(valueCapacity - 1, "x".getBytes(StandardCharsets.UTF_8));
// Leaves index valueCapacity unset: fillEmpties -> handleSafe(_, 0) on a full buffer.vector.setValueCount(valueCapacity + 1);
assertTrue(vector.getValueCapacity() > valueCapacity);
assertTrue(vector.isNull(valueCapacity));
}
}

setNull at the boundary

@ParameterizedTest@MethodSource({"vectorCreatorProvider"})
publicvoidtestSetNullAtViewBufferBoundary(
Function<BufferAllocator, BaseVariableWidthViewVector> vectorCreator) {
try (finalBaseVariableWidthViewVectorvector = vectorCreator.apply(allocator)) {
vector.allocateNew();
finalintvalueCapacity = vector.getValueCapacity();
for (inti = 0; i <= valueCapacity; i++) {
vector.setNull(i);
}
vector.setValueCount(valueCapacity + 1);
assertTrue(vector.getValueCapacity() > valueCapacity);
assertTrue(vector.isNull(valueCapacity));
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@mbutrovich added coverage for both additional paths: setNull and setValueCount/fillEmpties at the view-buffer boundary. The production fix is unchanged and his just closes the missing regression coverage. Let me know. thanks!

@lidavidmlidavidm added the bug-fix PRs that fix a big. label Jul 8, 2026
@github-actionsgithub-actionsBot added this to the 20.0.0 milestone Jul 8, 2026
@goutamadwant

Copy link
Copy Markdown
Author

@mbutrovich addressed your comments. let me know if you have more suggestions. thanks!

@mbutrovichmbutrovich left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This LGTM, thanks for the PR and revision @goutamadwant!

@parthchandraparthchandra left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks @goutamadwant. The original implementation is logically wrong and this is the correct fix.
Also, thanks for adding the tests !

@parthchandra

Copy link
Copy Markdown

@goutamadwant could you rebase on main and also run spotless?

@parthchandra

Copy link
Copy Markdown

@lidavidm@wgtmac@laurentgo@jbonofre Can you please take a look?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems reasonable, but CI is not passing

@parthchandra

Copy link
Copy Markdown

but CI is not passing

Looks like a rebase and running spotless will fix the CI issues.

@goutamadwant
goutamadwantforce-pushed the fix/GH-1190-view-buffer-empty-values branch from f9d306b to 697e3d6CompareJuly 14, 2026 02:27
@goutamadwant

Copy link
Copy Markdown
Author

@parthchandra Rebased this PR on latest main and pushed the update... also ran Spotless; the only formatting change was wrapping the long comment in BaseVariableWidthViewVector.java.

checked locally with below runs :

  • mvn --batch-mode -pl vector spotless:check
  • mvn --batch-mode -pl vector -am -Dtest=TestVariableWidthViewVector -Dsurefire.failIfNoSpecifiedTests=false test

The focused vector test passed under both netty and unsafe runs. Let me know. thanks!

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

Labels

bug-fixPRs that fix a big.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] BaseVariableWidthViewVector.handleSafe under-allocates the view buffer for empty (zero-length) values → IndexOutOfBoundsException

4 participants

@goutamadwant@parthchandra@lidavidm@mbutrovich