blob: 5342dc7d427fee349f590b89c5b977bc842feb44 [file] [log] [blame]
/*
* 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.drill.exec.record.vector;
import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.drill.categories.UnlikelyTest;
import org.apache.drill.categories.VectorTest;
import org.apache.drill.common.AutoCloseables;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.types.TypeProtos;
import org.apache.drill.common.types.Types;
import org.apache.drill.exec.ExecTest;
import org.apache.drill.exec.exception.OversizedAllocationException;
import org.apache.drill.exec.expr.TypeHelper;
import org.apache.drill.exec.expr.holders.BitHolder;
import org.apache.drill.exec.expr.holders.IntHolder;
import org.apache.drill.exec.expr.holders.NullableFloat4Holder;
import org.apache.drill.exec.expr.holders.NullableUInt4Holder;
import org.apache.drill.exec.expr.holders.NullableVar16CharHolder;
import org.apache.drill.exec.expr.holders.NullableVarCharHolder;
import org.apache.drill.exec.expr.holders.RepeatedFloat4Holder;
import org.apache.drill.exec.expr.holders.RepeatedIntHolder;
import org.apache.drill.exec.expr.holders.RepeatedVarBinaryHolder;
import org.apache.drill.exec.expr.holders.UInt1Holder;
import org.apache.drill.exec.expr.holders.UInt4Holder;
import org.apache.drill.exec.expr.holders.VarCharHolder;
import org.apache.drill.exec.memory.BufferAllocator;
import org.apache.drill.exec.memory.RootAllocatorFactory;
import org.apache.drill.exec.proto.UserBitShared;
import org.apache.drill.exec.record.MaterializedField;
import org.apache.drill.exec.vector.BaseValueVector;
import org.apache.drill.exec.vector.BitVector;
import org.apache.drill.exec.vector.NullableFloat4Vector;
import org.apache.drill.exec.vector.NullableUInt4Vector;
import org.apache.drill.exec.vector.NullableVarCharVector;
import org.apache.drill.exec.vector.RepeatedIntVector;
import org.apache.drill.exec.vector.UInt4Vector;
import org.apache.drill.exec.vector.ValueVector;
import org.apache.drill.exec.vector.VarCharVector;
import org.apache.drill.exec.vector.VariableWidthVector;
import org.apache.drill.exec.vector.complex.ListVector;
import org.apache.drill.exec.vector.complex.MapVector;
import org.apache.drill.exec.vector.complex.RepeatedListVector;
import org.apache.drill.exec.vector.complex.RepeatedMapVector;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import io.netty.buffer.DrillBuf;
@Category(VectorTest.class)
public class TestValueVector extends ExecTest {
private final static String EMPTY_SCHEMA_PATH = "";
private DrillConfig drillConfig;
private BufferAllocator allocator;
@Before
public void init() {
drillConfig = DrillConfig.create();
allocator = RootAllocatorFactory.newRoot(drillConfig);
}
private final static Charset utf8Charset = StandardCharsets.UTF_8;
private final static byte[] STR1 = "AAAAA1".getBytes(utf8Charset);
private final static byte[] STR2 = "BBBBBBBBB2".getBytes(utf8Charset);
private final static byte[] STR3 = "CCCC3".getBytes(utf8Charset);
@After
public void terminate() throws Exception {
allocator.close();
}
@Test(expected = OversizedAllocationException.class)
@Category(UnlikelyTest.class)
public void testFixedVectorReallocation() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, UInt4Holder.TYPE);
final UInt4Vector vector = new UInt4Vector(field, allocator);
// edge case 1: buffer size = max value capacity
final int expectedValueCapacity = BaseValueVector.MAX_ALLOCATION_SIZE / 4;
try {
vector.allocateNew(expectedValueCapacity);
assertEquals(expectedValueCapacity, vector.getValueCapacity());
vector.reAlloc();
assertEquals(expectedValueCapacity * 2, vector.getValueCapacity());
} finally {
vector.close();
}
// common case: value count < max value capacity
try {
vector.allocateNew(BaseValueVector.MAX_ALLOCATION_SIZE / 8);
vector.reAlloc(); // value allocation reaches to MAX_VALUE_ALLOCATION
vector.reAlloc(); // this should throw an IOOB
} finally {
vector.close();
}
}
@Test(expected = OversizedAllocationException.class)
@Category(UnlikelyTest.class)
public void testBitVectorReallocation() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, UInt4Holder.TYPE);
final BitVector vector = new BitVector(field, allocator);
// edge case 1: buffer size ~ max value capacity
final int expectedValueCapacity = 1 << 29;
try {
vector.allocateNew(expectedValueCapacity);
assertEquals(expectedValueCapacity, vector.getValueCapacity());
vector.reAlloc();
assertEquals(expectedValueCapacity * 2, vector.getValueCapacity());
} finally {
vector.close();
}
// common: value count < MAX_VALUE_ALLOCATION
try {
vector.allocateNew(expectedValueCapacity);
for (int i = 0; i < 3; i++) {
vector.reAlloc(); // expand buffer size
}
assertEquals(Integer.MAX_VALUE, vector.getValueCapacity());
vector.reAlloc(); // buffer size ~ max allocation
assertEquals(Integer.MAX_VALUE, vector.getValueCapacity());
vector.reAlloc(); // overflow
} finally {
vector.close();
}
}
@Test(expected = OversizedAllocationException.class)
@Category(UnlikelyTest.class)
public void testVariableVectorReallocation() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, UInt4Holder.TYPE);
final VarCharVector vector = new VarCharVector(field, allocator);
// edge case 1: value count = MAX_VALUE_ALLOCATION
final int expectedAllocationInBytes = BaseValueVector.MAX_ALLOCATION_SIZE;
final int expectedOffsetSize = 10;
try {
vector.allocateNew(expectedAllocationInBytes, 10);
assertTrue(expectedOffsetSize <= vector.getValueCapacity());
assertTrue(expectedAllocationInBytes <= vector.getBuffer().capacity());
vector.reAlloc();
assertTrue(expectedOffsetSize * 2 <= vector.getValueCapacity());
assertTrue(expectedAllocationInBytes * 2 <= vector.getBuffer().capacity());
} finally {
vector.close();
}
// common: value count < MAX_VALUE_ALLOCATION
try {
vector.allocateNew(BaseValueVector.MAX_ALLOCATION_SIZE / 2, 0);
vector.reAlloc(); // value allocation reaches to MAX_VALUE_ALLOCATION
vector.reAlloc(); // this tests if it overflows
} finally {
vector.close();
}
}
@Test
public void testFixedType() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, UInt4Holder.TYPE);
// Create a new value vector for 1024 integers.
try (final UInt4Vector vector = new UInt4Vector(field, allocator)) {
final UInt4Vector.Mutator m = vector.getMutator();
vector.allocateNew(1024);
// Put and set a few values
m.setSafe(0, 100);
m.setSafe(1, 101);
m.setSafe(100, 102);
m.setSafe(1022, 103);
m.setSafe(1023, 104);
final UInt4Vector.Accessor accessor = vector.getAccessor();
assertEquals(100, accessor.get(0));
assertEquals(101, accessor.get(1));
assertEquals(102, accessor.get(100));
assertEquals(103, accessor.get(1022));
assertEquals(104, accessor.get(1023));
}
}
@Test
public void testNullableVarLen2() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableVarCharHolder.TYPE);
// Create a new value vector for 1024 integers.
try (final NullableVarCharVector vector = new NullableVarCharVector(field, allocator)) {
final NullableVarCharVector.Mutator m = vector.getMutator();
vector.allocateNew(1024 * 10, 1024);
m.set(0, STR1);
m.set(1, STR2);
m.set(2, STR3);
// Check the sample strings.
final NullableVarCharVector.Accessor accessor = vector.getAccessor();
assertArrayEquals(STR1, accessor.get(0));
assertArrayEquals(STR2, accessor.get(1));
assertArrayEquals(STR3, accessor.get(2));
// Ensure null value throws.
boolean b = false;
try {
vector.getAccessor().get(3);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
}
private static DrillBuf combineBuffers(final BufferAllocator allocator, final DrillBuf[] buffers) {
// find the total size we'll need
int size = 0;
for(final DrillBuf buffer : buffers) {
size += buffer.readableBytes();
}
// create the new buffer
final DrillBuf newBuf = allocator.buffer(size);
final DrillBuf writeBuf = newBuf;
for(final DrillBuf buffer : buffers) {
final DrillBuf readBuf = (DrillBuf) buffer.slice();
final int nBytes = readBuf.readableBytes();
final byte[] bytes = new byte[nBytes];
readBuf.readBytes(bytes);
writeBuf.writeBytes(bytes);
}
return newBuf;
}
@Test
public void testRepeatedIntVector() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedIntHolder.TYPE);
// Create a new value vector.
final RepeatedIntVector vector1 = new RepeatedIntVector(field, allocator);
// Populate the vector.
final int[] values = {2, 3, 5, 7, 11, 13, 17, 19, 23, 27}; // some tricksy primes
final int nRecords = 7;
final int nElements = values.length;
vector1.allocateNew(nRecords, nRecords * nElements);
final RepeatedIntVector.Mutator mutator = vector1.getMutator();
for(int recordIndex = 0; recordIndex < nRecords; ++recordIndex) {
mutator.startNewValue(recordIndex);
for(int elementIndex = 0; elementIndex < nElements; ++elementIndex) {
mutator.add(recordIndex, recordIndex * values[elementIndex]);
}
}
mutator.setValueCount(nRecords);
// Verify the contents.
final RepeatedIntVector.Accessor accessor1 = vector1.getAccessor();
assertEquals(nRecords, accessor1.getValueCount());
for(int recordIndex = 0; recordIndex < nRecords; ++recordIndex) {
for(int elementIndex = 0; elementIndex < nElements; ++elementIndex) {
final int value = accessor1.get(recordIndex, elementIndex);
assertEquals(recordIndex * values[elementIndex], value);
}
}
/* TODO(cwestin)
the interface to load has changed
// Serialize, reify, and verify.
final DrillBuf[] buffers1 = vector1.getBuffers(false);
final DrillBuf buffer1 = combineBuffers(allocator, buffers1);
final RepeatedIntVector vector2 = new RepeatedIntVector(field, allocator);
vector2.load(nRecords, nRecords * nElements, buffer1);
final RepeatedIntVector.Accessor accessor2 = vector2.getAccessor();
for(int recordIndex = 0; recordIndex < nRecords; ++recordIndex) {
for(int elementIndex = 0; elementIndex < nElements; ++elementIndex) {
final int value = accessor2.get(recordIndex, elementIndex);
assertEquals(accessor1.get(recordIndex, elementIndex), value);
}
}
*/
vector1.close();
/* TODO(cwestin)
vector2.close();
buffer1.release();
*/
}
@Test
public void testVarCharVectorLoad() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, VarCharHolder.TYPE);
// Create a new value vector for 1024 variable length strings.
final VarCharVector vector1 = new VarCharVector(field, allocator);
final VarCharVector.Mutator mutator = vector1.getMutator();
vector1.allocateNew(1024 * 10, 1024);
// Populate the vector.
final StringBuilder stringBuilder = new StringBuilder();
final int valueCount = 10;
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
mutator.setSafe(i, stringBuilder.toString().getBytes(utf8Charset));
}
mutator.setValueCount(valueCount);
assertEquals(valueCount, vector1.getAccessor().getValueCount());
// Combine the backing buffers so we can load them into a new vector.
final DrillBuf[] buffers1 = vector1.getBuffers(false);
final DrillBuf buffer1 = combineBuffers(allocator, buffers1);
final VarCharVector vector2 = new VarCharVector(field, allocator);
vector2.load(vector1.getMetadata(), buffer1);
// Check the contents of the new vector.
final VarCharVector.Accessor accessor = vector2.getAccessor();
stringBuilder.setLength(0);
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
final Object object = accessor.getObject(i);
assertEquals(stringBuilder.toString(), object.toString());
}
vector1.close();
vector2.close();
buffer1.release();
}
@Test
public void testNullableVarCharVectorLoad() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableVarCharHolder.TYPE);
// Create a new value vector for 1024 nullable variable length strings.
final NullableVarCharVector vector1 = new NullableVarCharVector(field, allocator);
final NullableVarCharVector.Mutator mutator = vector1.getMutator();
vector1.allocateNew(1024 * 10, 1024);
// Populate the vector.
final StringBuilder stringBuilder = new StringBuilder();
final int valueCount = 10;
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
mutator.set(i, stringBuilder.toString().getBytes(utf8Charset));
}
// Check the contents.
final NullableVarCharVector.Accessor accessor1 = vector1.getAccessor();
stringBuilder.setLength(0);
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
final Object object = accessor1.getObject(i);
assertEquals(stringBuilder.toString(), object.toString());
}
mutator.setValueCount(valueCount);
assertEquals(valueCount, vector1.getAccessor().getValueCount());
// Still ok after setting value count?
stringBuilder.setLength(0);
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
final Object object = accessor1.getObject(i);
assertEquals(stringBuilder.toString(), object.toString());
}
// Combine into a single buffer so we can load it into a new vector.
final DrillBuf[] buffers1 = vector1.getBuffers(false);
final DrillBuf buffer1 = combineBuffers(allocator, buffers1);
final NullableVarCharVector vector2 = new NullableVarCharVector(field, allocator);
vector2.load(vector1.getMetadata(), buffer1);
// Check the vector's contents.
final NullableVarCharVector.Accessor accessor2 = vector2.getAccessor();
stringBuilder.setLength(0);
for(int i = 0; i < valueCount; ++i) {
stringBuilder.append('x');
final Object object = accessor2.getObject(i);
assertEquals(stringBuilder.toString(), object.toString());
}
vector1.close();
vector2.close();
buffer1.release();
}
@Test
public void testNullableFixedType() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableUInt4Holder.TYPE);
// Create a new value vector for 1024 integers.
try (final NullableUInt4Vector vector = new NullableUInt4Vector(field, allocator)) {
final NullableUInt4Vector.Mutator m = vector.getMutator();
vector.allocateNew(1024);
// Put and set a few values
m.set(0, 100);
m.set(1, 101);
m.set(100, 102);
m.set(1022, 103);
m.set(1023, 104);
final NullableUInt4Vector.Accessor accessor = vector.getAccessor();
assertEquals(100, accessor.get(0));
assertEquals(101, accessor.get(1));
assertEquals(102, accessor.get(100));
assertEquals(103, accessor.get(1022));
assertEquals(104, accessor.get(1023));
// Ensure null values throw
{
boolean b = false;
try {
accessor.get(3);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
vector.allocateNew(2048);
{
boolean b = false;
try {
accessor.get(0);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
m.set(0, 100);
m.set(1, 101);
m.set(100, 102);
m.set(1022, 103);
m.set(1023, 104);
assertEquals(100, accessor.get(0));
assertEquals(101, accessor.get(1));
assertEquals(102, accessor.get(100));
assertEquals(103, accessor.get(1022));
assertEquals(104, accessor.get(1023));
// Ensure null values throw.
{
boolean b = false;
try {
vector.getAccessor().get(3);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
}
}
@Test
public void testNullableFloat() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableFloat4Holder.TYPE);
// Create a new value vector for 1024 integers
try (final NullableFloat4Vector vector = (NullableFloat4Vector) TypeHelper.getNewVector(field, allocator)) {
final NullableFloat4Vector.Mutator m = vector.getMutator();
vector.allocateNew(1024);
// Put and set a few values.
m.set(0, 100.1f);
m.set(1, 101.2f);
m.set(100, 102.3f);
m.set(1022, 103.4f);
m.set(1023, 104.5f);
final NullableFloat4Vector.Accessor accessor = vector.getAccessor();
assertEquals(100.1f, accessor.get(0), 0);
assertEquals(101.2f, accessor.get(1), 0);
assertEquals(102.3f, accessor.get(100), 0);
assertEquals(103.4f, accessor.get(1022), 0);
assertEquals(104.5f, accessor.get(1023), 0);
// Ensure null values throw.
{
boolean b = false;
try {
vector.getAccessor().get(3);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
vector.allocateNew(2048);
{
boolean b = false;
try {
accessor.get(0);
} catch (IllegalStateException e) {
b = true;
} finally {
assertTrue(b);
}
}
}
}
@Test
public void testBitVector() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, BitHolder.TYPE);
// Create a new value vector for 1024 integers
try (final BitVector vector = new BitVector(field, allocator)) {
final BitVector.Mutator m = vector.getMutator();
vector.allocateNew(1024);
// Put and set a few values
m.set(0, 1);
m.set(1, 0);
m.set(100, 0);
m.set(1022, 1);
m.setValueCount(1023);
final BitVector.Accessor accessor = vector.getAccessor();
assertEquals(1, accessor.get(0));
assertEquals(0, accessor.get(1));
assertEquals(0, accessor.get(100));
assertEquals(1, accessor.get(1022));
// test setting the same value twice
m.set(0, 1);
m.set(0, 1);
m.set(1, 0);
m.set(1, 0);
m.setValueCount(2);
assertEquals(1, accessor.get(0));
assertEquals(0, accessor.get(1));
// test toggling the values
m.set(0, 0);
m.set(1, 1);
m.setValueCount(2);
assertEquals(0, accessor.get(0));
assertEquals(1, accessor.get(1));
// Ensure unallocated space returns 0
assertEquals(0, accessor.get(3));
}
}
@Test
public void testReAllocNullableFixedWidthVector() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableFloat4Holder.TYPE);
// Create a new value vector for 1024 integers
try (final NullableFloat4Vector vector = (NullableFloat4Vector) TypeHelper.getNewVector(field, allocator)) {
final NullableFloat4Vector.Mutator m = vector.getMutator();
vector.allocateNew(1024);
assertEquals(1024, vector.getValueCapacity());
// Put values in indexes that fall within the initial allocation
m.setSafe(0, 100.1f);
m.setSafe(100, 102.3f);
m.setSafe(1023, 104.5f);
// Now try to put values in space that falls beyond the initial allocation
m.setSafe(2000, 105.5f);
// Check valueCapacity is more than initial allocation
assertEquals(1024 * 2, vector.getValueCapacity());
final NullableFloat4Vector.Accessor accessor = vector.getAccessor();
assertEquals(100.1f, accessor.get(0), 0);
assertEquals(102.3f, accessor.get(100), 0);
assertEquals(104.5f, accessor.get(1023), 0);
assertEquals(105.5f, accessor.get(2000), 0);
// Set the valueCount to be more than valueCapacity of current allocation. This is possible for NullableValueVectors
// as we don't call setSafe for null values, but we do call setValueCount when all values are inserted into the
// vector
m.setValueCount(vector.getValueCapacity() + 200);
}
}
@Test
public void testReAllocNullableVariableWidthVector() {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableVarCharHolder.TYPE);
// Create a new value vector for 1024 integers
try (final NullableVarCharVector vector = (NullableVarCharVector) TypeHelper.getNewVector(field, allocator)) {
final NullableVarCharVector.Mutator m = vector.getMutator();
vector.allocateNew();
int initialCapacity = vector.getValueCapacity();
// Put values in indexes that fall within the initial allocation
m.setSafe(0, STR1, 0, STR1.length);
m.setSafe(initialCapacity - 1, STR2, 0, STR2.length);
// Now try to put values in space that falls beyond the initial allocation
m.setSafe(initialCapacity + 200, STR3, 0, STR3.length);
// Check valueCapacity is more than initial allocation
assertEquals((initialCapacity + 1) * 2 - 1, vector.getValueCapacity());
final NullableVarCharVector.Accessor accessor = vector.getAccessor();
assertArrayEquals(STR1, accessor.get(0));
assertArrayEquals(STR2, accessor.get(initialCapacity - 1));
assertArrayEquals(STR3, accessor.get(initialCapacity + 200));
// Set the valueCount to be more than valueCapacity of current allocation. This is possible for NullableValueVectors
// as we don't call setSafe for null values, but we do call setValueCount when the current batch is processed.
m.setValueCount(vector.getValueCapacity() + 200);
}
}
@Test
public void testVVInitialCapacity() throws Exception {
final MaterializedField[] fields = new MaterializedField[9];
final ValueVector[] valueVectors = new ValueVector[9];
fields[0] = MaterializedField.create(EMPTY_SCHEMA_PATH, BitHolder.TYPE);
fields[1] = MaterializedField.create(EMPTY_SCHEMA_PATH, IntHolder.TYPE);
fields[2] = MaterializedField.create(EMPTY_SCHEMA_PATH, VarCharHolder.TYPE);
fields[3] = MaterializedField.create(EMPTY_SCHEMA_PATH, NullableVar16CharHolder.TYPE);
fields[4] = MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedFloat4Holder.TYPE);
fields[5] = MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedVarBinaryHolder.TYPE);
fields[6] = MaterializedField.create(EMPTY_SCHEMA_PATH, MapVector.TYPE);
fields[6].addChild(fields[0] /*bit*/);
fields[6].addChild(fields[2] /*varchar*/);
fields[7] = MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedMapVector.TYPE);
fields[7].addChild(fields[1] /*int*/);
fields[7].addChild(fields[3] /*optional var16char*/);
fields[8] = MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedListVector.TYPE);
fields[8].addChild(fields[1] /*int*/);
final int initialCapacity = 1024;
try {
for (int i = 0; i < valueVectors.length; i++) {
valueVectors[i] = TypeHelper.getNewVector(fields[i], allocator);
valueVectors[i].setInitialCapacity(initialCapacity);
valueVectors[i].allocateNew();
}
for (int i = 0; i < valueVectors.length; i++) {
final ValueVector vv = valueVectors[i];
final int vvCapacity = vv.getValueCapacity();
// this can't be equality because Nullables will be allocated using power of two sized buffers (thus need 1025
// spots in one vector > power of two is 2048, available capacity will be 2048 => 2047)
assertTrue(String.format("Incorrect value capacity for %s [%d]", vv.getField(), vvCapacity),
initialCapacity <= vvCapacity);
}
} finally {
AutoCloseables.close(valueVectors);
}
}
protected interface VectorVerifier {
void verify(ValueVector vector) throws Exception;
}
protected static class ChildVerifier implements VectorVerifier {
public final TypeProtos.MajorType[] types;
public ChildVerifier(TypeProtos.MajorType... childTypes) {
this.types = Preconditions.checkNotNull(childTypes);
}
@Override
public void verify(ValueVector vector) throws Exception {
final String hint = String.format("%s failed the test case", vector.getClass().getSimpleName());
final UserBitShared.SerializedField metadata = vector.getMetadata();
final int actual = metadata.getChildCount();
assertEquals(hint, types.length, actual);
for (int i = 0; i < types.length; i++) {
final UserBitShared.SerializedField child = metadata.getChild(i);
assertEquals(hint, types[i], child.getMajorType());
}
}
}
/**
* Convenience method that allows running tests on various {@link ValueVector vector} instances.
*
* @param test test function to execute
*/
private void testVectors(VectorVerifier test) throws Exception {
final MaterializedField[] fields = {
MaterializedField.create(EMPTY_SCHEMA_PATH, UInt4Holder.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, BitHolder.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, VarCharHolder.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, NullableVarCharHolder.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedListVector.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, MapVector.TYPE),
MaterializedField.create(EMPTY_SCHEMA_PATH, RepeatedMapVector.TYPE)
};
final ValueVector[] vectors = {
new UInt4Vector(fields[0], allocator),
new BitVector(fields[1], allocator),
new VarCharVector(fields[2], allocator),
new NullableVarCharVector(fields[3], allocator),
new RepeatedListVector(fields[4], allocator, null),
new MapVector(fields[5], allocator, null),
new RepeatedMapVector(fields[6], allocator, null)
};
try {
for (final ValueVector vector : vectors) {
test.verify(vector);
}
} finally {
AutoCloseables.close(vectors);
}
}
@Test
public void testVectorMetadataIsAccurate() throws Exception {
final VectorVerifier noChild = new ChildVerifier();
final VectorVerifier offsetChild = new ChildVerifier(UInt4Holder.TYPE);
final ImmutableMap.Builder<Class<? extends ValueVector>, VectorVerifier> builder = ImmutableMap.builder();
builder.put(UInt4Vector.class, noChild);
builder.put(BitVector.class, noChild);
builder.put(VarCharVector.class, offsetChild);
builder.put(NullableVarCharVector.class, new ChildVerifier(UInt1Holder.TYPE, Types.optional(TypeProtos.MinorType.VARCHAR)));
builder.put(RepeatedListVector.class, new ChildVerifier(UInt4Holder.TYPE, Types.LATE_BIND_TYPE));
builder.put(MapVector.class, noChild);
builder.put(RepeatedMapVector.class, offsetChild);
final ImmutableMap<Class<? extends ValueVector>, VectorVerifier> children = builder.build();
testVectors(new VectorVerifier() {
@Override
public void verify(ValueVector vector) throws Exception {
final Class<?> klazz = vector.getClass();
final VectorVerifier verifier = children.get(klazz);
verifier.verify(vector);
}
});
}
@Test
public void testVectorCanLoadEmptyBuffer() throws Exception {
final DrillBuf empty = allocator.getEmpty();
testVectors(new VectorVerifier() {
@Override
public void verify(ValueVector vector) {
final String hint = String.format("%s failed the test case", vector.getClass().getSimpleName());
final UserBitShared.SerializedField metadata = vector.getMetadata();
assertEquals(hint, 0, metadata.getBufferLength());
assertEquals(hint, 0, metadata.getValueCount());
vector.load(metadata, empty);
assertEquals(hint, 0, vector.getValueCapacity());
assertEquals(hint, 0, vector.getAccessor().getValueCount());
vector.clear();
}
});
}
@Test
public void testListVectorShouldNotThrowOversizedAllocationException() throws Exception {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH,
Types.optional(TypeProtos.MinorType.LIST));
@SuppressWarnings("resource")
ListVector vector = new ListVector(field, allocator, null);
ListVector vectorFrom = new ListVector(field, allocator, null);
vectorFrom.allocateNew();
for (int i = 0; i < 10000; i++) {
vector.allocateNew();
vector.copyFromSafe(0, 0, vectorFrom);
vector.clear();
}
vectorFrom.clear();
vector.clear();
}
/**
* For VariableLengthVectors when we clear of the vector and then explicitly set the
* ValueCount of zero, then it should not fail with IndexOutOfBoundException.
* @throws Exception
*/
@Test
public void testVarLengthVector_SetCountZeroAfterClear() throws Exception {
try {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, VarCharHolder.TYPE);
@SuppressWarnings("resource")
VariableWidthVector vector = new VarCharVector(field, allocator);
vector.allocateNew();
vector.clear();
assertTrue(vector.getAccessor().getValueCount() == 0);
vector.getMutator().setValueCount(0);
assertTrue(vector.getAccessor().getValueCount() == 0);
} catch (Exception ex) {
fail();
}
}
/** For VariableLengthVectors when we try to set value count greater than value count for which memory is allocated,
* then it should fail with IndexOutOfBoundException.
* @throws Exception
*/
@Test
public void testVarLengthVector_SetOOBCount() throws Exception {
final MaterializedField field = MaterializedField.create(EMPTY_SCHEMA_PATH, VarCharHolder.TYPE);
@SuppressWarnings("resource")
VariableWidthVector vector = new VarCharVector(field, allocator);
try {
vector.allocateNew(10, 1);
vector.getMutator().setValueCount(4);
fail();
} catch (Exception ex) {
assertTrue(ex instanceof IndexOutOfBoundsException);
} finally {
vector.clear();
}
}
}