From 431083f6ac0a426fd703f4ad41bd175f21db118f Mon Sep 17 00:00:00 2001 From: Alex Kasko Date: Tue, 9 Sep 2025 21:26:04 +0100 Subject: [PATCH] Return precision values for non-dec columns (1.4) This is a backport of the PR #367 to `v1.4-andium` stable branch. This PR makes `ResultSetMetaData#getPrecision()` to return non-zero values for integral numbers, floating point, date/time and string columns. Unlike the `DECIMAL` type, where actual column precision is returned, for other types this call is expected to give a hint to a client software how much space the textual representation of the field is going to take. Previously returned 0 value appeared to cause problems in some of such clients. Values returned are consistent with ones returned by Postgres. Testing: new test added for all types. Fixes: #340 --- .../org/duckdb/DuckDBResultSetMetaData.java | 54 +++++++++++++-- src/test/java/org/duckdb/TestResults.java | 66 +++++++++++++++++++ 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/duckdb/DuckDBResultSetMetaData.java b/src/main/java/org/duckdb/DuckDBResultSetMetaData.java index 8b9c6dc3d..9bc233be5 100644 --- a/src/main/java/org/duckdb/DuckDBResultSetMetaData.java +++ b/src/main/java/org/duckdb/DuckDBResultSetMetaData.java @@ -310,17 +310,58 @@ public int getColumnDisplaySize(int column) throws SQLException { } public int getPrecision(int column) throws SQLException { - DuckDBColumnTypeMetaData typeMetaData = typeMetadataForColumn(column); + checkColumn(column); + DuckDBColumnTypeMetaData typeMetaData = column_types_meta[column - 1]; - if (typeMetaData == null) { - return 0; + if (typeMetaData != null) { + return typeMetaData.width; } - return typeMetaData.width; + DuckDBColumnType colType = column_types[column - 1]; + switch (colType) { + case BOOLEAN: + return 5; + case TINYINT: + case UTINYINT: + return 3; + case SMALLINT: + case USMALLINT: + return 5; + case INTEGER: + case UINTEGER: + return 10; + case BIGINT: + case UBIGINT: + return 19; + case HUGEINT: + case UHUGEINT: + return 38; + case FLOAT: + return 8; + case DOUBLE: + return 17; + case TIME: + return 15; + case DATE: + return 13; + case TIMESTAMP: + case TIMESTAMP_MS: + case TIMESTAMP_NS: + case TIMESTAMP_S: + return 29; + case TIMESTAMP_WITH_TIME_ZONE: + return 35; + case VARCHAR: + case BLOB: + return Integer.MAX_VALUE; + default: + return 0; + } } public int getScale(int column) throws SQLException { - DuckDBColumnTypeMetaData typeMetaData = typeMetadataForColumn(column); + checkColumn(column); + DuckDBColumnTypeMetaData typeMetaData = column_types_meta[column - 1]; if (typeMetaData == null) { return 0; @@ -347,10 +388,9 @@ public boolean isWrapperFor(Class iface) { return iface.isInstance(this); } - private DuckDBColumnTypeMetaData typeMetadataForColumn(int columnIndex) throws SQLException { + private void checkColumn(int columnIndex) throws SQLException { if (columnIndex > column_count) { throw new SQLException("Column index out of bounds"); } - return column_types_meta[columnIndex - 1]; } } diff --git a/src/test/java/org/duckdb/TestResults.java b/src/test/java/org/duckdb/TestResults.java index 93e7a2868..a0798fe32 100644 --- a/src/test/java/org/duckdb/TestResults.java +++ b/src/test/java/org/duckdb/TestResults.java @@ -8,6 +8,7 @@ import java.math.BigInteger; import java.sql.*; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Properties; import java.util.UUID; @@ -297,4 +298,69 @@ public static void test_results_strings_cast() throws Exception { } } } + + public static void test_results_precision() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT TRUE::BOOLEAN")) { + assertEquals(rs.getMetaData().getPrecision(1), 5); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::TINYINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 3); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::UTINYINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 3); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::SMALLINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 5); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::USMALLINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 5); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::INTEGER")) { + assertEquals(rs.getMetaData().getPrecision(1), 10); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::UINTEGER")) { + assertEquals(rs.getMetaData().getPrecision(1), 10); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::BIGINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 19); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::UBIGINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 19); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::HUGEINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 38); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::UHUGEINT")) { + assertEquals(rs.getMetaData().getPrecision(1), 38); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::FLOAT")) { + assertEquals(rs.getMetaData().getPrecision(1), 8); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::DOUBLE")) { + assertEquals(rs.getMetaData().getPrecision(1), 17); + } + try (ResultSet rs = stmt.executeQuery("SELECT 42::DECIMAL(15,3)")) { + assertEquals(rs.getMetaData().getPrecision(1), 15); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::TIME")) { + assertEquals(rs.getMetaData().getPrecision(1), 15); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::DATE")) { + assertEquals(rs.getMetaData().getPrecision(1), 13); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::TIMESTAMP")) { + assertEquals(rs.getMetaData().getPrecision(1), 29); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::TIMESTAMP WITH TIME ZONE")) { + assertEquals(rs.getMetaData().getPrecision(1), 35); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::VARCHAR")) { + assertEquals(rs.getMetaData().getPrecision(1), Integer.MAX_VALUE); + } + try (ResultSet rs = stmt.executeQuery("SELECT NULL::BLOB")) { + assertEquals(rs.getMetaData().getPrecision(1), Integer.MAX_VALUE); + } + } + } }