Skip to content

Commit b95dd38

Browse files
lriggsmyegorov
andauthored
apacheGH-44626: [Java] fix SplitAndTransfer throws for empty MapVector (apache#44627) (#93)
Empty MapVector.splitAndTransfer throws `java.lang.IndexOutOfBoundsException`. Details in apache#44626 Fixed for MapVector as for other vector types in apache#41066 Added unit test mimicking the scenario we've observed where MapVector's offset buffer capacity is 0. * GitHub Issue: apache#44626 Authored-by: Maksim Yegorov <59841139+maksimyego-db@users.noreply.github.com> Signed-off-by: David Li <li.davidm96@gmail.com> Co-authored-by: Maksim Yegorov <997437+myegorov@users.noreply.github.com>
1 parent 66bbaf1 commit b95dd38

2 files changed

Lines changed: 80 additions & 15 deletions

File tree

java/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -211,23 +211,25 @@ public void splitAndTransfer(int startIndex, int length) {
211211
startIndex,
212212
length,
213213
valueCount);
214-
final int startPoint = offsetBuffer.getInt(startIndex * OFFSET_WIDTH);
215-
final int sliceLength =
216-
offsetBuffer.getInt((startIndex + length) * OFFSET_WIDTH) - startPoint;
217214
to.clear();
218-
to.offsetBuffer = to.allocateOffsetBuffer((length + 1) * OFFSET_WIDTH);
219-
/* splitAndTransfer offset buffer */
220-
for (int i = 0; i < length + 1; i++) {
221-
final int relativeOffset =
222-
offsetBuffer.getInt((startIndex + i) * OFFSET_WIDTH) - startPoint;
223-
to.offsetBuffer.setInt(i * OFFSET_WIDTH, relativeOffset);
215+
if (length > 0) {
216+
final int startPoint = offsetBuffer.getInt(startIndex * OFFSET_WIDTH);
217+
final int sliceLength =
218+
offsetBuffer.getInt((startIndex + length) * OFFSET_WIDTH) - startPoint;
219+
to.offsetBuffer = to.allocateOffsetBuffer((length + 1) * OFFSET_WIDTH);
220+
/* splitAndTransfer offset buffer */
221+
for (int i = 0; i < length + 1; i++) {
222+
final int relativeOffset =
223+
offsetBuffer.getInt((startIndex + i) * OFFSET_WIDTH) - startPoint;
224+
to.offsetBuffer.setInt(i * OFFSET_WIDTH, relativeOffset);
225+
}
226+
/* splitAndTransfer validity buffer */
227+
splitAndTransferValidityBuffer(startIndex, length, to);
228+
/* splitAndTransfer data buffer */
229+
dataTransferPair.splitAndTransfer(startPoint, sliceLength);
230+
to.lastSet = length - 1;
231+
to.setValueCount(length);
224232
}
225-
/* splitAndTransfer validity buffer */
226-
splitAndTransferValidityBuffer(startIndex, length, to);
227-
/* splitAndTransfer data buffer */
228-
dataTransferPair.splitAndTransfer(startPoint, sliceLength);
229-
to.lastSet = length - 1;
230-
to.setValueCount(length);
231233
}
232234

233235
/*

java/vector/src/test/java/org/apache/arrow/vector/TestSplitAndTransfer.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
*/
1717
package org.apache.arrow.vector;
1818

19+
import static java.util.Arrays.asList;
1920
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
2021
import static org.junit.jupiter.api.Assertions.assertEquals;
2122
import static org.junit.jupiter.api.Assertions.assertFalse;
2223
import static org.junit.jupiter.api.Assertions.assertThrows;
2324
import static org.junit.jupiter.api.Assertions.assertTrue;
2425

2526
import java.nio.charset.StandardCharsets;
27+
import java.util.ArrayList;
2628
import java.util.HashMap;
29+
import java.util.List;
2730
import java.util.Map;
2831
import org.apache.arrow.memory.BufferAllocator;
2932
import org.apache.arrow.memory.RootAllocator;
@@ -36,6 +39,7 @@
3639
import org.apache.arrow.vector.complex.UnionVector;
3740
import org.apache.arrow.vector.types.pojo.ArrowType;
3841
import org.apache.arrow.vector.types.pojo.ArrowType.Struct;
42+
import org.apache.arrow.vector.types.pojo.Field;
3943
import org.apache.arrow.vector.types.pojo.FieldType;
4044
import org.apache.arrow.vector.util.TransferPair;
4145
import org.junit.jupiter.api.AfterEach;
@@ -198,6 +202,65 @@ public void testWithEmptyVector() {
198202
toDUV.clear();
199203
}
200204

205+
@Test
206+
public void testWithNullVector() {
207+
int valueCount = 123;
208+
int startIndex = 10;
209+
NullVector fromNullVector = new NullVector("nullVector");
210+
fromNullVector.setValueCount(valueCount);
211+
TransferPair transferPair = fromNullVector.getTransferPair(fromNullVector.getAllocator());
212+
transferPair.splitAndTransfer(startIndex, valueCount - startIndex);
213+
NullVector toNullVector = (NullVector) transferPair.getTo();
214+
215+
assertEquals(valueCount - startIndex, toNullVector.getValueCount());
216+
// no allocations to clear for NullVector
217+
}
218+
219+
@Test
220+
public void testWithZeroVector() {
221+
ZeroVector fromZeroVector = new ZeroVector("zeroVector");
222+
TransferPair transferPair = fromZeroVector.getTransferPair(fromZeroVector.getAllocator());
223+
transferPair.splitAndTransfer(0, 0);
224+
ZeroVector toZeroVector = (ZeroVector) transferPair.getTo();
225+
226+
assertEquals(0, toZeroVector.getValueCount());
227+
// no allocations to clear for ZeroVector
228+
}
229+
230+
@Test
231+
public void testListVectorWithEmptyMapVector() {
232+
// List<element: Map(false)<entries: Struct<key: Utf8 not null, value: Utf8> not null>>
233+
int valueCount = 1;
234+
List<Field> children = new ArrayList<>();
235+
children.add(new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null));
236+
children.add(new Field("value", FieldType.nullable(new ArrowType.Utf8()), null));
237+
Field structField =
238+
new Field("entries", FieldType.notNullable(ArrowType.Struct.INSTANCE), children);
239+
240+
Field mapField =
241+
new Field("element", FieldType.notNullable(new ArrowType.Map(false)), asList(structField));
242+
243+
Field listField = new Field("list", FieldType.nullable(new ArrowType.List()), asList(mapField));
244+
245+
ListVector fromListVector = (ListVector) listField.createVector(allocator);
246+
fromListVector.allocateNew();
247+
fromListVector.setValueCount(valueCount);
248+
249+
// child vector is empty
250+
MapVector dataVector = (MapVector) fromListVector.getDataVector();
251+
dataVector.allocateNew();
252+
// unset capacity to mimic observed failure mode
253+
dataVector.getOffsetBuffer().capacity(0);
254+
255+
TransferPair transferPair = fromListVector.getTransferPair(fromListVector.getAllocator());
256+
transferPair.splitAndTransfer(0, valueCount);
257+
ListVector toListVector = (ListVector) transferPair.getTo();
258+
259+
assertEquals(valueCount, toListVector.getValueCount());
260+
fromListVector.clear();
261+
toListVector.clear();
262+
}
263+
201264
@Test /* VarCharVector */
202265
public void test() throws Exception {
203266
try (final VarCharVector varCharVector = new VarCharVector("myvector", allocator)) {

0 commit comments

Comments
 (0)