diff --git a/src/main/java/org/duckdb/DuckDBResultSet.java b/src/main/java/org/duckdb/DuckDBResultSet.java index 0fabd7c81..3e9560ea8 100644 --- a/src/main/java/org/duckdb/DuckDBResultSet.java +++ b/src/main/java/org/duckdb/DuckDBResultSet.java @@ -1462,6 +1462,29 @@ public T getObject(int columnIndex, Class type) throws SQLException { ", SQL type: " + sqlType, ErrorCode.RESULT_SET_CONVERSION); } + } else if (type == OffsetTime.class) { + if (sqlType == DuckDBColumnType.TIME || sqlType == DuckDBColumnType.TIME_NS || + sqlType == DuckDBColumnType.TIME_WITH_TIME_ZONE) { + return type.cast(getOffsetTime(columnIndex)); + } else { + throw createSQLException("Can't convert value to OffsetTime, Java type: " + type + + ", SQL type: " + sqlType, + ErrorCode.RESULT_SET_CONVERSION); + } + } else if (type == Struct.class || type == DuckDBStruct.class) { + if (sqlType == DuckDBColumnType.STRUCT) { + return type.cast(getStruct(columnIndex)); + } else { + throw createSQLException("Can't convert value to Struct, Java type: " + type + ", SQL type: " + sqlType, + ErrorCode.RESULT_SET_CONVERSION); + } + } else if (type == Map.class || type == LinkedHashMap.class) { + if (sqlType == DuckDBColumnType.MAP) { + return type.cast(currentChunk[columnIndex - 1].getMap(chunkIdx - 1)); + } else { + throw createSQLException("Can't convert value to Map, Java type: " + type + ", SQL type: " + sqlType, + ErrorCode.RESULT_SET_CONVERSION); + } } else if (type == Blob.class) { if (sqlType == DuckDBColumnType.BLOB) { throw createSQLException("Can't convert value to Blob, Java type: " + type + ", SQL type: " + sqlType, diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index df7626003..d418d0a38 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -39,6 +39,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -1129,6 +1130,28 @@ public static void test_structs() throws Exception { } } + public static void test_struct_get_object_typed() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE t (s STRUCT(name VARCHAR, age INTEGER), i INTEGER)"); + stmt.execute("INSERT INTO t VALUES ({'name': 'foo', 'age': 42}, 1), (NULL, 2)"); + try (ResultSet rs = stmt.executeQuery("SELECT * FROM t ORDER BY i")) { + assertEquals(rs.getMetaData().getColumnClassName(1), DuckDBStruct.class.getName()); + assertTrue(rs.next()); + Struct struct = rs.getObject(1, Struct.class); + assertEquals(struct.getSQLTypeName(), "STRUCT(\"name\" VARCHAR, age INTEGER)"); + assertEquals(toJavaObject(struct), mapOf("name", "foo", "age", 42)); + assertEquals(toJavaObject(rs.getObject("s", Struct.class)), mapOf("name", "foo", "age", 42)); + DuckDBStruct duckdbStruct = rs.getObject(1, DuckDBStruct.class); + assertEquals(duckdbStruct.getMap(), mapOf("name", "foo", "age", 42)); + assertThrows(() -> { rs.getObject(2, Struct.class); }, SQLException.class); + assertTrue(rs.next()); + assertNull(rs.getObject(1, Struct.class)); + assertTrue(rs.wasNull()); + assertFalse(rs.next()); + } + } + } + public static void test_struct_with_timestamp() throws Exception { try (Connection connection = DriverManager.getConnection(JDBC_URL)) { LocalDateTime now = LocalDateTime.of(LocalDate.of(2020, 5, 12), LocalTime.of(16, 20, 0, 0)); @@ -1572,6 +1595,27 @@ public static void test_map() throws Exception { } } + public static void test_map_get_object_typed() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE t (m MAP(VARCHAR, INTEGER), i INTEGER)"); + stmt.execute("INSERT INTO t VALUES (MAP {'foo': 1, 'bar': 2}, 1), (NULL, 2)"); + try (ResultSet rs = stmt.executeQuery("SELECT * FROM t ORDER BY i")) { + assertEquals(rs.getMetaData().getColumnClassName(1), LinkedHashMap.class.getName()); + assertTrue(rs.next()); + Map map = rs.getObject(1, Map.class); + assertEquals(map, mapOf("foo", 1, "bar", 2)); + assertEquals(new ArrayList<>(map.keySet()), Arrays.asList("foo", "bar")); + assertEquals(rs.getObject("m", Map.class), mapOf("foo", 1, "bar", 2)); + assertEquals(rs.getObject(1, LinkedHashMap.class), mapOf("foo", 1, "bar", 2)); + assertThrows(() -> { rs.getObject(2, Map.class); }, SQLException.class); + assertTrue(rs.next()); + assertNull(rs.getObject(1, Map.class)); + assertTrue(rs.wasNull()); + assertFalse(rs.next()); + } + } + } + public static void test_getColumnClassName() throws Exception { try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement s = conn.createStatement();) { try (ResultSet rs = s.executeQuery("select * from test_all_types()")) { diff --git a/src/test/java/org/duckdb/TestTimestamp.java b/src/test/java/org/duckdb/TestTimestamp.java index c06109c60..27f6f9737 100644 --- a/src/test/java/org/duckdb/TestTimestamp.java +++ b/src/test/java/org/duckdb/TestTimestamp.java @@ -620,6 +620,40 @@ public static void test_time_tz() throws Exception { } } + public static void test_time_tz_get_object_offset_time() throws Exception { + TimeZone defaultTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement s = conn.createStatement()) { + s.executeUpdate("CREATE TABLE t (i TIME WITH TIME ZONE)"); + s.execute("INSERT INTO t VALUES ('01:02:03+12:30:45'), ('04:05:06-03:10'), (NULL)"); + try (ResultSet rs = s.executeQuery("SELECT * FROM t")) { + assertEquals(rs.getMetaData().getColumnClassName(1), OffsetTime.class.getName()); + assertTrue(rs.next()); + assertEquals(rs.getObject(1, OffsetTime.class), + OffsetTime.of(LocalTime.of(1, 2, 3), ZoneOffset.ofHoursMinutesSeconds(12, 30, 45))); + assertEquals(rs.getObject("i", OffsetTime.class), + OffsetTime.of(LocalTime.of(1, 2, 3), ZoneOffset.ofHoursMinutesSeconds(12, 30, 45))); + assertTrue(rs.next()); + assertEquals(rs.getObject(1, OffsetTime.class), + OffsetTime.of(LocalTime.of(4, 5, 6), ZoneOffset.ofHoursMinutesSeconds(-3, -10, 0))); + assertTrue(rs.next()); + assertNull(rs.getObject(1, OffsetTime.class)); + assertTrue(rs.wasNull()); + assertFalse(rs.next()); + } + try (ResultSet rs = s.executeQuery("SELECT '01:02:03'::TIME, 'foo'")) { + assertTrue(rs.next()); + assertEquals(rs.getObject(1, OffsetTime.class), + OffsetTime.of(LocalTime.of(1, 2, 3), ZoneOffset.UTC)); + assertThrows(() -> { rs.getObject(2, OffsetTime.class); }, SQLException.class); + } + } + } finally { + TimeZone.setDefault(defaultTimeZone); + } + } + public static void test_time_ns() throws Exception { try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement s = conn.createStatement()) { s.executeUpdate("create table t (i time_ns)");