| /* |
| * 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 |