From f6c37f96254c1cebfa23516c23c3c7d6594a20d6 Mon Sep 17 00:00:00 2001 From: Maksim Yegorov <59841139+maksimyego-db@users.noreply.github.com> Date: Sat, 2 Nov 2024 19:06:41 -0400 Subject: [PATCH] GH-44626: [Java] fix SplitAndTransfer throws for empty MapVector Signed-off-by: Maksim Yegorov --- .../arrow/vector/complex/MapVector.java | 32 ++++++++-------- .../arrow/vector/TestSplitAndTransfer.java | 38 +++++++++++++++++++ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/java/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java b/java/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java index 5c6b9b225569f..23cda8401b0bf 100644 --- a/java/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java +++ b/java/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java @@ -211,23 +211,25 @@ public void splitAndTransfer(int startIndex, int length) { startIndex, length, valueCount); - final int startPoint = offsetBuffer.getInt(startIndex * OFFSET_WIDTH); - final int sliceLength = - offsetBuffer.getInt((startIndex + length) * OFFSET_WIDTH) - startPoint; to.clear(); - to.offsetBuffer = to.allocateOffsetBuffer((length + 1) * OFFSET_WIDTH); - /* splitAndTransfer offset buffer */ - for (int i = 0; i < length + 1; i++) { - final int relativeOffset = - offsetBuffer.getInt((startIndex + i) * OFFSET_WIDTH) - startPoint; - to.offsetBuffer.setInt(i * OFFSET_WIDTH, relativeOffset); + if (length > 0) { + final int startPoint = offsetBuffer.getInt(startIndex * OFFSET_WIDTH); + final int sliceLength = + offsetBuffer.getInt((startIndex + length) * OFFSET_WIDTH) - startPoint; + to.offsetBuffer = to.allocateOffsetBuffer((length + 1) * OFFSET_WIDTH); + /* splitAndTransfer offset buffer */ + for (int i = 0; i < length + 1; i++) { + final int relativeOffset = + offsetBuffer.getInt((startIndex + i) * OFFSET_WIDTH) - startPoint; + to.offsetBuffer.setInt(i * OFFSET_WIDTH, relativeOffset); + } + /* splitAndTransfer validity buffer */ + splitAndTransferValidityBuffer(startIndex, length, to); + /* splitAndTransfer data buffer */ + dataTransferPair.splitAndTransfer(startPoint, sliceLength); + to.lastSet = length - 1; + to.setValueCount(length); } - /* splitAndTransfer validity buffer */ - splitAndTransferValidityBuffer(startIndex, length, to); - /* splitAndTransfer data buffer */ - dataTransferPair.splitAndTransfer(startPoint, sliceLength); - to.lastSet = length - 1; - to.setValueCount(length); } /* diff --git a/java/vector/src/test/java/org/apache/arrow/vector/TestSplitAndTransfer.java b/java/vector/src/test/java/org/apache/arrow/vector/TestSplitAndTransfer.java index 6aace956214ff..adf4eba10cb39 100644 --- a/java/vector/src/test/java/org/apache/arrow/vector/TestSplitAndTransfer.java +++ b/java/vector/src/test/java/org/apache/arrow/vector/TestSplitAndTransfer.java @@ -16,6 +16,7 @@ */ package org.apache.arrow.vector; +import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -23,7 +24,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; @@ -36,6 +39,7 @@ import org.apache.arrow.vector.complex.UnionVector; import org.apache.arrow.vector.types.pojo.ArrowType; import org.apache.arrow.vector.types.pojo.ArrowType.Struct; +import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.FieldType; import org.apache.arrow.vector.util.TransferPair; import org.junit.jupiter.api.AfterEach; @@ -223,6 +227,40 @@ public void testWithZeroVector() { // no allocations to clear for ZeroVector } + @Test + public void testListVectorWithEmptyMapVector() { + // List not null>> + int valueCount = 1; + List children = new ArrayList<>(); + children.add(new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null)); + children.add(new Field("value", FieldType.nullable(new ArrowType.Utf8()), null)); + Field structField = + new Field("entries", FieldType.notNullable(ArrowType.Struct.INSTANCE), children); + + Field mapField = + new Field("element", FieldType.notNullable(new ArrowType.Map(false)), asList(structField)); + + Field listField = new Field("list", FieldType.nullable(new ArrowType.List()), asList(mapField)); + + ListVector fromListVector = (ListVector) listField.createVector(allocator); + fromListVector.allocateNew(); + fromListVector.setValueCount(valueCount); + + // child vector is empty + MapVector dataVector = (MapVector) fromListVector.getDataVector(); + dataVector.allocateNew(); + // unset capacity to mimic observed failure mode + dataVector.getOffsetBuffer().capacity(0); + + TransferPair transferPair = fromListVector.getTransferPair(fromListVector.getAllocator()); + transferPair.splitAndTransfer(0, valueCount); + ListVector toListVector = (ListVector) transferPair.getTo(); + + assertEquals(valueCount, toListVector.getValueCount()); + fromListVector.clear(); + toListVector.clear(); + } + @Test /* VarCharVector */ public void test() throws Exception { try (final VarCharVector varCharVector = new VarCharVector("myvector", allocator)) {