Repository navigation
GH-1424: Write only the values a vector's offsets cover in VectorUnloader #1425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
f78cd30
fix(vector): write only the values a vector's offsets cover when unlo…
andygrove d811f92
fix(vector): handle unions, empty lists and split failures when re-ba…
andygrove bc8bd9c
fix(vector): reallocate LargeListVector offsets that cannot hold [0]
jbonofre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the covered range is empty, this hands back a list without a valid offsets buffer.
If
coveredChildLengthis 0 and the child is aListVectororMapVector, this callssplitAndTransfer(0, 0). That path only runsto.clear(), so the target's offsets stay asallocator.getEmpty(), the allocator's shared zero-capacity buffer.ListVector.getFieldBuffers()then callswriterIndex(4)on that shared buffer. The inner child's buffers are the same instance, so what gets written depends on which vector set the write index last:List<List<Int>>: the inner offsets buffer is written with 0 bytes, which is what GH-343: Fix ListVector offset buffer not properly serialized for nested empty arrays #967 fixed for [C++/Java] Error when reading inner lists within a struct in empty outer lists from C++/Python in Java #343.List<List<Null>>:ArrowStreamWriter.writeBatch()throwsIndexOutOfBoundsException: index: 0, length: 4 (expected: range(0, 0)).It also leaves the shared empty buffer with
writeIndex() == 4for everything else using that allocator.This doesn't need a C Data import. A Java-built list hits it once the row count is lowered:
The
List<List<Null>>case is outer offsets[0, 0, 0]over a 2-value inner list.mainwrites both correctly.LargeListVector's split already allocates an offsets buffer whenlength == 0, andBaseVariableWidthVector.setReaderAndWriterIndex()reallocates the offsets buffer when it's too small for[0].Doing either in
ListVectorwould fix this, and so would handlinglength == 0here without splitting.Maybe we could add a test with an empty covered range over a nested list child.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d811f92 with the
BaseVariableWidthVectorapproach:ListVector.getFieldBuffers()now reallocates an offsets buffer that can't hold[0], as #989 did for variable-width vectors, andMapVectorinherits it. Allocating in the zero-length split instead made every zero-length split allocate, whichTestSplitAndTransfer.testWithEmptyVectorcaught as a leak in code that doesn't close the target. Handlinglength == 0in the unloader alone wouldn't cover a list nested in a re-based list either, since that empty child comes out ofListVector's own split. YourList<List<Int>>case is nowemptyCoveredRangeOverNestedListKeepsItsFirstOffset(it wrote 0 bytes for the inner offsets before), andTestListVectorandTestMapVectorcheck that a zero-length split's target writes[0]without touching the shared empty buffer.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LargeListVectorneeds the same change. Otherwise the empty covered range case still writes a 0-byte offsets buffer when the nested list is aLargeList.LargeListVector's own zero-length split allocates[0], but that doesn't help when theLargeListVectoris the child of aListVectorsplit to zero length.ListVector's split only runsto.clear()and never splits its child, so the child keepallocator.getEmpty().LargeListVector.setReaderAndWriterIndex()then callswriterIndex(8)on the shared buffer:List<List<LargeList<Int>>>with outer offsets[0, 0, 0]over a 2 value middle list: the int child sets the shared buffer back to 0, so theLargeListoffsets are written with 0 bytes.List<List<LargeList<Null>>>nothing resets it, so the writer tries to read 8 bytes from a zero capacity buffer.mainwrites both correctly, as it doesn't split the middle list.I believe applying the same reallocation in
LargeListVector.setReadersAndWriterIndex()fixes it:Maybe we could add a
LargeListvariant ofemptyCoveredRangeOverNestedListKeepsItsFirstOffset(theLargeListoffsets should be written as 8 bytes holding0), and a check inTestLargeListVectorthat an empty vector'sgetFieldBuffers()leavesallocator.getEmpty().writerIndex()at 0, astestSplitAndTransferZeroLengthdoes forListVector.I can do that for you 😄