blob: 2e7e59824e45813e4decc1a0801ae796b4ab13cc [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.physical.rowSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.apache.drill.categories.RowSetTest;
import org.apache.drill.common.types.TypeProtos.MajorType;
import org.apache.drill.common.types.TypeProtos.MinorType;
import org.apache.drill.exec.physical.rowSet.RowSet.ExtendableRowSet;
import org.apache.drill.exec.physical.rowSet.RowSet.SingleRowSet;
import org.apache.drill.exec.record.VectorContainer;
import org.apache.drill.exec.record.metadata.SchemaBuilder;
import org.apache.drill.exec.record.metadata.TupleMetadata;
import org.apache.drill.exec.vector.NullableBigIntVector;
import org.apache.drill.exec.vector.NullableFloat8Vector;
import org.apache.drill.exec.vector.NullableIntVector;
import org.apache.drill.exec.vector.NullableVarCharVector;
import org.apache.drill.exec.vector.ValueVector;
import org.apache.drill.exec.vector.accessor.ArrayReader;
import org.apache.drill.exec.vector.accessor.ArrayWriter;
import org.apache.drill.exec.vector.accessor.ObjectReader;
import org.apache.drill.exec.vector.accessor.ObjectType;
import org.apache.drill.exec.vector.accessor.ObjectWriter;
import org.apache.drill.exec.vector.accessor.ScalarReader;
import org.apache.drill.exec.vector.accessor.ScalarWriter;
import org.apache.drill.exec.vector.accessor.TupleReader;
import org.apache.drill.exec.vector.accessor.TupleWriter;
import org.apache.drill.exec.vector.accessor.VariantReader;
import org.apache.drill.exec.vector.accessor.VariantWriter;
import org.apache.drill.exec.vector.complex.ListVector;
import org.apache.drill.exec.vector.complex.MapVector;
import org.apache.drill.exec.vector.complex.UnionVector;
import org.apache.drill.test.SubOperatorTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Tests for readers and writers for union and list types.
* <p>
* Note that the union type is only partially supported in Drill.
* The list type is unsupported. (However, the list type works in
* the schema builder, row set writer, row set reader and the
* result set builder. It does not, however, work in the Project
* and other operators. Some assembly required for future use.)
*/
@Category(RowSetTest.class)
public class TestVariantAccessors extends SubOperatorTest {
@Test
public void testBuildRowSetUnion() {
final TupleMetadata schema = new SchemaBuilder()
// Union with simple and complex types
.addUnion("u")
.addType(MinorType.INT)
.addMap()
.addNullable("c", MinorType.BIGINT)
.addNullable("d", MinorType.VARCHAR)
.resumeUnion()
.addList()
.addType(MinorType.VARCHAR)
.resumeUnion()
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final VectorContainer vc = rowSet.container();
assertEquals(1, vc.getNumberOfColumns());
// Single union
final ValueVector vector = vc.getValueVector(0).getValueVector();
assertTrue(vector instanceof UnionVector);
final UnionVector union = (UnionVector) vector;
final MapVector typeMap = union.getTypeMap();
ValueVector member = typeMap.getChild(MinorType.INT.name());
assertTrue(member instanceof NullableIntVector);
// Inner map
member = typeMap.getChild(MinorType.MAP.name());
assertTrue(member instanceof MapVector);
member = typeMap.getChild(MinorType.MAP.name());
assertTrue(member instanceof MapVector);
final MapVector childMap = (MapVector) member;
ValueVector mapMember = childMap.getChild("c");
assertNotNull(mapMember);
assertTrue(mapMember instanceof NullableBigIntVector);
mapMember = childMap.getChild("d");
assertNotNull(mapMember);
assertTrue(mapMember instanceof NullableVarCharVector);
// Inner list
member = typeMap.getChild(MinorType.LIST.name());
assertTrue(member instanceof ListVector);
final ListVector list = (ListVector) member;
assertTrue(list.getDataVector() instanceof NullableVarCharVector);
rowSet.clear();
}
/**
* Test a variant (AKA "union vector") at the top level, using
* just scalar values.
*/
@Test
public void testScalarVariant() {
final TupleMetadata schema = new SchemaBuilder()
.addUnion("u")
.addType(MinorType.INT)
.addType(MinorType.VARCHAR)
.addType(MinorType.FLOAT8)
.resumeSchema()
.buildSchema();
final ExtendableRowSet rs = fixture.rowSet(schema);
final RowSetWriter writer = rs.writer();
// Sanity check of writer structure
final ObjectWriter wo = writer.column(0);
assertEquals(ObjectType.VARIANT, wo.type());
final VariantWriter vw = wo.variant();
assertSame(vw, writer.variant(0));
assertSame(vw, writer.variant("u"));
assertTrue(vw.hasType(MinorType.INT));
assertTrue(vw.hasType(MinorType.VARCHAR));
assertTrue(vw.hasType(MinorType.FLOAT8));
// Write values of different types
vw.scalar(MinorType.INT).setInt(10);
writer.save();
vw.scalar(MinorType.VARCHAR).setString("fred");
writer.save();
// The entire variant is null
vw.setNull();
writer.save();
vw.scalar(MinorType.FLOAT8).setDouble(123.45);
writer.save();
// Strange case: just the value is null, but the variant
// is not null.
vw.scalar(MinorType.INT).setNull();
writer.save();
// Marker to avoid fill-empty issues (fill-empties tested elsewhere.)
vw.scalar(MinorType.INT).setInt(20);
writer.save();
final SingleRowSet result = writer.done();
assertEquals(6, result.rowCount());
// Read the values.
final RowSetReader reader = result.reader();
// Sanity check of structure
final ObjectReader ro = reader.column(0);
assertEquals(ObjectType.VARIANT, ro.type());
final VariantReader vr = ro.variant();
assertSame(vr, reader.variant(0));
assertSame(vr, reader.variant("u"));
for (final MinorType type : MinorType.values()) {
if (type == MinorType.INT || type == MinorType.VARCHAR || type == MinorType.FLOAT8) {
assertTrue(vr.hasType(type));
} else {
assertFalse(vr.hasType(type));
}
}
// Can get readers up front
final ScalarReader intReader = vr.scalar(MinorType.INT);
final ScalarReader strReader = vr.scalar(MinorType.VARCHAR);
final ScalarReader floatReader = vr.scalar(MinorType.FLOAT8);
// Verify the data
// Int 10
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.INT);
assertSame(intReader, vr.scalar());
assertNotNull(vr.member());
assertSame(vr.scalar(), vr.member().scalar());
assertFalse(intReader.isNull());
assertEquals(10, intReader.getInt());
assertTrue(strReader.isNull());
assertTrue(floatReader.isNull());
// String "fred"
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.VARCHAR);
assertSame(strReader, vr.scalar());
assertFalse(strReader.isNull());
assertEquals("fred", strReader.getString());
assertTrue(intReader.isNull());
assertTrue(floatReader.isNull());
// Null value
assertTrue(reader.next());
assertTrue(vr.isNull());
assertNull(vr.dataType());
assertNull(vr.scalar());
assertTrue(intReader.isNull());
assertTrue(strReader.isNull());
assertTrue(floatReader.isNull());
// Double 123.45
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.FLOAT8);
assertSame(floatReader, vr.scalar());
assertFalse(floatReader.isNull());
assertEquals(123.45, vr.scalar().getDouble(), 0.001);
assertTrue(intReader.isNull());
assertTrue(strReader.isNull());
// Strange case: null int (but union is not null)
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.INT);
assertTrue(intReader.isNull());
// Int 20
assertTrue(reader.next());
assertFalse(vr.isNull());
assertFalse(intReader.isNull());
assertEquals(20, intReader.getInt());
assertFalse(reader.next());
result.clear();
}
@Test
public void testBuildRowSetScalarList() {
final TupleMetadata schema = new SchemaBuilder()
// Top-level single-element list
.addList("list2")
.addType(MinorType.VARCHAR)
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final VectorContainer vc = rowSet.container();
assertEquals(1, vc.getNumberOfColumns());
// Single-type list
final ValueVector vector = vc.getValueVector(0).getValueVector();
assertTrue(vector instanceof ListVector);
final ListVector list = (ListVector) vector;
assertTrue(list.getDataVector() instanceof NullableVarCharVector);
rowSet.clear();
}
@Test
public void testBuildRowSetUnionArray() {
final TupleMetadata schema = new SchemaBuilder()
// List with multiple types
.addList("list1")
.addType(MinorType.BIGINT)
.addMap()
.addNullable("a", MinorType.INT)
.addNullable("b", MinorType.VARCHAR)
.resumeUnion()
// Nested single-element list
.addList()
.addType(MinorType.FLOAT8)
.resumeUnion()
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final VectorContainer vc = rowSet.container();
assertEquals(1, vc.getNumberOfColumns());
// List with complex internal structure
final ValueVector vector = vc.getValueVector(0).getValueVector();
assertTrue(vector instanceof ListVector);
final ListVector list = (ListVector) vector;
assertTrue(list.getDataVector() instanceof UnionVector);
final UnionVector union = (UnionVector) list.getDataVector();
// Union inside the list
final MajorType unionType = union.getField().getType();
final List<MinorType> types = unionType.getSubTypeList();
assertEquals(3, types.size());
assertTrue(types.contains(MinorType.BIGINT));
assertTrue(types.contains(MinorType.MAP));
assertTrue(types.contains(MinorType.LIST));
final MapVector typeMap = union.getTypeMap();
ValueVector member = typeMap.getChild(MinorType.BIGINT.name());
assertTrue(member instanceof NullableBigIntVector);
// Map inside the list
member = typeMap.getChild(MinorType.MAP.name());
assertTrue(member instanceof MapVector);
final MapVector childMap = (MapVector) member;
ValueVector mapMember = childMap.getChild("a");
assertNotNull(mapMember);
assertTrue(mapMember instanceof NullableIntVector);
mapMember = childMap.getChild("b");
assertNotNull(mapMember);
assertTrue(mapMember instanceof NullableVarCharVector);
// Single-type list inside the outer list
member = typeMap.getChild(MinorType.LIST.name());
assertTrue(member instanceof ListVector);
final ListVector childList = (ListVector) member;
assertTrue(childList.getDataVector() instanceof NullableFloat8Vector);
rowSet.clear();
}
/**
* Test a variant (AKA "union vector") at the top level which
* includes a map.
*/
@Test
public void testUnionWithMap() {
final TupleMetadata schema = new SchemaBuilder()
.addUnion("u")
.addType(MinorType.VARCHAR)
.addMap()
.addNullable("a", MinorType.INT)
.addNullable("b", MinorType.VARCHAR)
.resumeUnion()
.resumeSchema()
.buildSchema();
SingleRowSet result;
// Write values
{
final ExtendableRowSet rs = fixture.rowSet(schema);
final RowSetWriter writer = rs.writer();
// Sanity check of writer structure
final ObjectWriter wo = writer.column(0);
assertEquals(ObjectType.VARIANT, wo.type());
final VariantWriter vw = wo.variant();
assertTrue(vw.hasType(MinorType.VARCHAR));
final ObjectWriter strObj = vw.member(MinorType.VARCHAR);
final ScalarWriter strWriter = strObj.scalar();
assertSame(strWriter, vw.scalar(MinorType.VARCHAR));
assertTrue(vw.hasType(MinorType.MAP));
final ObjectWriter mapObj = vw.member(MinorType.MAP);
final TupleWriter mWriter = mapObj.tuple();
assertSame(mWriter, vw.tuple());
final ScalarWriter aWriter = mWriter.scalar("a");
final ScalarWriter bWriter = mWriter.scalar("b");
// First row: string "first"
vw.setType(MinorType.VARCHAR);
strWriter.setString("first");
writer.save();
// Second row: a map
vw.setType(MinorType.MAP);
aWriter.setInt(20);
bWriter.setString("fred");
writer.save();
// Third row: null
vw.setNull();
writer.save();
// Fourth row: map with a null string
vw.setType(MinorType.MAP);
aWriter.setInt(40);
bWriter.setNull();
writer.save();
// Fifth row: string "last"
vw.setType(MinorType.VARCHAR);
strWriter.setString("last");
writer.save();
result = writer.done();
assertEquals(5, result.rowCount());
}
// Read the values.
{
final RowSetReader reader = result.reader();
// Sanity check of structure
final ObjectReader ro = reader.column(0);
assertEquals(ObjectType.VARIANT, ro.type());
final VariantReader vr = ro.variant();
assertTrue(vr.hasType(MinorType.VARCHAR));
final ObjectReader strObj = vr.member(MinorType.VARCHAR);
final ScalarReader strReader = strObj.scalar();
assertSame(strReader, vr.scalar(MinorType.VARCHAR));
assertTrue(vr.hasType(MinorType.MAP));
final ObjectReader mapObj = vr.member(MinorType.MAP);
final TupleReader mReader = mapObj.tuple();
assertSame(mReader, vr.tuple());
final ScalarReader aReader = mReader.scalar("a");
final ScalarReader bReader = mReader.scalar("b");
// First row: string "first"
assertTrue(reader.next());
assertFalse(vr.isNull());
assertEquals(MinorType.VARCHAR, vr.dataType());
assertFalse(strReader.isNull());
assertTrue(mReader.isNull());
assertEquals("first", strReader.getString());
// Second row: a map
assertTrue(reader.next());
assertFalse(vr.isNull());
assertEquals(MinorType.MAP, vr.dataType());
assertTrue(strReader.isNull());
assertFalse(mReader.isNull());
assertFalse(aReader.isNull());
assertEquals(20, aReader.getInt());
assertFalse(bReader.isNull());
assertEquals("fred", bReader.getString());
// Third row: null
assertTrue(reader.next());
assertTrue(vr.isNull());
assertTrue(strReader.isNull());
assertTrue(mReader.isNull());
assertTrue(aReader.isNull());
assertTrue(bReader.isNull());
// Fourth row: map with a null string
assertTrue(reader.next());
assertEquals(MinorType.MAP, vr.dataType());
assertEquals(40, aReader.getInt());
assertTrue(bReader.isNull());
// Fifth row: string "last"
assertTrue(reader.next());
assertEquals(MinorType.VARCHAR, vr.dataType());
assertEquals("last", strReader.getString());
assertFalse(reader.next());
}
result.clear();
}
/**
* Test a scalar list. Should act just like a repeated type, with the
* addition of allowing the list for a row to be null. But, a list
* writer does not do auto-increment, so we must do that explicitly
* after each write.
*/
@Test
public void testScalarList() {
final TupleMetadata schema = new SchemaBuilder()
.addList("list")
.addType(MinorType.VARCHAR)
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final RowSetWriter writer = rowSet.writer();
{
final ObjectWriter listObj = writer.column(0);
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayWriter listArray = listObj.array();
// The list contains only a scalar. But, because lists can,
// in general, contain multiple contents, the list requires
// an explicit save after each entry.
final ObjectWriter itemObj = listArray.entry();
assertEquals(ObjectType.SCALAR, itemObj.type());
final ScalarWriter strWriter = itemObj.scalar();
// First row: two strings and a null
// Unlike a repeated type, a list can mark individual elements
// as null.
// List will automatically detect that data was written.
strWriter.setString("fred");
listArray.save();
strWriter.setNull();
listArray.save();
strWriter.setString("wilma");
listArray.save();
writer.save();
// Second row: null
writer.save();
// Third row: one string
strWriter.setString("dino");
listArray.save();
writer.save();
// Fourth row: empty array. Note that there is no trigger
// to say that the column is not null, so we have to do it
// explicitly.
listArray.setNull(false);
writer.save();
// Last row: a null string and non-null
strWriter.setNull();
listArray.save();
strWriter.setString("pebbles");
listArray.save();
writer.save();
}
final SingleRowSet result = writer.done();
assertEquals(5, result.rowCount());
{
final RowSetReader reader = result.reader();
final ObjectReader listObj = reader.column(0);
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayReader listArray = listObj.array();
// The list is a repeated scalar
assertEquals(ObjectType.SCALAR, listArray.entry().type());
final ScalarReader strReader = listArray.scalar();
// First row: two strings and a null
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(3, listArray.size());
assertTrue(listArray.next());
assertFalse(strReader.isNull());
assertEquals("fred", strReader.getString());
assertTrue(listArray.next());
assertTrue(strReader.isNull());
assertTrue(listArray.next());
assertFalse(strReader.isNull());
assertEquals("wilma", strReader.getString());
assertFalse(listArray.next());
// Second row: null
assertTrue(reader.next());
assertTrue(listArray.isNull());
assertEquals(0, listArray.size());
// Third row: one string
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(1, listArray.size());
assertTrue(listArray.next());
assertEquals("dino", strReader.getString());
assertFalse(listArray.next());
// Fourth row: empty array.
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(0, listArray.size());
assertFalse(listArray.next());
// Last row: a null string and non-null
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(2, listArray.size());
assertTrue(listArray.next());
assertTrue(strReader.isNull());
assertTrue(listArray.next());
assertFalse(strReader.isNull());
assertEquals("pebbles", strReader.getString());
assertFalse(listArray.next());
assertFalse(reader.next());
}
result.clear();
}
/**
* List of maps. Like a repeated map, but each list entry can be
* null.
*/
@Test
public void testListOfMaps() {
final TupleMetadata schema = new SchemaBuilder()
.addList("list")
.addMap()
.addNullable("a", MinorType.INT)
.addNullable("b", MinorType.VARCHAR)
.resumeUnion()
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final RowSetWriter writer = rowSet.writer();
{
final ObjectWriter listObj = writer.column("list");
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayWriter listArray = listObj.array();
final ObjectWriter itemObj = listArray.entry();
assertEquals(ObjectType.TUPLE, itemObj.type());
final TupleWriter mapWriter = itemObj.tuple();
final ScalarWriter aWriter = mapWriter.scalar("a");
final ScalarWriter bWriter = mapWriter.scalar("b");
// First row:
// {1, "fred"}, null, {3, null}
aWriter.setInt(1);
bWriter.setString("fred");
listArray.save();
// Can't mark the map as null. Instead, we simply skip
// the map and the contained nullable members will automatically
// back-fill each entry with a null value.
listArray.save();
aWriter.setInt(3);
bWriter.setNull();
listArray.save();
writer.save();
// Second row: null
writer.save();
// Third row: {null, "dino"}
aWriter.setNull();
bWriter.setString("dino");
listArray.save();
writer.save();
// Fourth row: empty array. Note that there is no trigger
// to say that the column is not null, so we have to do it
// explicitly.
listArray.setNull(false);
writer.save();
// Last row: {4, "pebbles"}
aWriter.setInt(4);
bWriter.setString("pebbles");
listArray.save();
writer.save();
}
final SingleRowSet result = writer.done();
assertEquals(5, result.rowCount());
{
final RowSetReader reader = result.reader();
final ObjectReader listObj = reader.column("list");
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayReader listArray = listObj.array();
assertEquals(ObjectType.TUPLE, listArray.entry().type());
final TupleReader mapReader = listArray.tuple();
final ScalarReader aReader = mapReader.scalar("a");
final ScalarReader bReader = mapReader.scalar("b");
// First row:
// {1, "fred"}, null, {3, null}
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertFalse(mapReader.isNull());
assertEquals(3, listArray.size());
assertTrue(listArray.next());
assertFalse(aReader.isNull());
assertEquals(1, aReader.getInt());
assertFalse(bReader.isNull());
assertEquals("fred", bReader.getString());
assertTrue(listArray.next());
// Awkward: the map has no null state, but its
// members do.
assertTrue(aReader.isNull());
assertTrue(bReader.isNull());
assertTrue(listArray.next());
assertFalse(aReader.isNull());
assertEquals(3, aReader.getInt());
assertTrue(bReader.isNull());
assertFalse(listArray.next());
// Second row: null
assertTrue(reader.next());
assertTrue(listArray.isNull());
assertEquals(0, listArray.size());
// Third row: {null, "dino"}
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(1, listArray.size());
assertTrue(listArray.next());
assertTrue(aReader.isNull());
assertFalse(bReader.isNull());
assertEquals("dino", bReader.getString());
assertFalse(listArray.next());
// Fourth row: empty array.
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(0, listArray.size());
assertFalse(listArray.next());
// Last row: {4, "pebbles"}
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(1, listArray.size());
assertTrue(listArray.next());
assertEquals(4, aReader.getInt());
assertEquals("pebbles", bReader.getString());
assertFalse(listArray.next());
assertFalse(reader.next());
}
result.clear();
}
/**
* Test a union list.
*/
@Test
public void testListOfUnions() {
final TupleMetadata schema = new SchemaBuilder()
.addList("list")
.addType(MinorType.INT)
.addType(MinorType.VARCHAR)
.resumeSchema()
.buildSchema();
final ExtendableRowSet rowSet = fixture.rowSet(schema);
final RowSetWriter writer = rowSet.writer();
{
final ObjectWriter listObj = writer.column(0);
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayWriter listArray = listObj.array();
final ObjectWriter itemObj = listArray.entry();
assertEquals(ObjectType.VARIANT, itemObj.type());
final VariantWriter variant = itemObj.variant();
final ScalarWriter intWriter = variant.scalar(MinorType.INT);
final ScalarWriter strWriter = variant.scalar(MinorType.VARCHAR);
// First row: (1, "two", 3)
variant.setType(MinorType.INT);
intWriter.setInt(1);
listArray.save();
variant.setType(MinorType.VARCHAR);
strWriter.setString("two");
listArray.save();
variant.setType(MinorType.INT);
intWriter.setInt(3);
listArray.save();
writer.save();
// Second row: null
writer.save();
// Third row: 4, null, "six", null int, null string
variant.setType(MinorType.INT);
intWriter.setInt(4);
listArray.save();
variant.setNull();
listArray.save();
variant.setType(MinorType.VARCHAR);
strWriter.setString("six");
listArray.save();
variant.setType(MinorType.INT);
intWriter.setNull();
listArray.save();
variant.setType(MinorType.VARCHAR);
intWriter.setNull();
listArray.save();
writer.save();
// Fourth row: empty array.
listArray.setNull(false);
writer.save();
// Fifth row: 9
variant.setType(MinorType.INT);
intWriter.setInt(9);
listArray.save();
writer.save();
}
final SingleRowSet result = writer.done();
assertEquals(5, result.rowCount());
{
final RowSetReader reader = result.reader();
final ObjectReader listObj = reader.column(0);
assertEquals(ObjectType.ARRAY, listObj.type());
final ArrayReader listArray = listObj.array();
assertEquals(ObjectType.VARIANT, listArray.entry().type());
final VariantReader variant = listArray.variant();
final ScalarReader intReader = variant.scalar(MinorType.INT);
final ScalarReader strReader = variant.scalar(MinorType.VARCHAR);
// First row: (1, "two", 3)
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(3, listArray.size());
assertTrue(listArray.next());
assertEquals(MinorType.INT, variant.dataType());
assertFalse(intReader.isNull());
assertTrue(strReader.isNull());
assertEquals(1, intReader.getInt());
assertEquals(1, variant.scalar().getInt());
assertTrue(listArray.next());
assertEquals(MinorType.VARCHAR, variant.dataType());
assertTrue(intReader.isNull());
assertFalse(strReader.isNull());
assertEquals("two", strReader.getString());
assertEquals("two", variant.scalar().getString());
assertTrue(listArray.next());
assertEquals(MinorType.INT, variant.dataType());
assertEquals(3, intReader.getInt());
assertFalse(listArray.next());
// Second row: null
assertTrue(reader.next());
assertTrue(listArray.isNull());
assertEquals(0, listArray.size());
// Third row: 4, null, "six", null int, null string
assertTrue(reader.next());
assertEquals(5, listArray.size());
assertTrue(listArray.next());
assertEquals(4, intReader.getInt());
assertTrue(listArray.next());
assertTrue(variant.isNull());
assertTrue(listArray.next());
assertEquals("six", strReader.getString());
assertTrue(listArray.next());
assertEquals(MinorType.INT, variant.dataType());
assertTrue(intReader.isNull());
assertTrue(listArray.next());
assertEquals(MinorType.VARCHAR, variant.dataType());
assertTrue(strReader.isNull());
assertFalse(listArray.next());
// Fourth row: empty array.
assertTrue(reader.next());
assertFalse(listArray.isNull());
assertEquals(0, listArray.size());
assertFalse(listArray.next());
// Fifth row: 9
assertTrue(reader.next());
assertEquals(1, listArray.size());
assertTrue(listArray.next());
assertEquals(9, intReader.getInt());
assertFalse(listArray.next());
assertFalse(reader.next());
}
result.clear();
}
/**
* Test a variant (AKA "union vector") at the top level, using
* just scalar values.
*/
@Test
public void testAddTypes() {
final TupleMetadata batchSchema = new SchemaBuilder()
.addNullable("v", MinorType.UNION)
.buildSchema();
final ExtendableRowSet rs = fixture.rowSet(batchSchema);
final RowSetWriter writer = rs.writer();
// Sanity check of writer structure
final ObjectWriter wo = writer.column(0);
assertEquals(ObjectType.VARIANT, wo.type());
final VariantWriter vw = wo.variant();
assertSame(vw, writer.variant(0));
assertSame(vw, writer.variant("v"));
for (final MinorType type : MinorType.values()) {
assertFalse(vw.hasType(type));
}
// Write values of different types
vw.scalar(MinorType.INT).setInt(10);
assertTrue(vw.hasType(MinorType.INT));
assertFalse(vw.hasType(MinorType.VARCHAR));
writer.save();
vw.scalar(MinorType.VARCHAR).setString("fred");
assertTrue(vw.hasType(MinorType.VARCHAR));
writer.save();
vw.setNull();
writer.save();
vw.scalar(MinorType.FLOAT8).setDouble(123.45);
assertTrue(vw.hasType(MinorType.INT));
assertTrue(vw.hasType(MinorType.FLOAT8));
writer.save();
final SingleRowSet result = writer.done();
assertEquals(4, result.rowCount());
// Read the values.
final RowSetReader reader = result.reader();
// Sanity check of structure
final ObjectReader ro = reader.column(0);
assertEquals(ObjectType.VARIANT, ro.type());
final VariantReader vr = ro.variant();
assertSame(vr, reader.variant(0));
assertSame(vr, reader.variant("v"));
for (final MinorType type : MinorType.values()) {
if (type == MinorType.INT || type == MinorType.VARCHAR || type == MinorType.FLOAT8) {
assertTrue(vr.hasType(type));
} else {
assertFalse(vr.hasType(type));
}
}
// Verify the data
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.INT);
assertSame(vr.scalar(MinorType.INT), vr.scalar());
assertNotNull(vr.member());
assertSame(vr.scalar(), vr.member().scalar());
assertEquals(10, vr.scalar().getInt());
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.VARCHAR);
assertSame(vr.scalar(MinorType.VARCHAR), vr.scalar());
assertEquals("fred", vr.scalar().getString());
assertTrue(reader.next());
assertTrue(vr.isNull());
assertNull(vr.dataType());
assertNull(vr.scalar());
assertTrue(reader.next());
assertFalse(vr.isNull());
assertSame(vr.dataType(), MinorType.FLOAT8);
assertSame(vr.scalar(MinorType.FLOAT8), vr.scalar());
assertEquals(123.45, vr.scalar().getDouble(), 0.001);
assertFalse(reader.next());
result.clear();
}
/**
* Test a variant (AKA "union vector") at the top level which includes
* a list.
*/
@Test
public void testUnionWithList() {
final TupleMetadata schema = new SchemaBuilder()
.addUnion("u")
.addType(MinorType.INT)
.addList()
.addType(MinorType.VARCHAR)
.resumeUnion()
.resumeSchema()
.buildSchema();
SingleRowSet result;
// Write values
{
final ExtendableRowSet rs = fixture.rowSet(schema);
final RowSetWriter writer = rs.writer();
final VariantWriter vw = writer.variant("u");
assertTrue(vw.hasType(MinorType.INT));
final ScalarWriter intWriter = vw.scalar(MinorType.INT);
assertTrue(vw.hasType(MinorType.LIST));
final ArrayWriter aWriter = vw.array();
final ScalarWriter strWriter = aWriter.scalar();
// Row 1: 1, ["fred", "barney"]
intWriter.setInt(1);
strWriter.setString("fred");
aWriter.save();
strWriter.setString("barney");
aWriter.save();
writer.save();
// Row 2, 2, ["wilma", "betty"]
intWriter.setInt(2);
strWriter.setString("wilma");
aWriter.save();
strWriter.setString("betty");
aWriter.save();
writer.save();
result = writer.done();
assertEquals(2, result.rowCount());
}
// Read the values.
{
final RowSetReader reader = result.reader();
final VariantReader vr = reader.variant("u");
assertTrue(vr.hasType(MinorType.INT));
final ScalarReader intReader = vr.scalar(MinorType.INT);
assertTrue(vr.hasType(MinorType.LIST));
final ArrayReader aReader = vr.array();
final ScalarReader strReader = aReader.scalar();
assertTrue(reader.next());
assertEquals(1, intReader.getInt());
assertEquals(2, aReader.size());
assertTrue(aReader.next());
assertEquals("fred", strReader.getString());
assertTrue(aReader.next());
assertEquals("barney", strReader.getString());
assertFalse(aReader.next());
assertTrue(reader.next());
assertEquals(2, intReader.getInt());
assertEquals(2, aReader.size());
assertTrue(aReader.next());
assertEquals("wilma", strReader.getString());
assertTrue(aReader.next());
assertEquals("betty", strReader.getString());
assertFalse(aReader.next());
assertFalse(reader.next());
}
result.clear();
}
// TODO: Repeated list
}