diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java index 2da7eb057e..44fecaa6db 100644 --- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java +++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java @@ -960,4 +960,19 @@ public void validate() { public void endValue(int index, int size) { sizeBuffer.setInt((long) index * SIZE_WIDTH, size); } + + /** Get data vector start index with the given list index. */ + public int getElementStartIndex(int index) { + return offsetBuffer.getInt((long) index * OFFSET_WIDTH); + } + + /** Get the size at the given index. */ + private int getElementSize(int index) { + return sizeBuffer.getInt((long) index * SIZE_WIDTH); + } + + /** Get data vector end index with the given list index. */ + public int getElementEndIndex(int index) { + return getElementStartIndex(index) + getElementSize(index); + } } diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListReader.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListReader.java index be236c3166..a919f10297 100644 --- a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListReader.java +++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListReader.java @@ -53,9 +53,25 @@ public boolean isSet() { @Override public void setPosition(int index) { + int valueCount = vector.getValueCount(); + if (valueCount == 0 && index == 0) { + setEmptyPosition(index); + return; + } + + UnionListReaderPositionValidator.checkIndex(index, valueCount); + UnionListReaderPositionValidator.checkListBufferReadable( + vector.getOffsetBuffer(), index, OFFSET_WIDTH); + + super.setPosition(index); + currentOffset = vector.getElementStartIndex(index) - 1; + maxOffset = vector.getElementEndIndex(index); + } + + private void setEmptyPosition(int index) { super.setPosition(index); - currentOffset = vector.getOffsetBuffer().getLong((long) index * OFFSET_WIDTH) - 1; - maxOffset = vector.getOffsetBuffer().getLong(((long) index + 1L) * OFFSET_WIDTH); + currentOffset = 0; + maxOffset = 0; } @Override diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListViewReader.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListViewReader.java index 4bcd028de3..2f09598964 100644 --- a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListViewReader.java +++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionLargeListViewReader.java @@ -19,7 +19,6 @@ import static org.apache.arrow.memory.util.LargeMemoryUtil.checkedCastToInt; import org.apache.arrow.vector.ValueVector; -import org.apache.arrow.vector.complex.BaseLargeRepeatedValueViewVector; import org.apache.arrow.vector.complex.LargeListViewVector; import org.apache.arrow.vector.complex.reader.FieldReader; import org.apache.arrow.vector.holders.UnionHolder; @@ -56,18 +55,29 @@ public boolean isSet() { @Override public void setPosition(int index) { - super.setPosition(index); - if (vector.getOffsetBuffer().capacity() == 0) { - currentOffset = 0; - size = 0; - } else { - currentOffset = - vector - .getOffsetBuffer() - .getInt(index * (long) BaseLargeRepeatedValueViewVector.OFFSET_WIDTH); - size = - vector.getSizeBuffer().getInt(index * (long) BaseLargeRepeatedValueViewVector.SIZE_WIDTH); + int valueCount = vector.getValueCount(); + if (valueCount == 0 && index == 0) { + setEmptyPosition(index); + return; } + + UnionListReaderPositionValidator.checkIndex(index, valueCount); + UnionListReaderPositionValidator.checkListViewBufferReadable( + vector.getOffsetBuffer(), + vector.getSizeBuffer(), + index, + LargeListViewVector.OFFSET_WIDTH, + LargeListViewVector.SIZE_WIDTH); + + super.setPosition(index); + currentOffset = vector.getElementStartIndex(index); + size = vector.getElementEndIndex(index) - currentOffset; + } + + private void setEmptyPosition(int index) { + super.setPosition(index); + currentOffset = 0; + size = 0; } @Override diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReader.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReader.java index 014608afee..4273290efa 100644 --- a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReader.java +++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReader.java @@ -52,14 +52,25 @@ public boolean isSet() { @Override public void setPosition(int index) { - super.setPosition(index); - if (vector.getOffsetBuffer().capacity() == 0) { - currentOffset = 0; - maxOffset = 0; - } else { - currentOffset = vector.getOffsetBuffer().getInt(index * (long) OFFSET_WIDTH) - 1; - maxOffset = vector.getOffsetBuffer().getInt((index + 1) * (long) OFFSET_WIDTH); + int valueCount = vector.getValueCount(); + if (valueCount == 0 && index == 0) { + setEmptyPosition(index); + return; } + + UnionListReaderPositionValidator.checkIndex(index, valueCount); + UnionListReaderPositionValidator.checkListBufferReadable( + vector.getOffsetBuffer(), index, OFFSET_WIDTH); + + super.setPosition(index); + currentOffset = vector.getElementStartIndex(index) - 1; + maxOffset = vector.getElementEndIndex(index); + } + + private void setEmptyPosition(int index) { + super.setPosition(index); + currentOffset = 0; + maxOffset = 0; } @Override diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReaderPositionValidator.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReaderPositionValidator.java new file mode 100644 index 0000000000..7b7eeb0879 --- /dev/null +++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListReaderPositionValidator.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.arrow.vector.complex.impl; + +import org.apache.arrow.memory.ArrowBuf; + +/** + * Utility class for validating list and list view reader positions. + * + *
A list vector contains an offset buffer that denotes lists boundaries. A list view vector
+ * contains an offset buffer and a size buffer.
+ */
+final class UnionListReaderPositionValidator {
+
+ private UnionListReaderPositionValidator() {}
+
+ /** Check if the given index is within the current value count of the vector. */
+ static void checkIndex(int index, int valueCount) {
+ if (index < 0 || index >= valueCount) {
+ throw new IndexOutOfBoundsException(
+ String.format("index: %s, expected range [0, %s)", index, valueCount));
+ }
+ }
+
+ /** Check that the list offset buffer contains entries for {@code index} and {@code index + 1}. */
+ static void checkListBufferReadable(ArrowBuf offsetBuffer, int index, long offsetWidth) {
+ long requiredBytes = ((long) index + 2L) * offsetWidth;
+ long capacity = offsetBuffer.capacity();
+ if (capacity < requiredBytes) {
+ throw new IndexOutOfBoundsException(
+ String.format(
+ "Offset buffer has capacity %s but reading index %s requires %s bytes",
+ capacity, index, requiredBytes));
+ }
+ }
+
+ /** Check that the list view offset and size buffers contain entries for {@code index}. */
+ static void checkListViewBufferReadable(
+ ArrowBuf offsetBuffer, ArrowBuf sizeBuffer, int index, long offsetWidth, long sizeWidth) {
+ long requiredOffsetBytes = ((long) index + 1L) * offsetWidth;
+ long offsetCapacity = offsetBuffer.capacity();
+ if (offsetCapacity < requiredOffsetBytes) {
+ throw new IndexOutOfBoundsException(
+ String.format(
+ "Offset buffer has capacity %s but reading index %s requires %s bytes",
+ offsetCapacity, index, requiredOffsetBytes));
+ }
+
+ long requiredSizeBytes = ((long) index + 1L) * sizeWidth;
+ long sizeCapacity = sizeBuffer.capacity();
+ if (sizeCapacity < requiredSizeBytes) {
+ throw new IndexOutOfBoundsException(
+ String.format(
+ "Size buffer has capacity %s but reading index %s requires %s bytes",
+ sizeCapacity, index, requiredSizeBytes));
+ }
+ }
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListViewReader.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListViewReader.java
index 17ac1150fd..3e341da298 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListViewReader.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionListViewReader.java
@@ -17,7 +17,6 @@
package org.apache.arrow.vector.complex.impl;
import org.apache.arrow.vector.ValueVector;
-import org.apache.arrow.vector.complex.BaseRepeatedValueViewVector;
import org.apache.arrow.vector.complex.ListViewVector;
import org.apache.arrow.vector.complex.reader.FieldReader;
import org.apache.arrow.vector.holders.UnionHolder;
@@ -54,15 +53,29 @@ public boolean isSet() {
@Override
public void setPosition(int index) {
- super.setPosition(index);
- if (vector.getOffsetBuffer().capacity() == 0) {
- currentOffset = 0;
- size = 0;
- } else {
- currentOffset =
- vector.getOffsetBuffer().getInt(index * (long) BaseRepeatedValueViewVector.OFFSET_WIDTH);
- size = vector.getSizeBuffer().getInt(index * (long) BaseRepeatedValueViewVector.SIZE_WIDTH);
+ int valueCount = vector.getValueCount();
+ if (valueCount == 0 && index == 0) {
+ setEmptyPosition(index);
+ return;
}
+
+ UnionListReaderPositionValidator.checkIndex(index, valueCount);
+ UnionListReaderPositionValidator.checkListViewBufferReadable(
+ vector.getOffsetBuffer(),
+ vector.getSizeBuffer(),
+ index,
+ ListViewVector.OFFSET_WIDTH,
+ ListViewVector.SIZE_WIDTH);
+
+ super.setPosition(index);
+ currentOffset = vector.getElementStartIndex(index);
+ size = vector.getElementEndIndex(index) - currentOffset;
+ }
+
+ private void setEmptyPosition(int index) {
+ super.setPosition(index);
+ currentOffset = 0;
+ size = 0;
}
@Override
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
index 80d03cae6d..d64c766eef 100644
--- a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
+++ b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
@@ -22,6 +22,7 @@
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
@@ -29,6 +30,7 @@
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -48,6 +50,9 @@
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.ViewVarCharVector;
+import org.apache.arrow.vector.complex.BaseRepeatedValueVector;
+import org.apache.arrow.vector.complex.LargeListVector;
+import org.apache.arrow.vector.complex.LargeListViewVector;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.ListViewVector;
import org.apache.arrow.vector.complex.MapVector;
@@ -59,6 +64,8 @@
import org.apache.arrow.vector.complex.impl.NullableStructWriter;
import org.apache.arrow.vector.complex.impl.SingleStructReaderImpl;
import org.apache.arrow.vector.complex.impl.SingleStructWriter;
+import org.apache.arrow.vector.complex.impl.UnionLargeListReader;
+import org.apache.arrow.vector.complex.impl.UnionLargeListViewReader;
import org.apache.arrow.vector.complex.impl.UnionListReader;
import org.apache.arrow.vector.complex.impl.UnionListViewReader;
import org.apache.arrow.vector.complex.impl.UnionListViewWriter;
@@ -89,6 +96,7 @@
import org.apache.arrow.vector.holders.NullableUuidHolder;
import org.apache.arrow.vector.holders.TimeStampMilliTZHolder;
import org.apache.arrow.vector.holders.UuidHolder;
+import org.apache.arrow.vector.ipc.message.ArrowFieldNode;
import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
@@ -2054,6 +2062,199 @@ public void testListViewOfListViewOfListViewWriterWithNulls() {
}
}
+ @Test
+ public void testUnionListReaderSetPositionOnEmptyIpcOffsetBuffer() {
+ try (ListVector listVector = ListVector.empty("list", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(0);
+ ArrowBuf offsetBuffer = allocator.buffer(BaseRepeatedValueVector.OFFSET_WIDTH)) {
+ offsetBuffer.setInt(0, 0);
+ offsetBuffer.writerIndex(BaseRepeatedValueVector.OFFSET_WIDTH);
+ listVector.loadFieldBuffers(
+ new ArrowFieldNode(0, 0), Arrays.asList(validityBuffer, offsetBuffer));
+
+ assertEquals(0, listVector.getValueCount());
+ assertEquals(BaseRepeatedValueVector.OFFSET_WIDTH, listVector.getOffsetBuffer().capacity());
+ assertEmptyPosition(new UnionListReader(listVector));
+ }
+ }
+
+ @Test
+ public void testUnionLargeListReaderSetPositionOnEmptyIpcOffsetBuffer() {
+ try (LargeListVector listVector = LargeListVector.empty("largeList", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(0);
+ ArrowBuf offsetBuffer = allocator.buffer(LargeListVector.OFFSET_WIDTH)) {
+ offsetBuffer.setLong(0, 0);
+ offsetBuffer.writerIndex(LargeListVector.OFFSET_WIDTH);
+ listVector.loadFieldBuffers(
+ new ArrowFieldNode(0, 0), Arrays.asList(validityBuffer, offsetBuffer));
+
+ assertEquals(0, listVector.getValueCount());
+ assertEquals(LargeListVector.OFFSET_WIDTH, listVector.getOffsetBuffer().capacity());
+ assertEmptyPosition(new UnionLargeListReader(listVector));
+ }
+ }
+
+ @Test
+ public void testUnionListViewReaderSetPositionOnEmptyIpcBuffers() {
+ try (ListViewVector listViewVector = ListViewVector.empty("listView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(0);
+ ArrowBuf offsetBuffer = allocator.buffer(0);
+ ArrowBuf sizeBuffer = allocator.buffer(0)) {
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(0, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ assertEquals(0, listViewVector.getValueCount());
+ assertEquals(0, listViewVector.getOffsetBuffer().capacity());
+ assertEquals(0, listViewVector.getSizeBuffer().capacity());
+ assertEmptyPosition(new UnionListViewReader(listViewVector));
+ }
+ }
+
+ @Test
+ public void testUnionLargeListViewReaderSetPositionOnEmptyIpcBuffers() {
+ try (LargeListViewVector listViewVector =
+ LargeListViewVector.empty("largeListView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(0);
+ ArrowBuf offsetBuffer = allocator.buffer(0);
+ ArrowBuf sizeBuffer = allocator.buffer(0)) {
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(0, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ assertEquals(0, listViewVector.getValueCount());
+ assertEquals(0, listViewVector.getOffsetBuffer().capacity());
+ assertEquals(0, listViewVector.getSizeBuffer().capacity());
+ assertEmptyPosition(new UnionLargeListViewReader(listViewVector));
+ }
+ }
+
+ @Test
+ public void testUnionListReaderSetPositionChecksIpcOffsetBuffer() {
+ try (ListVector listVector = ListVector.empty("list", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(BaseRepeatedValueVector.OFFSET_WIDTH)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ offsetBuffer.setInt(0, 0);
+ offsetBuffer.writerIndex(BaseRepeatedValueVector.OFFSET_WIDTH);
+ listVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionListReader(listVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Offset buffer has capacity"));
+ }
+ }
+
+ @Test
+ public void testUnionLargeListReaderSetPositionChecksIpcOffsetBuffer() {
+ try (LargeListVector listVector = LargeListVector.empty("largeList", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(LargeListVector.OFFSET_WIDTH)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ offsetBuffer.setLong(0, 0);
+ offsetBuffer.writerIndex(LargeListVector.OFFSET_WIDTH);
+ listVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionLargeListReader(listVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Offset buffer has capacity"));
+ }
+ }
+
+ @Test
+ public void testUnionListViewReaderSetPositionChecksIpcBuffers() {
+ try (ListViewVector listViewVector = ListViewVector.empty("listView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(0);
+ ArrowBuf sizeBuffer = allocator.buffer(ListViewVector.SIZE_WIDTH)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ sizeBuffer.setInt(0, 0);
+ sizeBuffer.writerIndex(ListViewVector.SIZE_WIDTH);
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionListViewReader(listViewVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Offset buffer has capacity"));
+ }
+
+ try (ListViewVector listViewVector = ListViewVector.empty("listView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(ListViewVector.OFFSET_WIDTH);
+ ArrowBuf sizeBuffer = allocator.buffer(0)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ offsetBuffer.setInt(0, 0);
+ offsetBuffer.writerIndex(ListViewVector.OFFSET_WIDTH);
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionListViewReader(listViewVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Size buffer has capacity"));
+ }
+ }
+
+ @Test
+ public void testUnionLargeListViewReaderSetPositionChecksIpcBuffers() {
+ try (LargeListViewVector listViewVector =
+ LargeListViewVector.empty("largeListView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(0);
+ ArrowBuf sizeBuffer = allocator.buffer(LargeListViewVector.SIZE_WIDTH)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ sizeBuffer.setLong(0, 0);
+ sizeBuffer.writerIndex(LargeListViewVector.SIZE_WIDTH);
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionLargeListViewReader(listViewVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Offset buffer has capacity"));
+ }
+
+ try (LargeListViewVector listViewVector =
+ LargeListViewVector.empty("largeListView", allocator);
+ ArrowBuf validityBuffer = allocator.buffer(1);
+ ArrowBuf offsetBuffer = allocator.buffer(LargeListViewVector.OFFSET_WIDTH);
+ ArrowBuf sizeBuffer = allocator.buffer(0)) {
+ validityBuffer.setByte(0, 1);
+ validityBuffer.writerIndex(1);
+ offsetBuffer.setLong(0, 0);
+ offsetBuffer.writerIndex(LargeListViewVector.OFFSET_WIDTH);
+ listViewVector.loadFieldBuffers(
+ new ArrowFieldNode(1, 0), Arrays.asList(validityBuffer, offsetBuffer, sizeBuffer));
+
+ IndexOutOfBoundsException exception =
+ assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> new UnionLargeListViewReader(listViewVector).setPosition(0));
+ assertTrue(exception.getMessage().contains("Size buffer has capacity"));
+ }
+ }
+
+ private void assertEmptyPosition(FieldReader reader) {
+ reader.setPosition(0);
+ assertEquals(0, reader.size());
+ assertFalse(reader.next());
+ assertThrows(IndexOutOfBoundsException.class, () -> reader.setPosition(-1));
+ assertThrows(IndexOutOfBoundsException.class, () -> reader.setPosition(1));
+ }
+
@Test
public void testStructOfList() {
try (StructVector structVector = StructVector.empty("struct1", allocator)) {
diff --git a/vector/src/test/java/org/apache/arrow/vector/ipc/TestRoundTrip.java b/vector/src/test/java/org/apache/arrow/vector/ipc/TestRoundTrip.java
index 65a3791dd4..c61ad65026 100644
--- a/vector/src/test/java/org/apache/arrow/vector/ipc/TestRoundTrip.java
+++ b/vector/src/test/java/org/apache/arrow/vector/ipc/TestRoundTrip.java
@@ -50,8 +50,11 @@
import org.apache.arrow.vector.TinyIntVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.VectorUnloader;
+import org.apache.arrow.vector.complex.BaseRepeatedValueVector;
import org.apache.arrow.vector.complex.FixedSizeListVector;
+import org.apache.arrow.vector.complex.MapVector;
import org.apache.arrow.vector.complex.StructVector;
+import org.apache.arrow.vector.complex.reader.FieldReader;
import org.apache.arrow.vector.dictionary.DictionaryProvider;
import org.apache.arrow.vector.ipc.message.ArrowBlock;
import org.apache.arrow.vector.ipc.message.ArrowBuffer;
@@ -326,6 +329,85 @@ public void testMetadata(String name, IpcOption writeOption) throws Exception {
}
}
+ @ParameterizedTest(name = "options = {0}")
+ @MethodSource("getWriteOption")
+ public void testEmptyUnionListReadersAfterIpc(String name, IpcOption writeOption)
+ throws Exception {
+ Field structField =
+ new Field(
+ "struct",
+ FieldType.nullable(ArrowType.Struct.INSTANCE),
+ Collections2.asImmutableList(
+ listField("list", ArrowType.List.INSTANCE),
+ listField("largeList", ArrowType.LargeList.INSTANCE),
+ mapField("map")));
+ Schema schema = new Schema(Collections2.asImmutableList(structField));
+
+ try (final BufferAllocator originalVectorAllocator =
+ allocator.newChildAllocator("original vectors", 0, allocator.getLimit());
+ final StructVector vector =
+ (StructVector) structField.createVector(originalVectorAllocator)) {
+ vector.allocateNewSafe();
+ vector.setValueCount(0);
+
+ List