Skip to content

Commit 21e72e6

Browse files
committed
Keep vectors usable when loading their validity buffer fails
loadFieldBuffers released the vector's validity buffer before BitVectorHelper.loadValidityBuffer created the new one. When that failed, for example on an allocation failure, the vector still pointed at the released buffer, and closing it later released the buffer again. Create the new validity buffer first in every vector that loads one. loadValidityBuffer also treated a missing validity buffer with an unknown null count (-1) as the mixed case, and kept the null or empty source buffer: a NullPointerException for a NULL C Data buffer, or a vector whose validity buffer is too small. Treat it as all valid, and reject a missing validity buffer when the null count says that only some values are null.
1 parent 1c57160 commit 21e72e6

13 files changed

Lines changed: 176 additions & 17 deletions

‎vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
503503
ArrowBuf bitBuffer = ownBuffers.get(0);
504504
ArrowBuf dataBuffer = ownBuffers.get(1);
505505

506+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
507+
ArrowBuf newValidityBuffer =
508+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
506509
validityBuffer.getReferenceManager().release();
507-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
510+
validityBuffer = newValidityBuffer;
508511
valueBuffer.getReferenceManager().release();
509512
valueBuffer = dataBuffer.getReferenceManager().retain(dataBuffer, allocator);
510513
refreshValueCapacity();

‎vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
304304
ArrowBuf offBuffer = ownBuffers.get(1);
305305
ArrowBuf dataBuffer = ownBuffers.get(2);
306306

307+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
308+
ArrowBuf newValidityBuffer =
309+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
307310
validityBuffer.getReferenceManager().release();
308-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
311+
validityBuffer = newValidityBuffer;
309312
offsetBuffer.getReferenceManager().release();
310313
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
311314
valueBuffer.getReferenceManager().release();

‎vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
320320
ArrowBuf offBuffer = ownBuffers.get(1);
321321
ArrowBuf dataBuffer = ownBuffers.get(2);
322322

323+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
324+
ArrowBuf newValidityBuffer =
325+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
323326
validityBuffer.getReferenceManager().release();
324-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
327+
validityBuffer = newValidityBuffer;
325328
offsetBuffer.getReferenceManager().release();
326329
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
327330
valueBuffer.getReferenceManager().release();

‎vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,13 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
354354
ArrowBuf viewBuf = ownBuffers.get(1);
355355
List<ArrowBuf> dataBufs = ownBuffers.subList(2, ownBuffers.size());
356356

357+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
358+
ArrowBuf newValidityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuf, allocator);
359+
357360
this.clear();
358361

359362
this.viewBuffer = viewBuf.getReferenceManager().retain(viewBuf, allocator);
360-
this.validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuf, allocator);
363+
this.validityBuffer = newValidityBuffer;
361364

362365
for (ArrowBuf dataBuf : dataBufs) {
363366
this.dataBuffers.add(dataBuf.getReferenceManager().retain(dataBuf, allocator));

‎vector/src/main/java/org/apache/arrow/vector/BitVectorHelper.java‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ public static byte getBitsFromNextByte(ArrowBuf data, int index, int offset) {
309309
* @param allocator The allocator to use for creating a new buffer if necessary.
310310
* @return A new buffer that is either allocated or points to the same memory as
311311
* sourceValidityBuffer.
312+
* @throws IllegalArgumentException if the source validity buffer is missing, but the null count
313+
* says that only some values are null
312314
*/
313315
public static ArrowBuf loadValidityBuffer(
314316
final ArrowFieldNode fieldNode,
@@ -317,17 +319,24 @@ public static ArrowBuf loadValidityBuffer(
317319
final int valueCount = fieldNode.getLength();
318320
ArrowBuf newBuffer = null;
319321

320-
// Create a new validity buffer iff both of the following are true:
321-
// - validity buffer is not present, that is, it is either null or empty (in the case of
322-
// IPC for instance).
323-
// - values are either all NULLs or all non-NULLs
322+
// Create a new validity buffer iff the validity buffer is not present, that is, it is either
323+
// null or empty (in the case of IPC for instance). The values are then either all NULLs, or
324+
// all non-NULLs when the null count is 0 or unknown (-1).
324325
boolean isValidityBufferNull =
325326
sourceValidityBuffer == null || sourceValidityBuffer.capacity() == 0;
326-
if (isValidityBufferNull
327-
&& (fieldNode.getNullCount() == 0 || fieldNode.getNullCount() == valueCount)) {
327+
if (isValidityBufferNull) {
328+
final int nullCount = fieldNode.getNullCount();
329+
final boolean allNull = valueCount > 0 && nullCount == valueCount;
330+
if (!allNull && nullCount != 0 && nullCount != -1) {
331+
throw new IllegalArgumentException(
332+
"Validity buffer is missing for "
333+
+ valueCount
334+
+ " values with a null count of "
335+
+ nullCount);
336+
}
328337
newBuffer = allocator.buffer(getValidityBufferSizeFromCount(valueCount));
329338
newBuffer.setZero(0, newBuffer.capacity());
330-
if (fieldNode.getNullCount() != 0) {
339+
if (allNull) {
331340
/* all NULLs */
332341
return newBuffer;
333342
}

‎vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
170170

171171
ArrowBuf bitBuffer = ownBuffers.get(0);
172172

173+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
174+
ArrowBuf newValidityBuffer =
175+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
173176
validityBuffer.getReferenceManager().release();
174-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
177+
validityBuffer = newValidityBuffer;
175178
valueCount = fieldNode.getLength();
176179

177180
validityAllocationSizeInBytes = checkedCastToInt(validityBuffer.capacity());

‎vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
255255
ArrowBuf bitBuffer = ownBuffers.get(0);
256256
ArrowBuf offBuffer = ownBuffers.get(1);
257257

258+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
259+
ArrowBuf newValidityBuffer =
260+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
258261
validityBuffer.getReferenceManager().release();
259-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
262+
validityBuffer = newValidityBuffer;
260263
offsetBuffer.getReferenceManager().release();
261264
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
262265

‎vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
202202
ArrowBuf offBuffer = ownBuffers.get(1);
203203
ArrowBuf szBuffer = ownBuffers.get(2);
204204

205+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
206+
ArrowBuf newValidityBuffer =
207+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
205208
validityBuffer.getReferenceManager().release();
206-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
209+
validityBuffer = newValidityBuffer;
207210
offsetBuffer.getReferenceManager().release();
208211
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
209212
sizeBuffer.getReferenceManager().release();

‎vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
213213
ArrowBuf bitBuffer = ownBuffers.get(0);
214214
ArrowBuf offBuffer = ownBuffers.get(1);
215215

216+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
217+
ArrowBuf newValidityBuffer =
218+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
216219
validityBuffer.getReferenceManager().release();
217-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
220+
validityBuffer = newValidityBuffer;
218221
offsetBuffer.getReferenceManager().release();
219222
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
220223

‎vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,11 @@ public void loadFieldBuffers(ArrowFieldNode fieldNode, List<ArrowBuf> ownBuffers
201201
ArrowBuf offBuffer = ownBuffers.get(1);
202202
ArrowBuf szBuffer = ownBuffers.get(2);
203203

204+
// Load the new validity buffer first, so that a failure leaves the vector unchanged
205+
ArrowBuf newValidityBuffer =
206+
BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
204207
validityBuffer.getReferenceManager().release();
205-
validityBuffer = BitVectorHelper.loadValidityBuffer(fieldNode, bitBuffer, allocator);
208+
validityBuffer = newValidityBuffer;
206209
offsetBuffer.getReferenceManager().release();
207210
offsetBuffer = offBuffer.getReferenceManager().retain(offBuffer, allocator);
208211
sizeBuffer.getReferenceManager().release();

0 commit comments

Comments
 (0)