diff --git a/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaData.java b/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaData.java index 0553a65..2f2d120 100644 --- a/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaData.java +++ b/libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaData.java @@ -342,58 +342,23 @@ public boolean isDefinitelyWritable(int column) throws SQLException { @Override public String getColumnClassName(int column) throws SQLException { getCol(column); - Value sample = sampleValue(column); - if (sample != null) { - return switch (sample) { - case Value.Integer i -> integerClassName(column); - case Value.Float f -> Double.class.getName(); - case Value.Text t -> String.class.getName(); - case Value.Blob b -> Object.class.getName(); - case Value.Null n -> Object.class.getName(); - }; - } - if (!rows.isEmpty()) { - return Object.class.getName(); - } - return switch (getColumnType(column)) { - case Types.TINYINT, Types.SMALLINT, Types.INTEGER -> Integer.class.getName(); - case Types.BIGINT -> Long.class.getName(); - case Types.FLOAT, Types.REAL, Types.DOUBLE, Types.NUMERIC, Types.DECIMAL -> Double.class.getName(); - case Types.CHAR, Types.VARCHAR, Types.CLOB -> String.class.getName(); - case Types.BOOLEAN -> Boolean.class.getName(); - default -> Object.class.getName(); - }; + return className(runtimeValue(column)); } - private String integerClassName(int column) { - Value firstNonInteger = null; - boolean outsideIntRange = false; - for (Row row : rows) { - List values = row.values(); - if (values == null || column < 1 || column > values.size()) { - continue; - } - Value value = values.get(column - 1); - if (value instanceof Value.Integer) { - if (!isIntInRange(value)) { - outsideIntRange = true; - } - } else if (value != null - && !(value instanceof Value.Null) - && firstNonInteger == null) { - firstNonInteger = value; - } + private static String className(Value value) { + if (value instanceof Value.Integer i) { + long val = i.value(); + return val > Integer.MAX_VALUE || val < Integer.MIN_VALUE + ? Long.class.getName() + : Integer.class.getName(); } - if (firstNonInteger instanceof Value.Float) { + if (value instanceof Value.Float) { return Double.class.getName(); } - if (firstNonInteger instanceof Value.Text) { + if (value instanceof Value.Text) { return String.class.getName(); } - if (firstNonInteger != null) { - return Object.class.getName(); - } - return outsideIntRange ? Long.class.getName() : Integer.class.getName(); + return Object.class.getName(); } private String declType(int column) throws SQLException { @@ -458,12 +423,6 @@ private static SampleType storageType(Value value) { return SampleType.NULL; } - private static boolean isIntInRange(Value value) { - return value instanceof Value.Integer i - && i.value() >= Integer.MIN_VALUE - && i.value() <= Integer.MAX_VALUE; - } - private TableInfo resolveTableContext() throws SQLException { if (!tableContextResolved) { TableInfo resolved = null; diff --git a/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaDataTest.java b/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaDataTest.java index d47af8f..da7a111 100644 --- a/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaDataTest.java +++ b/libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlResultSetMetaDataTest.java @@ -206,6 +206,85 @@ void usesDeclaredTypesForEmptyResultSet() throws SQLException { assertDeclaredTypes(metadata); } + @Test + void reportsObjectClassNameForEmptyResultSet() throws SQLException { + StmtResult result = new StmtResult( + List.of( + new Col("i", "INTEGER"), + new Col("big", "BIGINT"), + new Col("txt", "TEXT"), + new Col("b", "BLOB"), + new Col("flag", "BOOLEAN"), + new Col("real", "REAL"), + new Col("untyped", null)), + List.of(), + 0, + null); + LibsqlResultSet resultSet = new LibsqlResultSet(null, result); + ResultSetMetaData metadata = resultSet.getMetaData(); + + for (int column = 1; column <= metadata.getColumnCount(); column++) { + assertThat(metadata.getColumnClassName(column)) + .as("column %d before the first row", column) + .isEqualTo(Object.class.getName()); + } + + assertThat(resultSet.next()).isFalse(); + + for (int column = 1; column <= metadata.getColumnCount(); column++) { + assertThat(metadata.getColumnClassName(column)) + .as("column %d after the last row", column) + .isEqualTo(Object.class.getName()); + } + } + + @Test + void reportsCurrentRowClassNameForMixedTypeColumn() throws SQLException { + StmtResult result = new StmtResult( + List.of(new Col("x", null)), + List.of(new Row(List.of(Value.of(1L))), new Row(List.of(Value.of("a")))), + 0, + null); + LibsqlResultSet resultSet = new LibsqlResultSet(null, result); + ResultSetMetaData metadata = resultSet.getMetaData(); + + assertThat(metadata.getColumnClassName(1)) + .as("before the first row") + .isEqualTo(Integer.class.getName()); + + assertThat(resultSet.next()).isTrue(); + assertThat(metadata.getColumnClassName(1)) + .as("on the integer row") + .isEqualTo(Integer.class.getName()); + + assertThat(resultSet.next()).isTrue(); + assertThat(metadata.getColumnClassName(1)) + .as("on the text row") + .isEqualTo(String.class.getName()); + + assertThat(resultSet.next()).isFalse(); + assertThat(metadata.getColumnClassName(1)) + .as("after the last row") + .isEqualTo(Object.class.getName()); + } + + @Test + void reportsClassNameForCurrentRowOutsideIntRange() throws SQLException { + StmtResult result = new StmtResult( + List.of(new Col("x", null)), + List.of(new Row(List.of(Value.of(1L))), new Row(List.of(Value.of(5_000_000_000L)))), + 0, + null); + LibsqlResultSet resultSet = new LibsqlResultSet(null, result); + ResultSetMetaData metadata = resultSet.getMetaData(); + + assertThat(resultSet.next()).isTrue(); + assertThat(metadata.getColumnClassName(1)).isEqualTo(Integer.class.getName()); + + assertThat(resultSet.next()).isTrue(); + assertThat(metadata.getColumnClassName(1)).isEqualTo(Long.class.getName()); + } + @Test void reflectsStorageClassChangesBetweenRows() throws SQLException { StmtResult result = new StmtResult(