blob: 9e9fd6e956adbe62b52e768e3570fa6f79cc0ba7 [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.util;
import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.MetaImpl;
import org.apache.calcite.avatica.util.Cursor.Accessor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertArrayEquals;
/**
* Test class for StructImpl.
*/
@RunWith(Parameterized.class)
public class StructImplTest {
/**
* A class holding the necessary information for performing tests related with one table column.
*
* @param <T> the type of the expected values for the column
*/
private static class ColumnInputBundle<T> {
private final ColumnMetaData metaData;
/**
* The input values for the given column.
*
* <p>These values are used to construct the rows of a ResultSet.
*/
private final List<Object> inputValues;
/**
* The expected values for the given column.
*
* <p>These values are used to verify that the result obtained from a
* ResultSet is correct. Note that inputValues and expectedValues are not
* necessarily equal.
*/
private final List<T> expectedValues;
ColumnInputBundle(List<Object> inputValues, List<T> expected, ColumnMetaData metaData) {
this.inputValues = inputValues;
this.expectedValues = expected;
this.metaData = metaData;
}
@Override public String toString() {
return "column=" + metaData.columnName + ", inputType=" + inputValues.get(0).getClass();
}
}
private final ColumnInputBundle<Struct> columnInputBundle;
public StructImplTest(ColumnInputBundle<Struct> columnInputBundle) {
this.columnInputBundle = columnInputBundle;
}
@Parameterized.Parameters(name = "{0}")
public static Collection<ColumnInputBundle<Struct>> data() throws SQLException {
List<ColumnInputBundle<Struct>> data = new ArrayList<>();
final int numRows = 5;
ColumnMetaData oneAttrStructMeta = MetaImpl.columnMetaData(
"ONE_ATTRIBUTE_STRUCT",
0,
ColumnMetaData.struct(
Arrays.asList(
MetaImpl.columnMetaData("INT", 0, int.class, false))),
false);
ColumnMetaData twoAttrStructMeta = MetaImpl.columnMetaData(
"TWO_ATTRIBUTES_STRUCT",
0,
ColumnMetaData.struct(
Arrays.asList(
MetaImpl.columnMetaData("INT", 0, int.class, false),
MetaImpl.columnMetaData("INT", 1, int.class, false))),
false);
List<Struct> oneAttrStructData = new ArrayList<>(numRows);
List<Struct> twoAttrStructData = new ArrayList<>(numRows);
for (int i = 0; i < numRows; i++) {
oneAttrStructData.add(new StructImpl(Arrays.asList(i)));
twoAttrStructData.add(new StructImpl(Arrays.asList(i, i)));
}
// A struct has various internal representations. The most common are the following:
Class[] structTypes = new Class[]{Object[].class, Struct.class, List.class};
// Generate column bundles for every possible representation that is supported currently.
for (Class<?> type : structTypes) {
data.add(newStructColBundle(oneAttrStructData, type, oneAttrStructMeta));
data.add(newStructColBundle(twoAttrStructData, type, twoAttrStructMeta));
}
return data;
}
private static ColumnInputBundle newStructColBundle(
List<Struct> data, Class<?> structType, ColumnMetaData meta) throws SQLException {
List<Object> input = new ArrayList<>();
List<Struct> expected = new ArrayList<>();
for (Struct struct : data) {
input.add(structOf(structType, struct));
// The result obtained from StructAccessor is always of type Struct.class
expected.add(structOf(Struct.class, struct));
}
return new ColumnInputBundle(input, expected, meta);
}
/**
* Creates a struct in the representation dictated by the <code>structClass</code> parameter.
*/
private static <T> T structOf(Class<T> structClass, Struct structData) throws SQLException {
Object[] fieldValues = structData.getAttributes();
if (structClass.equals(Object[].class)) {
Object[] arrayStruct = new Object[fieldValues.length];
for (int i = 0; i < fieldValues.length; i++) {
arrayStruct[i] = fieldValues[i];
}
return (T) arrayStruct;
} else if (structClass.equals(Struct.class)) {
List<Object> listStruct = new ArrayList<>();
for (int i = 0; i < fieldValues.length; i++) {
listStruct.add(fieldValues[i]);
}
return (T) new StructImpl(listStruct);
} else if (structClass.equals(List.class)) {
List<Object> listStruct = new ArrayList<>();
for (int i = 0; i < fieldValues.length; i++) {
listStruct.add(fieldValues[i]);
}
return (T) listStruct;
} else {
throw new IllegalStateException("Cannot create struct of type" + structClass);
}
}
@Test public void testStructAccessor() throws Exception {
// Create rows based on the inputValues data
List<List<Object>> rows = new ArrayList<>();
for (Object o : columnInputBundle.inputValues) {
rows.add(Collections.singletonList(o));
}
try (Cursor cursor = new ListIteratorCursor(rows.iterator())) {
List<Accessor> accessors =
cursor.createAccessors(
Collections.singletonList(columnInputBundle.metaData), Unsafe.localCalendar(), null);
Accessor accessor = accessors.get(0);
int i = 0;
while (cursor.next()) {
Struct s = accessor.getObject(Struct.class);
Object[] expectedStructAttributes = columnInputBundle.expectedValues.get(i).getAttributes();
Object[] actualStructAttributes = s.getAttributes();
assertArrayEquals(expectedStructAttributes, actualStructAttributes);
i++;
}
}
}
}
// End StructImplTest.java