/* * 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.remote; import org.apache.calcite.avatica.AvaticaUtils; import org.apache.calcite.avatica.ColumnMetaData; import org.apache.calcite.avatica.ColumnMetaData.ArrayType; import org.apache.calcite.avatica.ColumnMetaData.AvaticaType; import org.apache.calcite.avatica.ColumnMetaData.Rep; import org.apache.calcite.avatica.ColumnMetaData.ScalarType; import org.apache.calcite.avatica.SqlType; import org.apache.calcite.avatica.remote.Driver.Serialization; import org.apache.calcite.avatica.server.HttpServer; import org.apache.calcite.avatica.util.AbstractCursor.ArrayAccessor; import org.apache.calcite.avatica.util.ArrayImpl; import org.apache.calcite.avatica.util.Cursor.Accessor; import org.apache.calcite.avatica.util.ListIteratorCursor; import org.apache.calcite.avatica.util.Unsafe; import org.junit.AfterClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.sql.Array; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static java.nio.charset.StandardCharsets.UTF_8; /** * Test class for verifying functionality with arrays. */ @RunWith(Parameterized.class) public class ArrayTypeTest { private static final AvaticaServersForTest SERVERS = new AvaticaServersForTest(); private final HttpServer server; private final String url; private final int port; @SuppressWarnings("unused") private final Driver.Serialization serialization; @Parameters(name = "{0}") public static List<Object[]> parameters() throws Exception { SERVERS.startServers(); return SERVERS.getJUnitParameters(); } public ArrayTypeTest(Serialization serialization, HttpServer server) { this.server = server; this.port = this.server.getPort(); this.serialization = serialization; this.url = SERVERS.getJdbcUrl(port, serialization); } @AfterClass public static void afterClass() throws Exception { if (null != SERVERS) { SERVERS.stopServers(); } } @Test public void simpleArrayTest() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { ScalarType varcharComponent = ColumnMetaData.scalar(Types.VARCHAR, "VARCHAR", Rep.STRING); List<Array> varcharArrays = new ArrayList<>(); for (int i = 0; i < 5; i++) { List<String> value = Collections.singletonList(Integer.toString(i)); varcharArrays.add(createArray("VARCHAR", varcharComponent, value)); } writeAndReadArrays(conn, "varchar_arrays", "VARCHAR(30)", varcharComponent, varcharArrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void booleanArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.BOOLEAN, "BOOLEAN", Rep.BOOLEAN); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Boolean> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { switch (r.nextInt(3)) { case 0: elements.add(Boolean.FALSE); break; case 1: elements.add(Boolean.TRUE); break; case 2: elements.add(null); break; default: fail(); } } arrays.add(createArray("BOOLEAN", component, elements)); } // Verify we can read and write the data writeAndReadArrays(conn, "boolean_arrays", "BOOLEAN", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void shortArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.SMALLINT, "SMALLINT", Rep.SHORT); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Short> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { short value = (short) r.nextInt(Short.MAX_VALUE); // 50% of the time, negate the value if (0 == r.nextInt(2)) { value *= -1; } elements.add(Short.valueOf(value)); } arrays.add(createArray("SMALLINT", component, elements)); } // Verify read/write writeAndReadArrays(conn, "short_arrays", "SMALLINT", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void shortArraysWithNull() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.SMALLINT, "SMALLINT", Rep.SHORT); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Short> elements = new ArrayList<>(); for (int j = 0; j < 4; j++) { short value = (short) r.nextInt(Short.MAX_VALUE); // 50% of the time, negate the value if (0 == r.nextInt(2)) { value *= -1; } elements.add(Short.valueOf(value)); } elements.add(null); arrays.add(createArray("SMALLINT", component, elements)); } // Verify read/write writeAndReadArrays(conn, "short_arrays", "SMALLINT", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void longArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.BIGINT, "BIGINT", Rep.LONG); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Long> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(r.nextLong()); } arrays.add(createArray("BIGINT", component, elements)); } // Verify read/write writeAndReadArrays(conn, "long_arrays", "BIGINT", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void stringArrays() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.VARCHAR, "VARCHAR", Rep.STRING); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<String> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(i + "_" + j); } arrays.add(createArray("VARCHAR", component, elements)); } // Verify read/write writeAndReadArrays(conn, "string_arrays", "VARCHAR", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void bigintArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.BIGINT, "BIGINT", Rep.LONG); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 3; i++) { List<Long> elements = new ArrayList<>(); for (int j = 0; j < 7; j++) { long element = r.nextLong(); if (r.nextBoolean()) { element *= -1; } elements.add(element); } arrays.add(createArray("BIGINT", component, elements)); } writeAndReadArrays(conn, "long_arrays", "BIGINT", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void doubleArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.DOUBLE, "DOUBLE", Rep.DOUBLE); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 3; i++) { List<Double> elements = new ArrayList<>(); for (int j = 0; j < 7; j++) { double element = r.nextDouble(); if (r.nextBoolean()) { element *= -1; } elements.add(element); } arrays.add(createArray("DOUBLE", component, elements)); } writeAndReadArrays(conn, "float_arrays", "DOUBLE", component, arrays, PRIMITIVE_LIST_VALIDATOR); } } @Test public void arraysOfByteArrays() throws Exception { final Random r = new Random(); try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.TINYINT, "TINYINT", Rep.BYTE); // [ Array([b, b, b]), Array([b, b, b]), ... ] List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Byte> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { byte value = (byte) r.nextInt(Byte.MAX_VALUE); // 50% of the time, negate the value if (0 == r.nextInt(2)) { value *= -1; } elements.add(Byte.valueOf(value)); } arrays.add(createArray("TINYINT", component, elements)); } // Verify read/write writeAndReadArrays(conn, "byte_arrays", "TINYINT", component, arrays, BYTE_ARRAY_VALIDATOR); } } @Test public void varbinaryArrays() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.VARBINARY, "VARBINARY", Rep.BYTE_STRING); // [ Array(binary, binary, binary), Array(binary, binary, binary), ...] List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<byte[]> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add((i + "_" + j).getBytes(UTF_8)); } arrays.add(createArray("VARBINARY", component, elements)); } writeAndReadArrays(conn, "binary_arrays", "VARBINARY", component, arrays, BYTE_ARRAY_ARRAY_VALIDATOR); } } @Test public void timeArrays() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { final long now = System.currentTimeMillis(); ScalarType component = ColumnMetaData.scalar(Types.TIME, "TIME", Rep.JAVA_SQL_TIME); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Time> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(new Time(now + i + j)); } arrays.add(createArray("TIME", component, elements)); } writeAndReadArrays(conn, "time_arrays", "TIME", component, arrays, new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws SQLException { Object[] expectedTimes = (Object[]) expected.getArray(); Object[] actualTimes = (Object[]) actual.getArray(); assertEquals(expectedTimes.length, actualTimes.length); final Calendar cal = Unsafe.localCalendar(); for (int i = 0; i < expectedTimes.length; i++) { cal.setTime((Time) expectedTimes[i]); int expectedHour = cal.get(Calendar.HOUR_OF_DAY); int expectedMinute = cal.get(Calendar.MINUTE); int expectedSecond = cal.get(Calendar.SECOND); cal.setTime((Time) actualTimes[i]); assertEquals(expectedHour, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(expectedMinute, cal.get(Calendar.MINUTE)); assertEquals(expectedSecond, cal.get(Calendar.SECOND)); } } }); // Ensure an array with a null element can be written/read Array arrayWithNull = createArray("TIME", component, Arrays.asList((Time) null)); writeAndReadArrays(conn, "time_array_with_null", "TIME", component, Collections.singletonList(arrayWithNull), new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws Exception { Object[] expectedArray = (Object[]) expected.getArray(); Object[] actualArray = (Object[]) actual.getArray(); assertEquals(1, expectedArray.length); assertEquals(expectedArray.length, actualArray.length); assertEquals(expectedArray[0], actualArray[0]); } }); } } @Test public void dateArrays() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { final long now = System.currentTimeMillis(); ScalarType component = ColumnMetaData.scalar(Types.DATE, "DATE", Rep.JAVA_SQL_DATE); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Date> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(new Date(now + i + j)); } arrays.add(createArray("DATE", component, elements)); } writeAndReadArrays(conn, "date_arrays", "DATE", component, arrays, new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws SQLException { Object[] expectedDates = (Object[]) expected.getArray(); Object[] actualDates = (Object[]) actual.getArray(); assertEquals(expectedDates.length, actualDates.length); final Calendar cal = Unsafe.localCalendar(); for (int i = 0; i < expectedDates.length; i++) { cal.setTime((Date) expectedDates[i]); int expectedDayOfMonth = cal.get(Calendar.DAY_OF_MONTH); int expectedMonth = cal.get(Calendar.MONTH); int expectedYear = cal.get(Calendar.YEAR); cal.setTime((Date) actualDates[i]); assertEquals(expectedDayOfMonth, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(expectedMonth, cal.get(Calendar.MONTH)); assertEquals(expectedYear, cal.get(Calendar.YEAR)); } } }); // Ensure an array with a null element can be written/read Array arrayWithNull = createArray("DATE", component, Arrays.asList((Time) null)); writeAndReadArrays(conn, "date_array_with_null", "DATE", component, Collections.singletonList(arrayWithNull), new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws Exception { Object[] expectedArray = (Object[]) expected.getArray(); Object[] actualArray = (Object[]) actual.getArray(); assertEquals(1, expectedArray.length); assertEquals(expectedArray.length, actualArray.length); assertEquals(expectedArray[0], actualArray[0]); } }); } } @Test public void timestampArrays() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { final long now = System.currentTimeMillis(); ScalarType component = ColumnMetaData.scalar(Types.TIMESTAMP, "TIMESTAMP", Rep.JAVA_SQL_TIMESTAMP); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<Timestamp> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(new Timestamp(now + i + j)); } arrays.add(createArray("TIMESTAMP", component, elements)); } writeAndReadArrays(conn, "timestamp_arrays", "TIMESTAMP", component, arrays, new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws SQLException { Object[] expectedTimestamps = (Object[]) expected.getArray(); Object[] actualTimestamps = (Object[]) actual.getArray(); assertEquals(expectedTimestamps.length, actualTimestamps.length); final Calendar cal = Unsafe.localCalendar(); for (int i = 0; i < expectedTimestamps.length; i++) { cal.setTime((Timestamp) expectedTimestamps[i]); int expectedDayOfMonth = cal.get(Calendar.DAY_OF_MONTH); int expectedMonth = cal.get(Calendar.MONTH); int expectedYear = cal.get(Calendar.YEAR); int expectedHour = cal.get(Calendar.HOUR_OF_DAY); int expectedMinute = cal.get(Calendar.MINUTE); int expectedSecond = cal.get(Calendar.SECOND); int expectedMillisecond = cal.get(Calendar.MILLISECOND); cal.setTime((Timestamp) actualTimestamps[i]); assertEquals(expectedDayOfMonth, cal.get(Calendar.DAY_OF_MONTH)); assertEquals(expectedMonth, cal.get(Calendar.MONTH)); assertEquals(expectedYear, cal.get(Calendar.YEAR)); assertEquals(expectedHour, cal.get(Calendar.HOUR_OF_DAY)); assertEquals(expectedMinute, cal.get(Calendar.MINUTE)); assertEquals(expectedSecond, cal.get(Calendar.SECOND)); assertEquals(expectedMillisecond, cal.get(Calendar.MILLISECOND)); } } }); // Ensure an array with a null element can be written/read Array arrayWithNull = createArray("TIMESTAMP", component, Arrays.asList((Timestamp) null)); writeAndReadArrays(conn, "timestamp_array_with_null", "TIMESTAMP", component, Collections.singletonList(arrayWithNull), new Validator<Array>() { @Override public void validate(Array expected, Array actual) throws Exception { Object[] expectedArray = (Object[]) expected.getArray(); Object[] actualArray = (Object[]) actual.getArray(); assertEquals(1, expectedArray.length); assertEquals(expectedArray.length, actualArray.length); assertEquals(expectedArray[0], actualArray[0]); } }); } } @Test public void testCreateArrayOf() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { final String componentName = SqlType.INTEGER.name(); Array a1 = conn.createArrayOf(componentName, new Object[] {1, 2, 3, 4, 5}); Array a2 = conn.createArrayOf(componentName, new Object[] {2, 3, 4, 5, 6}); Array a3 = conn.createArrayOf(componentName, new Object[] {3, 4, 5, 6, 7}); AvaticaType arrayType = ColumnMetaData.array( ColumnMetaData.scalar(Types.INTEGER, componentName, Rep.INTEGER), "NUMBERS", Rep.ARRAY); writeAndReadArrays(conn, "CREATE_ARRAY_OF_INTEGERS", componentName, arrayType, Arrays.asList(a1, a2, a3), PRIMITIVE_LIST_VALIDATOR); } } @Test public void testBatchInsert() throws Exception { try (Connection conn = DriverManager.getConnection(url)) { ScalarType component = ColumnMetaData.scalar(Types.VARCHAR, "VARCHAR", Rep.STRING); List<Array> arrays = new ArrayList<>(); // Construct the data for (int i = 0; i < 5; i++) { List<String> elements = new ArrayList<>(); for (int j = 0; j < 5; j++) { elements.add(i + "_" + j); } arrays.add(createArray("VARCHAR", component, elements)); } String tableName = "test_batch_insert"; // Drop and create the table try (Statement stmt = conn.createStatement()) { assertFalse(stmt.execute(Unsafe.formatLocalString("DROP TABLE IF EXISTS %s", tableName))); String createTableSql = Unsafe.formatLocalString( "CREATE TABLE %s (id integer, vals %s ARRAY)", tableName, "VARCHAR"); assertFalse(stmt.execute(createTableSql)); } // Insert records, each with an array final String dml = Unsafe.formatLocalString("INSERT INTO %s VALUES (?, ?)", tableName); try (PreparedStatement stmt = conn.prepareStatement(dml)) { int i = 0; for (Array inputArray : arrays) { stmt.setInt(1, i); stmt.setArray(2, inputArray); stmt.addBatch(); i++; } assertEquals(i, stmt.executeBatch().length); } // Read the records try (Statement stmt = conn.createStatement()) { ResultSet results = stmt.executeQuery( Unsafe.formatLocalString("SELECT * FROM %s", tableName)); assertNotNull("Expected a ResultSet", results); int i = 0; for (Array expectedArray : arrays) { assertTrue(results.next()); assertEquals(i++, results.getInt(1)); Array actualArray = results.getArray(2); PRIMITIVE_LIST_VALIDATOR.validate(expectedArray, actualArray); } assertFalse("Expected no more records", results.next()); } } } /** * Creates a JDBC {@link Array} from a list of values. * * @param typeName the SQL type name of the elements in the array * @param componentType The Avatica type for the array elements * @param arrayValues The array elements * @return An Array instance for the given component and values */ @SuppressWarnings("unchecked") private <T> Array createArray(String typeName, AvaticaType componentType, List<T> arrayValues) { // Make a "row" with one "column" (which is really a list) final List<Object> oneRow = Collections.singletonList((Object) arrayValues); // Make an iterator over this one "row" final Iterator<List<Object>> rowIterator = Collections.singletonList(oneRow).iterator(); ArrayType array = ColumnMetaData.array(componentType, typeName, Rep.ARRAY); try (ListIteratorCursor cursor = new ListIteratorCursor(rowIterator)) { List<ColumnMetaData> types = Collections.singletonList(ColumnMetaData.dummy(array, true)); Calendar calendar = Unsafe.localCalendar(); List<Accessor> accessors = cursor.createAccessors(types, calendar, null); assertTrue("Expected at least one accessor, found " + accessors.size(), !accessors.isEmpty()); ArrayAccessor arrayAccessor = (ArrayAccessor) accessors.get(0); return new ArrayImpl((List<Object>) arrayValues, arrayAccessor); } } /** * Creates a table, writes the arrays to the table, and then verifies that the arrays can be * read from that table and are equivalent to the original arrays. * * @param conn The JDBC connection * @param tableName The name of the table to create and use * @param componentType The component type of the array * @param scalarType The Avatica type object for the component type of the array * @param inputArrays The data to write and read */ private void writeAndReadArrays(Connection conn, String tableName, String componentType, AvaticaType scalarType, List<Array> inputArrays, Validator<Array> validator) throws Exception { // Drop and create the table try (Statement stmt = conn.createStatement()) { assertFalse(stmt.execute(Unsafe.formatLocalString("DROP TABLE IF EXISTS %s", tableName))); String createTableSql = Unsafe.formatLocalString( "CREATE TABLE %s (id integer, vals %s ARRAY)", tableName, componentType); assertFalse(stmt.execute(createTableSql)); } // Insert records, each with an array final String dml = Unsafe.formatLocalString("INSERT INTO %s VALUES (?, ?)", tableName); try (PreparedStatement stmt = conn.prepareStatement(dml)) { int i = 0; for (Array inputArray : inputArrays) { stmt.setInt(1, i); stmt.setArray(2, inputArray); assertEquals(1, stmt.executeUpdate()); i++; } } // Read the records try (Statement stmt = conn.createStatement()) { ResultSet results = stmt.executeQuery( Unsafe.formatLocalString("SELECT * FROM %s", tableName)); assertNotNull("Expected a ResultSet", results); int i = 0; for (Array expectedArray : inputArrays) { assertTrue(results.next()); assertEquals(i++, results.getInt(1)); Array actualArray = results.getArray(2); validator.validate(expectedArray, actualArray); // TODO Fix this. See {@link AvaticaResultSet#create(ColumnMetaData.AvaticaType,Iterable)} //ResultSet inputResults = expectedArray.getResultSet(); //ResultSet actualResult = actualArray.getResultSet(); } assertFalse("Expected no more records", results.next()); } } /** * A simple interface to validate to objects in support of type test cases * @param <T> the type of element to be validated */ private interface Validator<T> { void validate(T expected, T actual) throws Exception; } private static final PrimitiveArrayValidator PRIMITIVE_LIST_VALIDATOR = new PrimitiveArrayValidator(); /** * Validator that coerces primitive arrays into lists and comparse them. */ private static class PrimitiveArrayValidator implements Validator<Array> { @Override public void validate(Array expected, Array actual) throws SQLException { assertEquals(AvaticaUtils.primitiveList(expected.getArray()), AvaticaUtils.primitiveList(actual.getArray())); } } private static final ByteArrayValidator BYTE_ARRAY_VALIDATOR = new ByteArrayValidator(); /** * Validator that compares lists of bytes (the object). */ private static class ByteArrayValidator implements Validator<Array> { @SuppressWarnings("unchecked") @Override public void validate(Array expected, Array actual) throws SQLException { // Need to compare the byte arrays. List<Byte> expectedArray = (List<Byte>) AvaticaUtils.primitiveList(expected.getArray()); List<Byte> actualArray = (List<Byte>) AvaticaUtils.primitiveList(actual.getArray()); assertEquals(expectedArray.size(), actualArray.size()); for (int j = 0; j < expectedArray.size(); j++) { Byte expectedByte = expectedArray.get(j); Byte actualByte = actualArray.get(j); assertEquals(expectedByte, actualByte); } } } // Arrays of byte arrays (e.g. an Array<Varbinary>) private static final ByteArrayArrayValidator BYTE_ARRAY_ARRAY_VALIDATOR = new ByteArrayArrayValidator(); /** * Validator that compares lists of byte arrays. */ private static class ByteArrayArrayValidator implements Validator<Array> { @SuppressWarnings("unchecked") @Override public void validate(Array expected, Array actual) throws SQLException { // Need to compare the byte arrays. List<byte[]> expectedArray = (List<byte[]>) AvaticaUtils.primitiveList(expected.getArray()); List<byte[]> actualArray = (List<byte[]>) AvaticaUtils.primitiveList(actual.getArray()); assertEquals(expectedArray.size(), actualArray.size()); for (int j = 0; j < expectedArray.size(); j++) { byte[] expectedBytes = expectedArray.get(j); byte[] actualBytes = actualArray.get(j); assertArrayEquals(expectedBytes, actualBytes); } } } } // End ArrayTypeTest.java