blob: 4f34a3cd1fb4efcc86043ae8670c8107cd9f9b6c [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.calcite.avatica;
import org.apache.calcite.avatica.Meta.Frame;
import org.apache.calcite.avatica.proto.Common;
import org.apache.calcite.avatica.proto.Common.ColumnValue;
import org.apache.calcite.avatica.proto.Common.TypedValue;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests serialization of {@link Frame}.
*/
public class FrameTest {
private static final TypedValue NUMBER_VALUE = TypedValue.newBuilder().setNumberValue(1)
.setType(Common.Rep.LONG).build();
private void serializeAndTestEquality(Frame frame) {
Frame frameCopy = Frame.fromProto(frame.toProto());
assertEquals(frame.done, frameCopy.done);
assertEquals(frame.offset, frameCopy.offset);
Iterable<Object> origRows = frame.rows;
Iterable<Object> copiedRows = frameCopy.rows;
assertEquals("Expected rows to both be null, or both be non-null",
origRows == null, copiedRows == null);
Iterator<Object> origIter = origRows.iterator();
Iterator<Object> copiedIter = copiedRows.iterator();
while (origIter.hasNext() && copiedIter.hasNext()) {
Object orig = origIter.next();
Object copy = copiedIter.next();
assertEquals(orig == null, copy == null);
// This is goofy, but it seems like an Array comes from the JDBC implementation but then
// the resulting Frame has a List to support the Avatica typed Accessors
assertEquals(Object[].class, orig.getClass());
assertTrue("Expected List but got " + copy.getClass(), copy instanceof List);
@SuppressWarnings("unchecked")
List<Object> copyList = (List<Object>) copy;
assertArrayEquals((Object[]) orig, copyList.toArray(new Object[0]));
}
assertEquals(origIter.hasNext(), copiedIter.hasNext());
}
@Test
public void testEmpty() {
serializeAndTestEquality(Frame.EMPTY);
}
@Test
public void testSingleRow() {
ArrayList<Object> rows = new ArrayList<>();
rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()});
Frame singleRow = new Frame(0, true, rows);
serializeAndTestEquality(singleRow);
}
@Test
public void testMultipleRows() {
ArrayList<Object> rows = new ArrayList<>();
rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()});
rows.add(new Object[] {"gnirts", 0, Long.MIN_VALUE});
rows.add(new Object[] {"", null, Long.MAX_VALUE});
Frame singleRow = new Frame(0, true, rows);
serializeAndTestEquality(singleRow);
}
@Test public void testMalformedColumnValue() {
// Invalid ColumnValue: has an array and scalar
final ColumnValue bothAttributesColumnValue = ColumnValue.newBuilder().setHasArrayValue(true)
.setScalarValue(NUMBER_VALUE).build();
// Note omission of setScalarValue(TypedValue).
final ColumnValue neitherAttributeColumnValue = ColumnValue.newBuilder().setHasArrayValue(false)
.build();
try {
Frame.validateColumnValue(bothAttributesColumnValue);
fail("Validating the ColumnValue should have failed as it has an array and scalar");
} catch (IllegalArgumentException e) {
// Pass
}
try {
Frame.validateColumnValue(neitherAttributeColumnValue);
fail("Validating the ColumnValue should have failed as it has neither an array nor scalar");
} catch (IllegalArgumentException e) {
// Pass
}
}
@Test public void testColumnValueBackwardsCompatibility() {
// 1
final ColumnValue oldStyleScalarValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE).build();
// [1, 1]
final ColumnValue oldStyleArrayValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE)
.addValue(NUMBER_VALUE).build();
assertFalse(Frame.isNewStyleColumn(oldStyleScalarValue));
assertFalse(Frame.isNewStyleColumn(oldStyleArrayValue));
Object scalar = Frame.parseOldStyleColumn(oldStyleScalarValue);
assertEquals(1L, scalar);
Object array = Frame.parseOldStyleColumn(oldStyleArrayValue);
assertEquals(Arrays.asList(1L, 1L), array);
}
@Test public void testColumnValueParsing() {
// 1
final ColumnValue scalarValue = ColumnValue.newBuilder().setScalarValue(NUMBER_VALUE).build();
// [1, 1]
final ColumnValue arrayValue = ColumnValue.newBuilder().addArrayValue(NUMBER_VALUE)
.addArrayValue(NUMBER_VALUE).setHasArrayValue(true).build();
assertTrue(Frame.isNewStyleColumn(scalarValue));
assertTrue(Frame.isNewStyleColumn(arrayValue));
Object scalar = Frame.parseColumn(scalarValue);
assertEquals(1L, scalar);
Object array = Frame.parseColumn(arrayValue);
assertEquals(Arrays.asList(1L, 1L), array);
}
@Test public void testDeprecatedValueAttributeForScalars() {
// Create a row with schema: [VARCHAR, INTEGER, DATE]
List<Object> rows = Collections.<Object>singletonList(new Object[] {"string", Integer.MAX_VALUE,
new Date().getTime()});
Meta.Frame frame = Meta.Frame.create(0, true, rows);
// Convert it to a protobuf
Common.Frame protoFrame = frame.toProto();
assertEquals(1, protoFrame.getRowsCount());
// Get that row we created
Common.Row protoRow = protoFrame.getRows(0);
// One row has many columns
List<Common.ColumnValue> protoColumns = protoRow.getValueList();
assertEquals(3, protoColumns.size());
// Verify that the scalar value is also present in the deprecated values attributes.
List<Common.TypedValue> deprecatedValues = protoColumns.get(0).getValueList();
assertEquals(1, deprecatedValues.size());
Common.TypedValue scalarValue = protoColumns.get(0).getScalarValue();
assertEquals(deprecatedValues.get(0), scalarValue);
}
@Test public void testDeprecatedValueAttributeForArrays() {
// Create a row with schema: [VARCHAR, ARRAY]
List<Object> rows = Collections.<Object>singletonList(new Object[] {"string",
Arrays.asList(1, 2, 3)});
Meta.Frame frame = Meta.Frame.create(0, true, rows);
// Convert it to a protobuf
Common.Frame protoFrame = frame.toProto();
assertEquals(1, protoFrame.getRowsCount());
// Get that row we created
Common.Row protoRow = protoFrame.getRows(0);
// One row has many columns
List<Common.ColumnValue> protoColumns = protoRow.getValueList();
// We should have two columns
assertEquals(2, protoColumns.size());
// Fetch the ARRAY column
Common.ColumnValue protoColumn = protoColumns.get(1);
// We should have the 3 ARRAY elements in the array_values attribute as well as the deprecated
// values attribute.
List<Common.TypedValue> deprecatedValues = protoColumn.getValueList();
assertEquals(3, deprecatedValues.size());
assertTrue("Column 2 should have an array_value", protoColumns.get(1).getHasArrayValue());
List<Common.TypedValue> arrayValues = protoColumns.get(1).getArrayValueList();
assertEquals(arrayValues, deprecatedValues);
}
@Test public void testNestedArraySerialization() {
List<Object> rows = new ArrayList<>();
// [ "pk", [[1,2], [3,4]] ]
rows.add(Arrays.asList("pk", Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4))));
Frame frame = new Frame(0, true, rows);
// Parse back the list in serialized form
Common.Frame protoFrame = frame.toProto();
Common.Row protoRow = protoFrame.getRows(0);
Common.ColumnValue protoColumn = protoRow.getValue(1);
assertTrue(protoColumn.getHasArrayValue());
int value = 1;
for (Common.TypedValue arrayElement : protoColumn.getArrayValueList()) {
assertEquals(Common.Rep.ARRAY, arrayElement.getType());
for (Common.TypedValue nestedArrayElement : arrayElement.getArrayValueList()) {
assertEquals(Common.Rep.INTEGER, nestedArrayElement.getType());
assertEquals(value++, nestedArrayElement.getNumberValue());
}
}
Frame newFrame = Frame.fromProto(protoFrame);
@SuppressWarnings("unchecked")
List<Object> newRow = (List<Object>) newFrame.rows.iterator().next();
@SuppressWarnings("unchecked")
List<Object> expectedRow = (List<Object>) rows.get(0);
assertEquals(expectedRow.get(0), newRow.get(0));
assertEquals(expectedRow.get(1), newRow.get(1));
}
}
// End FrameTest.java