blob: f6ff88571ea1a40c012b6036daf63a250f80bb0a [file]
/*
* 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.c;
import static org.apache.arrow.vector.testing.ValueVectorDataPopulator.setVector;
import static org.junit.jupiter.api.Assertions.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.BaseLargeVariableWidthVector;
import org.apache.arrow.vector.BaseVariableWidthVector;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.BitVector;
import org.apache.arrow.vector.DateDayVector;
import org.apache.arrow.vector.DateMilliVector;
import org.apache.arrow.vector.DecimalVector;
import org.apache.arrow.vector.DurationVector;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.FixedSizeBinaryVector;
import org.apache.arrow.vector.Float2Vector;
import org.apache.arrow.vector.Float4Vector;
import org.apache.arrow.vector.Float8Vector;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.IntervalDayVector;
import org.apache.arrow.vector.IntervalYearVector;
import org.apache.arrow.vector.LargeVarBinaryVector;
import org.apache.arrow.vector.LargeVarCharVector;
import org.apache.arrow.vector.NullVector;
import org.apache.arrow.vector.SmallIntVector;
import org.apache.arrow.vector.TimeMicroVector;
import org.apache.arrow.vector.TimeMilliVector;
import org.apache.arrow.vector.TimeNanoVector;
import org.apache.arrow.vector.TimeSecVector;
import org.apache.arrow.vector.TimeStampMicroTZVector;
import org.apache.arrow.vector.TimeStampMicroVector;
import org.apache.arrow.vector.TimeStampMilliTZVector;
import org.apache.arrow.vector.TimeStampMilliVector;
import org.apache.arrow.vector.TimeStampNanoTZVector;
import org.apache.arrow.vector.TimeStampNanoVector;
import org.apache.arrow.vector.TimeStampSecTZVector;
import org.apache.arrow.vector.TimeStampSecVector;
import org.apache.arrow.vector.TinyIntVector;
import org.apache.arrow.vector.UInt1Vector;
import org.apache.arrow.vector.UInt2Vector;
import org.apache.arrow.vector.UInt4Vector;
import org.apache.arrow.vector.UInt8Vector;
import org.apache.arrow.vector.UuidVector;
import org.apache.arrow.vector.ValueVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.ViewVarBinaryVector;
import org.apache.arrow.vector.ViewVarCharVector;
import org.apache.arrow.vector.ZeroVector;
import org.apache.arrow.vector.compare.VectorEqualsVisitor;
import org.apache.arrow.vector.complex.FixedSizeListVector;
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;
import org.apache.arrow.vector.complex.RunEndEncodedVector;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.complex.UnionVector;
import org.apache.arrow.vector.complex.impl.UnionMapWriter;
import org.apache.arrow.vector.extension.UuidType;
import org.apache.arrow.vector.holders.IntervalDayHolder;
import org.apache.arrow.vector.holders.NullableLargeVarBinaryHolder;
import org.apache.arrow.vector.holders.NullableUInt4Holder;
import org.apache.arrow.vector.table.Table;
import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.ArrowType.ExtensionType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.types.pojo.Schema;
import org.apache.arrow.vector.util.TransferPair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class RoundtripTest {
private static final String EMPTY_SCHEMA_PATH = "";
private RootAllocator allocator = null;
private BufferAllocator childAllocator = null;
@BeforeEach
public void setUp() {
allocator = new RootAllocator(Long.MAX_VALUE);
childAllocator = allocator.newChildAllocator("child", 0, Long.MAX_VALUE);
}
@AfterEach
public void tearDown() {
childAllocator.close();
allocator.close();
}
FieldVector vectorRoundtrip(FieldVector vector) {
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
// Producer creates structures from existing memory pointers
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportVector(allocator, vector, null, arrowArray, arrowSchema);
}
// Consumer imports vector
FieldVector imported =
Data.importVector(childAllocator, consumerArrowArray, consumerArrowSchema, null);
if (!(imported instanceof NullVector)) {
assertEquals(childAllocator, imported.getAllocator());
}
// Check that transfers work
TransferPair pair = imported.getTransferPair(allocator);
pair.transfer();
return (FieldVector) pair.getTo();
}
}
VectorSchemaRoot vectorSchemaRootRoundtrip(VectorSchemaRoot root) {
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
// Producer creates structures from existing memory pointers
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportVectorSchemaRoot(allocator, root, null, arrowArray, arrowSchema);
}
// Consumer imports vector
return Data.importVectorSchemaRoot(allocator, consumerArrowArray, consumerArrowSchema, null);
}
}
boolean roundtrip(FieldVector vector, Class<?> clazz) {
List<ArrowBuf> fieldBuffers = vector.getFieldBuffers();
List<Integer> orgRefCnts =
fieldBuffers.stream().map(buf -> buf.refCnt()).collect(Collectors.toList());
long orgMemorySize = allocator.getAllocatedMemory();
boolean result = false;
try (ValueVector imported = vectorRoundtrip(vector)) {
assertTrue(
clazz.isInstance(imported),
String.format("expected %s but was %s", clazz, imported.getClass()));
result = VectorEqualsVisitor.vectorEquals(vector, imported);
if (imported instanceof BaseVariableWidthVector
|| imported instanceof BaseLargeVariableWidthVector) {
ArrowBuf offsetBuffer = imported.getOffsetBuffer();
assertTrue(offsetBuffer.capacity() > 0);
assertEquals(0, offsetBuffer.getInt(0));
}
}
// Check that the ref counts of the buffers are the same after the roundtrip
IntStream.range(0, orgRefCnts.size())
.forEach(
i -> {
ArrowBuf buf = fieldBuffers.get(i);
assertEquals(buf.refCnt(), orgRefCnts.get(i));
});
assertEquals(orgMemorySize, allocator.getAllocatedMemory());
return result;
}
@Test
public void testBitVector() {
BitVector imported;
try (final BitVector vector = new BitVector(EMPTY_SCHEMA_PATH, allocator)) {
vector.allocateNew(1024);
vector.setValueCount(1024);
// Put and set a few values
vector.set(0, 1);
vector.set(1, 0);
vector.set(100, 0);
vector.set(1022, 1);
vector.setValueCount(1024);
imported = (BitVector) vectorRoundtrip(vector);
assertTrue(VectorEqualsVisitor.vectorEquals(vector, imported));
}
assertEquals(1, imported.get(0));
assertEquals(0, imported.get(1));
assertEquals(0, imported.get(100));
assertEquals(1, imported.get(1022));
assertEquals(1020, imported.getNullCount());
imported.close();
}
@Test
public void testIntVector() {
IntVector imported;
try (final IntVector vector = new IntVector("v", allocator)) {
setVector(vector, 1, 2, 3, null);
imported = (IntVector) vectorRoundtrip(vector);
assertTrue(VectorEqualsVisitor.vectorEquals(vector, imported));
}
assertEquals(1, imported.get(0));
assertEquals(2, imported.get(1));
assertEquals(3, imported.get(2));
assertEquals(4, imported.getValueCount());
assertEquals(1, imported.getNullCount());
imported.close();
}
@Test
public void testBigIntVector() {
BigIntVector imported;
try (final BigIntVector vector = new BigIntVector("v", allocator)) {
setVector(vector, 1L, 2L, 3L, null);
imported = (BigIntVector) vectorRoundtrip(vector);
assertTrue(VectorEqualsVisitor.vectorEquals(vector, imported));
}
assertEquals(1, imported.get(0));
assertEquals(2, imported.get(1));
assertEquals(3, imported.get(2));
assertEquals(4, imported.getValueCount());
assertEquals(1, imported.getNullCount());
imported.close();
}
@Test
public void testDateDayVector() {
DateDayVector imported;
try (final DateDayVector vector = new DateDayVector("v", allocator)) {
setVector(vector, 1, 2, 3, null);
imported = (DateDayVector) vectorRoundtrip(vector);
assertTrue(VectorEqualsVisitor.vectorEquals(vector, imported));
}
assertEquals(1, imported.get(0));
assertEquals(2, imported.get(1));
assertEquals(3, imported.get(2));
assertEquals(4, imported.getValueCount());
assertEquals(1, imported.getNullCount());
imported.close();
}
@Test
public void testDateMilliVector() {
DateMilliVector imported;
try (final DateMilliVector vector = new DateMilliVector("v", allocator)) {
setVector(vector, 1L, 2L, 3L, null);
imported = (DateMilliVector) vectorRoundtrip(vector);
assertTrue(VectorEqualsVisitor.vectorEquals(vector, imported));
}
assertEquals(1, imported.get(0));
assertEquals(2, imported.get(1));
assertEquals(3, imported.get(2));
assertEquals(4, imported.getValueCount());
assertEquals(1, imported.getNullCount());
imported.close();
}
@Test
public void testDecimalVector() {
try (final DecimalVector vector = new DecimalVector("v", allocator, 1, 1)) {
setVector(vector, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, DecimalVector.class));
}
}
@Test
public void testDurationVector() {
for (TimeUnit unit : TimeUnit.values()) {
final FieldType fieldType = FieldType.nullable(new ArrowType.Duration(unit));
try (final DurationVector vector = new DurationVector("v", fieldType, allocator)) {
setVector(vector, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, DurationVector.class));
}
}
}
@Test
public void testZeroVectorEquals() {
try (final ZeroVector vector = new ZeroVector()) {
// A ZeroVector is imported as a NullVector
assertTrue(roundtrip(vector, NullVector.class));
}
}
@Test
public void testFixedSizeBinaryVector() {
try (final FixedSizeBinaryVector vector = new FixedSizeBinaryVector("v", allocator, 2)) {
setVector(vector, new byte[] {0b0000, 0b0001}, new byte[] {0b0010, 0b0011});
assertTrue(roundtrip(vector, FixedSizeBinaryVector.class));
}
}
@Test
public void testFloat2Vector() {
try (final Float2Vector vector = new Float2Vector("v", allocator)) {
setVector(vector, 0.1f, 0.2f, 0.3f, null);
assertTrue(roundtrip(vector, Float2Vector.class));
}
}
@Test
public void testFloat4Vector() {
try (final Float4Vector vector = new Float4Vector("v", allocator)) {
setVector(vector, 0.1f, 0.2f, 0.3f, null);
assertTrue(roundtrip(vector, Float4Vector.class));
}
}
@Test
public void testFloat8Vector() {
try (final Float8Vector vector = new Float8Vector("v", allocator)) {
setVector(vector, 0.1d, 0.2d, 0.3d, null);
assertTrue(roundtrip(vector, Float8Vector.class));
}
}
@Test
public void testIntervalDayVector() {
try (final IntervalDayVector vector = new IntervalDayVector("v", allocator)) {
IntervalDayHolder value = new IntervalDayHolder();
value.days = 5;
value.milliseconds = 100;
setVector(vector, value, null);
assertTrue(roundtrip(vector, IntervalDayVector.class));
}
}
@Test
public void testIntervalYearVector() {
try (final IntervalYearVector vector = new IntervalYearVector("v", allocator)) {
setVector(vector, 1990, 2000, 2010, 2020, null);
assertTrue(roundtrip(vector, IntervalYearVector.class));
}
}
@Test
public void testSmallIntVector() {
try (final SmallIntVector vector = new SmallIntVector("v", allocator)) {
setVector(vector, (short) 0, (short) 256, null);
assertTrue(roundtrip(vector, SmallIntVector.class));
}
}
@Test
public void testTimeMicroVector() {
try (final TimeMicroVector vector = new TimeMicroVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeMicroVector.class));
}
}
@Test
public void testTimeMilliVector() {
try (final TimeMilliVector vector = new TimeMilliVector("v", allocator)) {
setVector(vector, 0, 1, 2, 3, null);
assertTrue(roundtrip(vector, TimeMilliVector.class));
}
}
@Test
public void testTimeNanoVector() {
try (final TimeNanoVector vector = new TimeNanoVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeNanoVector.class));
}
}
@Test
public void testTimeSecVector() {
try (final TimeSecVector vector = new TimeSecVector("v", allocator)) {
setVector(vector, 0, 1, 2, 3, null);
assertTrue(roundtrip(vector, TimeSecVector.class));
}
}
@Test
public void testTimeStampMicroTZVector() {
try (final TimeStampMicroTZVector vector = new TimeStampMicroTZVector("v", allocator, "UTC")) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampMicroTZVector.class));
}
}
@Test
public void testTimeStampMicroVector() {
try (final TimeStampMicroVector vector = new TimeStampMicroVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampMicroVector.class));
}
}
@Test
public void testTimeStampMilliTZVector() {
try (final TimeStampMilliTZVector vector = new TimeStampMilliTZVector("v", allocator, "UTC")) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampMilliTZVector.class));
}
}
@Test
public void testTimeStampMilliVector() {
try (final TimeStampMilliVector vector = new TimeStampMilliVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampMilliVector.class));
}
}
@Test
public void testTimeTimeStampNanoTZVector() {
try (final TimeStampNanoTZVector vector = new TimeStampNanoTZVector("v", allocator, "UTC")) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampNanoTZVector.class));
}
}
@Test
public void testTimeStampNanoVector() {
try (final TimeStampNanoVector vector = new TimeStampNanoVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampNanoVector.class));
}
}
@Test
public void testTimeStampSecTZVector() {
try (final TimeStampSecTZVector vector = new TimeStampSecTZVector("v", allocator, "UTC")) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampSecTZVector.class));
}
}
@Test
public void testTimeStampSecVector() {
try (final TimeStampSecVector vector = new TimeStampSecVector("v", allocator)) {
setVector(vector, 0L, 1L, 2L, 3L, null);
assertTrue(roundtrip(vector, TimeStampSecVector.class));
}
}
@Test
public void testTinyIntVector() {
try (final TinyIntVector vector = new TinyIntVector("v", allocator)) {
setVector(vector, (byte) 0, (byte) 1, null);
assertTrue(roundtrip(vector, TinyIntVector.class));
}
}
@Test
public void testUInt1Vector() {
try (final UInt1Vector vector = new UInt1Vector("v", allocator)) {
setVector(vector, (byte) 0, (byte) 1, null);
assertTrue(roundtrip(vector, UInt1Vector.class));
}
}
@Test
public void testUInt2Vector() {
try (final UInt2Vector vector = new UInt2Vector("v", allocator)) {
setVector(vector, '0', '1', null);
assertTrue(roundtrip(vector, UInt2Vector.class));
}
}
@Test
public void testUInt4Vector() {
try (final UInt4Vector vector = new UInt4Vector("v", allocator)) {
setVector(vector, 0, 1, null);
assertTrue(roundtrip(vector, UInt4Vector.class));
}
}
@Test
public void testUInt8Vector() {
try (final UInt8Vector vector = new UInt8Vector("v", allocator)) {
setVector(vector, 0L, 1L, null);
assertTrue(roundtrip(vector, UInt8Vector.class));
}
}
@Test
public void testNullVector() {
try (final NullVector vector = new NullVector("v", 1024)) {
assertTrue(roundtrip(vector, NullVector.class));
}
}
@Test
public void testVarBinaryVector() {
try (final VarBinaryVector vector = new VarBinaryVector("v", allocator)) {
setVector(
vector,
"abc".getBytes(StandardCharsets.UTF_8),
"def".getBytes(StandardCharsets.UTF_8),
null);
assertTrue(roundtrip(vector, VarBinaryVector.class));
}
}
@Test
public void testViewVector() {
// ViewVarCharVector with short strings
try (final ViewVarCharVector vector = new ViewVarCharVector("v1", allocator)) {
setVector(
vector,
"abc".getBytes(StandardCharsets.UTF_8),
"def".getBytes(StandardCharsets.UTF_8),
null);
assertTrue(roundtrip(vector, ViewVarCharVector.class));
}
// ViewVarCharVector with long strings
try (final ViewVarCharVector vector = new ViewVarCharVector("v2", allocator)) {
setVector(
vector,
"01234567890123".getBytes(StandardCharsets.UTF_8),
"01234567890123567".getBytes(StandardCharsets.UTF_8),
null);
assertTrue(roundtrip(vector, ViewVarCharVector.class));
}
// ViewVarBinaryVector with short values
try (final ViewVarBinaryVector vector = new ViewVarBinaryVector("v3", allocator)) {
setVector(
vector,
"abc".getBytes(StandardCharsets.UTF_8),
"def".getBytes(StandardCharsets.UTF_8),
null);
assertTrue(roundtrip(vector, ViewVarBinaryVector.class));
}
// ViewVarBinaryVector with long values
try (final ViewVarBinaryVector vector = new ViewVarBinaryVector("v4", allocator)) {
setVector(
vector,
"01234567890123".getBytes(StandardCharsets.UTF_8),
"01234567890123567".getBytes(StandardCharsets.UTF_8),
null);
assertTrue(roundtrip(vector, ViewVarBinaryVector.class));
}
List<byte[]> byteArrayList = new ArrayList<>();
for (int i = 1; i <= 500; i++) {
StringBuilder sb = new StringBuilder(i);
for (int j = 0; j < i; j++) {
sb.append(j); // or any other character
}
byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
byteArrayList.add(bytes);
}
// ViewVarCharVector with short long strings with multiple data buffers
try (final ViewVarCharVector vector = new ViewVarCharVector("v5", allocator)) {
setVector(vector, byteArrayList.toArray(new byte[0][]));
assertTrue(roundtrip(vector, ViewVarCharVector.class));
}
// ViewVarBinaryVector with short long strings with multiple data buffers
try (final ViewVarBinaryVector vector = new ViewVarBinaryVector("v6", allocator)) {
setVector(vector, byteArrayList.toArray(new byte[0][]));
assertTrue(roundtrip(vector, ViewVarBinaryVector.class));
}
}
@Test
public void testVarCharVector() {
try (final VarCharVector vector = new VarCharVector("v", allocator)) {
setVector(vector, "abc", "def", null);
assertTrue(roundtrip(vector, VarCharVector.class));
}
}
@Test
public void testEmptyVarCharVector() {
try (final VarCharVector vector = new VarCharVector("v", allocator)) {
assertTrue(roundtrip(vector, VarCharVector.class));
}
}
@Test
public void testLargeVarBinaryVector() {
try (final LargeVarBinaryVector vector = new LargeVarBinaryVector("", allocator)) {
vector.allocateNew(5, 1);
NullableLargeVarBinaryHolder nullHolder = new NullableLargeVarBinaryHolder();
nullHolder.isSet = 0;
NullableLargeVarBinaryHolder binHolder = new NullableLargeVarBinaryHolder();
binHolder.isSet = 1;
String str = "hello world";
try (ArrowBuf buf = allocator.buffer(16)) {
buf.setBytes(0, str.getBytes(StandardCharsets.UTF_8));
binHolder.start = 0;
binHolder.end = str.length();
binHolder.buffer = buf;
vector.setSafe(0, binHolder);
vector.setSafe(1, nullHolder);
assertTrue(roundtrip(vector, LargeVarBinaryVector.class));
}
}
}
@Test
public void testLargeVarCharVector() {
try (final LargeVarCharVector vector = new LargeVarCharVector("v", allocator)) {
setVector(vector, "abc", "def", null);
assertTrue(roundtrip(vector, LargeVarCharVector.class));
}
}
@Test
public void testEmptyLargeVarCharVector() {
try (final LargeVarCharVector vector = new LargeVarCharVector("v", allocator)) {
assertTrue(roundtrip(vector, LargeVarCharVector.class));
}
}
@Test
public void testListVector() {
try (final ListVector vector = ListVector.empty("v", allocator)) {
setVector(
vector,
Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()),
Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()),
new ArrayList<Integer>());
assertTrue(roundtrip(vector, ListVector.class));
}
}
@Test
public void testEmptyListVector() {
try (final ListVector vector = ListVector.empty("v", allocator)) {
setVector(vector, new ArrayList<Integer>());
assertTrue(roundtrip(vector, ListVector.class));
}
}
@Test
public void testLargeListVector() {
try (final LargeListVector vector = LargeListVector.empty("v", allocator)) {
setVector(
vector,
Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()),
Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()),
new ArrayList<Integer>());
assertTrue(roundtrip(vector, LargeListVector.class));
}
}
@Test
public void testFixedSizeListVector() {
try (final FixedSizeListVector vector = FixedSizeListVector.empty("v", 2, allocator)) {
setVector(
vector,
Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()),
Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()));
assertTrue(roundtrip(vector, FixedSizeListVector.class));
}
}
@Test
public void testListViewVector() {
try (final ListViewVector vector = ListViewVector.empty("v", allocator)) {
setVector(
vector,
Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()),
Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()),
new ArrayList<Integer>());
assertTrue(roundtrip(vector, ListViewVector.class));
}
}
@Test
public void testEmptyListViewVector() {
try (final ListViewVector vector = ListViewVector.empty("v", allocator)) {
setVector(vector, new ArrayList<Integer>());
assertTrue(roundtrip(vector, ListViewVector.class));
}
}
@Test
public void testLargeListViewVector() {
try (final LargeListViewVector vector = LargeListViewVector.empty("v", allocator)) {
setVector(
vector,
Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()),
Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()),
new ArrayList<Integer>());
assertTrue(roundtrip(vector, LargeListViewVector.class));
}
}
@Test
public void testEmptyLargeListViewVector() {
try (final LargeListViewVector vector = LargeListViewVector.empty("v", allocator)) {
setVector(vector, new ArrayList<Integer>());
assertTrue(roundtrip(vector, LargeListViewVector.class));
}
}
@Test
public void testMapVector() {
int count = 5;
try (final MapVector vector = MapVector.empty("v", allocator, false)) {
vector.allocateNew();
UnionMapWriter mapWriter = vector.getWriter();
for (int i = 0; i < count; i++) {
mapWriter.startMap();
for (int j = 0; j < i + 1; j++) {
mapWriter.startEntry();
mapWriter.key().bigInt().writeBigInt(j);
mapWriter.value().integer().writeInt(j);
mapWriter.endEntry();
}
mapWriter.endMap();
}
mapWriter.setValueCount(count);
assertTrue(roundtrip(vector, MapVector.class));
}
}
@Test
public void testUnionVector() {
final NullableUInt4Holder uInt4Holder = new NullableUInt4Holder();
uInt4Holder.value = 100;
uInt4Holder.isSet = 1;
try (UnionVector vector = UnionVector.empty("v", allocator)) {
vector.allocateNew();
// write some data
vector.setType(0, MinorType.UINT4);
vector.setSafe(0, uInt4Holder);
vector.setType(2, MinorType.UINT4);
vector.setSafe(2, uInt4Holder);
vector.setValueCount(4);
assertTrue(roundtrip(vector, UnionVector.class));
}
}
@Test
public void testStructVector() {
try (final StructVector vector = StructVector.empty("v", allocator)) {
Map<String, List<Integer>> data = new HashMap<>();
data.put("col_1", Arrays.stream(new int[] {1, 2}).boxed().collect(Collectors.toList()));
data.put("col_2", Arrays.stream(new int[] {3, 4}).boxed().collect(Collectors.toList()));
setVector(vector, data);
assertTrue(roundtrip(vector, StructVector.class));
}
}
@Test
public void testRunEndEncodedVector() {
try (final RunEndEncodedVector vector = RunEndEncodedVector.empty("v", allocator)) {
setVector(vector, List.of(1, 3), List.of(1, 2));
assertTrue(roundtrip(vector, RunEndEncodedVector.class));
}
}
@Test
public void testEmptyRunEndEncodedVector() {
try (final RunEndEncodedVector vector = RunEndEncodedVector.empty("v", allocator)) {
setVector(vector, List.of(), List.of());
assertTrue(roundtrip(vector, RunEndEncodedVector.class));
}
}
@Test
public void testExtensionTypeVector() {
final Schema schema =
new Schema(Collections.singletonList(Field.nullable("a", UuidType.INSTANCE)));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) {
// Fill with data
UUID u1 = UUID.randomUUID();
UUID u2 = UUID.randomUUID();
UuidVector vector = (UuidVector) root.getVector("a");
vector.setValueCount(2);
vector.set(0, u1);
vector.set(1, u2);
root.setRowCount(2);
// Roundtrip (export + import)
VectorSchemaRoot importedRoot = vectorSchemaRootRoundtrip(root);
// Verify correctness
assertEquals(root.getSchema(), importedRoot.getSchema());
final Field field = importedRoot.getSchema().getFields().get(0);
assertEquals(
field.getMetadata().get(ExtensionType.EXTENSION_METADATA_KEY_NAME),
UuidType.INSTANCE.extensionName());
assertEquals(
field.getMetadata().get(ExtensionType.EXTENSION_METADATA_KEY_METADATA),
UuidType.INSTANCE.serialize());
final UuidVector deserialized = (UuidVector) importedRoot.getFieldVectors().get(0);
assertEquals(vector.getValueCount(), deserialized.getValueCount());
for (int i = 0; i < vector.getValueCount(); i++) {
assertEquals(vector.isNull(i), deserialized.isNull(i));
if (!vector.isNull(i)) {
assertEquals(vector.getObject(i), deserialized.getObject(i));
}
}
importedRoot.close();
}
}
@Test
public void testVectorSchemaRoot() {
VectorSchemaRoot imported;
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
try (VectorSchemaRoot vsr = createTestVSR()) {
// Producer creates structures from existing memory pointers
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportVectorSchemaRoot(allocator, vsr, null, arrowArray, arrowSchema);
}
}
// Consumer imports vector
imported =
Data.importVectorSchemaRoot(allocator, consumerArrowArray, consumerArrowSchema, null);
}
// Ensure that imported VectorSchemaRoot is valid even after C Data Interface
// structures are closed
try (VectorSchemaRoot original = createTestVSR()) {
assertTrue(imported.equals(original));
}
imported.close();
}
/**
* Tests exporting Table and importing back to VSR. Importing back to Table is not supported at
* present.
*/
@Test
public void testTable() {
VectorSchemaRoot imported;
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
try (VectorSchemaRoot vsr = createTestVSR();
Table table = new Table(vsr)) {
// Producer creates structures from existing memory pointers
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportTable(allocator, table, null, arrowArray, arrowSchema);
}
}
// Consumer imports vector
imported =
Data.importVectorSchemaRoot(allocator, consumerArrowArray, consumerArrowSchema, null);
}
// Ensure that imported VectorSchemaRoot is valid even after C Data Interface
// structures are closed
try (VectorSchemaRoot original = createTestVSR()) {
assertTrue(imported.equals(original));
}
imported.close();
}
@Test
public void testVectorSchemaRootWithDuplicatedFieldNames() {
VectorSchemaRoot imported;
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
try (VectorSchemaRoot testVSR1 = createTestVSR();
VectorSchemaRoot testVSR2 = createTestVSR()) {
// Merge two VSRs to produce duplicated field names
final VectorSchemaRoot vsr =
new VectorSchemaRoot(
Stream.concat(
testVSR1.getFieldVectors().stream(), testVSR2.getFieldVectors().stream())
.collect(Collectors.toList()));
// Producer creates structures from existing memory pointers
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportVectorSchemaRoot(allocator, vsr, null, arrowArray, arrowSchema);
}
}
// Consumer imports vector
imported =
Data.importVectorSchemaRoot(allocator, consumerArrowArray, consumerArrowSchema, null);
}
// Ensure that imported VectorSchemaRoot is valid even after C Data Interface
// structures are closed
try (VectorSchemaRoot testVSR1 = createTestVSR();
VectorSchemaRoot testVSR2 = createTestVSR()) {
final VectorSchemaRoot original =
new VectorSchemaRoot(
Stream.concat(
testVSR1.getFieldVectors().stream(), testVSR2.getFieldVectors().stream())
.collect(Collectors.toList()));
assertTrue(imported.equals(original));
}
imported.close();
}
@Test
public void testSchema() {
Schema schema = createSchema();
// Consumer allocates empty ArrowSchema
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator)) {
// Producer fills the schema with data
exportSchema(schema, consumerArrowSchema);
// Consumer imports schema
Schema importedSchema = Data.importSchema(allocator, consumerArrowSchema, null);
assertEquals(schema.toJson(), importedSchema.toJson());
}
}
@Test
public void testSchemaStructReuse() {
Schema schema = createSchema();
// Consumer allocates empty ArrowSchema
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator)) {
// Producer fills the schema with data
exportSchema(schema, consumerArrowSchema);
// Consumer imports schema
Schema importedSchema = Data.importSchema(allocator, consumerArrowSchema, null, false);
assertEquals(schema.toJson(), importedSchema.toJson());
// Imported struct should be released but not closed
assertEquals(0, consumerArrowSchema.snapshot().release);
assertNotEquals(0, consumerArrowSchema.memoryAddress());
// Export and import again
exportSchema(schema, consumerArrowSchema);
importedSchema = Data.importSchema(allocator, consumerArrowSchema, null, false);
assertEquals(schema.toJson(), importedSchema.toJson());
assertEquals(0, consumerArrowSchema.snapshot().release);
assertNotEquals(0, consumerArrowSchema.memoryAddress());
}
}
private void exportSchema(Schema schema, ArrowSchema targetArrowSchema) {
try (ArrowSchema arrowSchema = ArrowSchema.wrap(targetArrowSchema.memoryAddress())) {
Data.exportSchema(allocator, schema, null, arrowSchema);
}
}
private static Schema createSchema() {
Field decimalField =
new Field("inner1", FieldType.nullable(new ArrowType.Decimal(19, 4, 128)), null);
Field strField = new Field("inner2", FieldType.nullable(new ArrowType.Utf8()), null);
Field itemField =
new Field(
"col1",
FieldType.nullable(new ArrowType.Struct()),
Arrays.asList(decimalField, strField));
Field intField = new Field("col2", FieldType.nullable(new ArrowType.Int(32, true)), null);
Schema schema = new Schema(Arrays.asList(itemField, intField));
return schema;
}
@Test
public void testImportedBufferAsNioBuffer() {
IntVector imported;
try (final IntVector vector = new IntVector("v", allocator)) {
setVector(vector, 1, 2, 3, null);
imported = (IntVector) vectorRoundtrip(vector);
}
ArrowBuf dataBuffer = imported.getDataBuffer();
ByteBuffer nioBuffer = dataBuffer.nioBuffer().asReadOnlyBuffer();
nioBuffer.order(ByteOrder.nativeOrder());
assertEquals(1, nioBuffer.getInt(0));
assertEquals(2, nioBuffer.getInt(1 << 2));
assertEquals(3, nioBuffer.getInt(2 << 2));
imported.close();
}
@Test
public void testImportReleasedArray() {
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
// Producer creates structures from existing memory pointers
try (final NullVector vector = new NullVector()) {
exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
}
// Release array structure
consumerArrowArray.markReleased();
// Consumer tried to imports vector but fails
Exception e =
assertThrows(
IllegalStateException.class,
() -> {
Data.importVector(allocator, consumerArrowArray, consumerArrowSchema, null);
});
assertEquals("Cannot import released ArrowArray", e.getMessage());
}
}
@Test
public void testArrayStructReuse() {
// Consumer allocates empty structures
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
// Producer creates structures from existing memory pointers
try (final NullVector vector = new NullVector()) {
exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
}
Data.importVector(allocator, consumerArrowArray, consumerArrowSchema, null, false);
// Imported structs should be released but not closed
assertEquals(0, consumerArrowSchema.snapshot().release);
assertNotEquals(0, consumerArrowSchema.memoryAddress());
assertEquals(0, consumerArrowArray.snapshot().release);
assertNotEquals(0, consumerArrowArray.memoryAddress());
try (final NullVector vector = new NullVector()) {
exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
}
Data.importVector(allocator, consumerArrowArray, consumerArrowSchema, null, false);
// Imported structs should be released but not closed
assertEquals(0, consumerArrowSchema.snapshot().release);
assertNotEquals(0, consumerArrowSchema.memoryAddress());
assertEquals(0, consumerArrowArray.snapshot().release);
assertNotEquals(0, consumerArrowArray.memoryAddress());
}
}
private void exportFieldVector(
FieldVector vector, ArrowSchema consumerArrowSchema, ArrowArray consumerArrowArray) {
try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
// Producer exports vector into the C Data Interface structures
Data.exportVector(allocator, vector, null, arrowArray, arrowSchema);
}
}
private VectorSchemaRoot createTestVSR() {
BitVector bitVector = new BitVector("boolean", allocator);
Map<String, String> metadata = new HashMap<>();
metadata.put("key", "value");
FieldType fieldType = new FieldType(true, ArrowType.Utf8.INSTANCE, null, metadata);
VarCharVector varCharVector = new VarCharVector("varchar", fieldType, allocator);
bitVector.allocateNew();
varCharVector.allocateNew();
for (int i = 0; i < 10; i++) {
bitVector.setSafe(i, i % 2 == 0 ? 0 : 1);
varCharVector.setSafe(i, ("test" + i).getBytes(StandardCharsets.UTF_8));
}
bitVector.setValueCount(10);
varCharVector.setValueCount(10);
List<Field> fields = Arrays.asList(bitVector.getField(), varCharVector.getField());
List<FieldVector> vectors = Arrays.asList(bitVector, varCharVector);
return new VectorSchemaRoot(fields, vectors);
}
}