diff --git a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlResultSet.java b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlResultSet.java index 0469ac1..67edf47 100644 --- a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlResultSet.java +++ b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlResultSet.java @@ -889,10 +889,10 @@ private static Clob clobValue(Value value) { rejectTypeMap(map); return getObject(columnLabel); } - @Override public Ref getRef(String columnLabel) throws SQLException { throw new SQLFeatureNotSupportedException("not implemented by SQLite JDBC driver"); } + @Override public Ref getRef(String columnLabel) throws SQLException { return getRef(findColumn(columnLabel)); } @Override public Blob getBlob(String columnLabel) throws SQLException { return getBlob(findColumn(columnLabel)); } @Override public Clob getClob(String columnLabel) throws SQLException { return getClob(findColumn(columnLabel)); } - @Override public Array getArray(String columnLabel) throws SQLException { throw new SQLFeatureNotSupportedException("not implemented by SQLite JDBC driver"); } + @Override public Array getArray(String columnLabel) throws SQLException { return getArray(findColumn(columnLabel)); } @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { return getDate(columnIndex); } @Override public Date getDate(String columnLabel, Calendar cal) throws SQLException { return getDate(columnLabel); } @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { return getTime(columnIndex); } diff --git a/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LibsqlResultSetTest.java b/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LibsqlResultSetTest.java index a9509ba..03521e4 100644 --- a/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LibsqlResultSetTest.java +++ b/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LibsqlResultSetTest.java @@ -447,6 +447,85 @@ void getObjectWithMapRejectsNonEmptyMap() throws SQLException { .hasMessageContaining("Type maps"); } + @ParameterizedTest(name = "{0} closed on {1}") + @ValueSource(strings = {"getRef", "getArray"}) + void labelAndIntGettersShareTheClosedResultSetCheck(String getter) throws SQLException { + LibsqlResultSet rs = resultSet(new Value.Integer(1)); + assertThat(rs.next()).isTrue(); + rs.close(); + + assertThatThrownBy(() -> readArrayOrRef(rs, getter, 1)) + .as("%s(int) on a closed result set", getter) + .isInstanceOf(SQLException.class) + .hasMessage("ResultSet is closed"); + assertThatThrownBy(() -> readArrayOrRef(rs, getter, "c1")) + .as("%s(String) on a closed result set", getter) + .isInstanceOf(SQLException.class) + .hasMessage("ResultSet is closed"); + } + + @ParameterizedTest(name = "{0} declared {1}") + @ValueSource(strings = {"ARRAY", "STRUCT", "REF", "DISTINCT", "JAVA_OBJECT"}) + void labelAndIntGettersShareTheDeclaredTypeCheck(String declaredType) throws SQLException { + LibsqlResultSet rs = declaredResultSet(declaredType, Value.of("42")); + assertThat(rs.next()).isTrue(); + + assertThatThrownBy(() -> rs.getArray(1)) + .as("getArray(int) on declared %s", declaredType) + .isInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("Declared " + declaredType + " is not supported"); + assertThatThrownBy(() -> rs.getArray("c1")) + .as("getArray(String) on declared %s", declaredType) + .isInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("Declared " + declaredType + " is not supported"); + assertThatThrownBy(() -> rs.getRef(1)) + .as("getRef(int) on declared %s", declaredType) + .isInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("Declared " + declaredType + " is not supported"); + assertThatThrownBy(() -> rs.getRef("c1")) + .as("getRef(String) on declared %s", declaredType) + .isInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("Declared " + declaredType + " is not supported"); + } + + @Test + void labelGettersRejectAnUnknownLabelBeforeTheUnsupportedThrow() throws SQLException { + LibsqlResultSet rs = resultSet(new Value.Integer(1)); + assertThat(rs.next()).isTrue(); + + assertThatThrownBy(() -> rs.getArray("missing")) + .isInstanceOf(SQLException.class) + .isNotInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("no such column: 'missing'"); + assertThatThrownBy(() -> rs.getRef("missing")) + .isInstanceOf(SQLException.class) + .isNotInstanceOf(SQLFeatureNotSupportedException.class) + .hasMessage("no such column: 'missing'"); + } + + @Test + void labelAndIntGettersStillRejectAnOrdinaryOpenColumn() throws SQLException { + LibsqlResultSet rs = resultSet(new Value.Integer(1)); + assertThat(rs.next()).isTrue(); + + assertThatThrownBy(() -> rs.getArray(1)) + .isInstanceOf(SQLFeatureNotSupportedException.class); + assertThatThrownBy(() -> rs.getArray("c1")) + .isInstanceOf(SQLFeatureNotSupportedException.class); + assertThatThrownBy(() -> rs.getRef(1)) + .isInstanceOf(SQLFeatureNotSupportedException.class); + assertThatThrownBy(() -> rs.getRef("c1")) + .isInstanceOf(SQLFeatureNotSupportedException.class); + } + + private static Object readArrayOrRef(LibsqlResultSet rs, String getter, Object column) + throws SQLException { + if ("getArray".equals(getter)) { + return column instanceof Integer ? rs.getArray((Integer) column) : rs.getArray((String) column); + } + return column instanceof Integer ? rs.getRef((Integer) column) : rs.getRef((String) column); + } + @Test void findColumnRejectsNullLabel() { LibsqlResultSet rs = resultSet(new Value.Integer(1)); @@ -612,8 +691,17 @@ private static void assertNumeric(LibsqlResultSet rs, int column, int expectedIn } private static LibsqlResultSet resultSet(Value... values) { + String[] declaredTypes = new String[values.length]; + return declaredResultSet(declaredTypes, values); + } + + private static LibsqlResultSet declaredResultSet(String declaredType, Value value) { + return declaredResultSet(new String[] {declaredType}, value); + } + + private static LibsqlResultSet declaredResultSet(String[] declaredTypes, Value... values) { List