diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcArray.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcArray.java index 1dd83f817..3cb523186 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcArray.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcArray.java @@ -92,19 +92,41 @@ private JdbcArray(JdbcDataType type, Object[] elements) throws SQLException { this.data = java.lang.reflect.Array.newInstance( elements.getClass().getComponentType(), elements.length); + System.arraycopy(elements, 0, this.data, 0, elements.length); + } else if (type == JdbcDataType.INT64 && requiresWideningToLong(elements)) { + // Convert Byte[], Short[], and Integer[] to Long[] for INT64 type + // since Spanner only supports ARRAY + this.data = convertToLongArray(elements); } else { this.data = java.lang.reflect.Array.newInstance(type.getJavaClass(), elements.length); + try { + System.arraycopy(elements, 0, this.data, 0, elements.length); + } catch (Exception e) { + throw JdbcSqlExceptionFactory.of( + "Could not copy array elements. Make sure the supplied array only contains elements of class " + + type.getJavaClass().getName(), + Code.UNKNOWN, + e); + } } - try { - System.arraycopy(elements, 0, this.data, 0, elements.length); - } catch (Exception e) { - throw JdbcSqlExceptionFactory.of( - "Could not copy array elements. Make sure the supplied array only contains elements of class " - + type.getJavaClass().getName(), - Code.UNKNOWN, - e); + } + } + + private static boolean requiresWideningToLong(Object[] elements) { + Class componentType = elements.getClass().getComponentType(); + return componentType == Byte.class + || componentType == Short.class + || componentType == Integer.class; + } + + private static Long[] convertToLongArray(Object[] elements) { + Long[] longElements = new Long[elements.length]; + for (int i = 0; i < elements.length; i++) { + if (elements[i] != null) { + longElements[i] = ((Number) elements[i]).longValue(); } } + return longElements; } private JdbcArray(JdbcDataType type, List elements) { diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java index bb2f7c007..892c0057c 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java @@ -201,7 +201,7 @@ public Set getPostgreSQLAliases() { }, INT64 { private final Set> classes = - new HashSet<>(Arrays.asList(Byte.class, Integer.class, Long.class)); + new HashSet<>(Arrays.asList(Byte.class, Short.class, Integer.class, Long.class)); @Override public int getSqlType() { diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java index 0964db087..b43b44ddc 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java @@ -875,6 +875,8 @@ private Builder setArrayValue(ValueBinder binder, int type, Object valu return binder.toBoolArray((boolean[]) value); } else if (Boolean[].class.isAssignableFrom(value.getClass())) { return binder.toBoolArray(Arrays.asList((Boolean[]) value)); + } else if (Byte[].class.isAssignableFrom(value.getClass())) { + return binder.toInt64Array(toLongList((Byte[]) value)); } else if (short[].class.isAssignableFrom(value.getClass())) { long[] l = new long[((short[]) value).length]; for (int i = 0; i < l.length; i++) { diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java index bfae4e556..44dd1a78f 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcArrayTest.java @@ -144,6 +144,82 @@ public void testCreateArrayTypeName() throws SQLException { assertThat(rs.next()).isFalse(); } + // Test that Byte[] arrays are automatically widened to Long[] for INT64 type + Long[] data; + array = JdbcArray.createArray("INT64", new Byte[] {1, 2, 3, null, Byte.MAX_VALUE}); + assertThat(array.getBaseType()).isEqualTo(Types.BIGINT); + // Data should be stored as Long[] + assertThat(array.getArray()).isInstanceOf(Long[].class); + data = (Long[]) array.getArray(); + assertThat(data[0]).isEqualTo(1L); + assertThat(data[1]).isEqualTo(2L); + assertThat(data[2]).isEqualTo(3L); + assertThat(data[3]).isNull(); + assertThat(data[4]).isEqualTo((long) Byte.MAX_VALUE); + + try (ResultSet rs = array.getResultSet()) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getByte(2)).isEqualTo((byte) 1); + assertThat(rs.next()).isTrue(); + assertThat(rs.getByte(2)).isEqualTo((byte) 2); + assertThat(rs.next()).isTrue(); + assertThat(rs.getByte(2)).isEqualTo((byte) 3); + assertThat(rs.next()).isTrue(); + assertThat(rs.getByte(2)).isEqualTo((byte) 0); + assertTrue(rs.wasNull()); + assertThat(rs.next()).isTrue(); + assertThat(rs.getByte(2)).isEqualTo(Byte.MAX_VALUE); + assertThat(rs.next()).isFalse(); + } + + // Test that Short[] arrays are automatically widened to Long[] for INT64 type + array = JdbcArray.createArray("INT64", new Short[] {100, 200, null, Short.MAX_VALUE}); + assertThat(array.getBaseType()).isEqualTo(Types.BIGINT); + // Data should be stored as Long[] + assertThat(array.getArray()).isInstanceOf(Long[].class); + data = (Long[]) array.getArray(); + assertThat(data[0]).isEqualTo(100L); + assertThat(data[1]).isEqualTo(200L); + assertThat(data[2]).isNull(); + assertThat(data[3]).isEqualTo((long) Short.MAX_VALUE); + + try (ResultSet rs = array.getResultSet()) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getShort(2)).isEqualTo((short) 100); + assertThat(rs.next()).isTrue(); + assertThat(rs.getShort(2)).isEqualTo((short) 200); + assertThat(rs.next()).isTrue(); + assertThat(rs.getShort(2)).isEqualTo((short) 0); + assertTrue(rs.wasNull()); + assertThat(rs.next()).isTrue(); + assertThat(rs.getShort(2)).isEqualTo(Short.MAX_VALUE); + assertThat(rs.next()).isFalse(); + } + + // Test that Integer[] arrays are automatically widened to Long[] for INT64 type + array = JdbcArray.createArray("INT64", new Integer[] {1000, 2000, null, Integer.MAX_VALUE}); + assertThat(array.getBaseType()).isEqualTo(Types.BIGINT); + // Data should be stored as Long[] + assertThat(array.getArray()).isInstanceOf(Long[].class); + data = (Long[]) array.getArray(); + assertThat(data[0]).isEqualTo(1000L); + assertThat(data[1]).isEqualTo(2000L); + assertThat(data[2]).isNull(); + assertThat(data[3]).isEqualTo((long) Integer.MAX_VALUE); + + try (ResultSet rs = array.getResultSet()) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(2)).isEqualTo(1000); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(2)).isEqualTo(2000); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(2)).isEqualTo(0); + assertTrue(rs.wasNull()); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(2)).isEqualTo(Integer.MAX_VALUE); + assertThat(rs.next()).isFalse(); + } + array = JdbcArray.createArray("NUMERIC", new BigDecimal[] {BigDecimal.ONE, null, BigDecimal.TEN}); assertThat(array.getBaseType()).isEqualTo(Types.NUMERIC);