From a289773893c845002e45604b9b08030d7ac4b87a Mon Sep 17 00:00:00 2001 From: Alex Kasko Date: Sun, 19 Apr 2026 18:27:50 +0100 Subject: [PATCH] Fix IS_NULLABLE in getColumns (1.5) This is a backport of the PR #665 to v1.5-variegata stable branch. This PR fixes the output type and value of the `IS_NULLABLE` column of the `DatabaseMetaData#getColumns()` method ([ref](https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html#getColumns-java.lang.String-java.lang.String-java.lang.String-java.lang.String-)). It was inadvertently broken by #639 that changed the underlying query to use `duckdb_columns()` instead of `information_schema.columns` view, that returns almost the same columns, but not exactly ([impl](https://github.com/duckdb/duckdb/blob/e64b98f66712b9674897bb27f7f417e5e88fb4be/src/catalog/default/default_views.cpp#L51)). Fixes: #664 --- .../org/duckdb/DuckDBDatabaseMetaData.java | 6 +++-- src/test/java/org/duckdb/TestMetadata.java | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java b/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java index 36a6e9f1c..1cfee691a 100644 --- a/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java +++ b/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java @@ -827,7 +827,7 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa sb.append("NULL AS 'BUFFER_LENGTH'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("numeric_scale AS 'DECIMAL_DIGITS'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("10 AS 'NUM_PREC_RADIX'").append(TRAILING_COMMA).append(lineSeparator()); - sb.append("CASE WHEN is_nullable = 'YES' THEN 1 else 0 END AS 'NULLABLE'") + sb.append("CASE WHEN is_nullable = TRUE THEN 1 else 0 END AS 'NULLABLE'") .append(TRAILING_COMMA) .append(lineSeparator()); sb.append("comment as 'REMARKS'").append(TRAILING_COMMA).append(lineSeparator()); @@ -836,7 +836,9 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa sb.append("NULL AS 'SQL_DATETIME_SUB'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("NULL AS 'CHAR_OCTET_LENGTH'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("column_index AS 'ORDINAL_POSITION'").append(TRAILING_COMMA).append(lineSeparator()); - sb.append("is_nullable AS 'IS_NULLABLE'").append(TRAILING_COMMA).append(lineSeparator()); + sb.append("CASE WHEN is_nullable = TRUE THEN 'YES' ELSE 'NO' END AS 'IS_NULLABLE'") + .append(TRAILING_COMMA) + .append(lineSeparator()); sb.append("NULL AS 'SCOPE_CATALOG'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("NULL AS 'SCOPE_SCHEMA'").append(TRAILING_COMMA).append(lineSeparator()); sb.append("NULL AS 'SCOPE_TABLE'").append(TRAILING_COMMA).append(lineSeparator()); diff --git a/src/test/java/org/duckdb/TestMetadata.java b/src/test/java/org/duckdb/TestMetadata.java index 89c205889..c2aa14515 100644 --- a/src/test/java/org/duckdb/TestMetadata.java +++ b/src/test/java/org/duckdb/TestMetadata.java @@ -1,6 +1,5 @@ package org.duckdb; -import static java.lang.System.lineSeparator; import static java.util.Arrays.asList; import static org.duckdb.TestDuckDBJDBC.JDBC_URL; import static org.duckdb.test.Assertions.*; @@ -1117,4 +1116,26 @@ public static void test_metadata_get_udts() throws Exception { } } } + + public static void test_metadata_is_nullable() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE tab1 (col1 INT, col2 INT NOT NULL)"); + DatabaseMetaData dbMeta = conn.getMetaData(); + try (ResultSet rs = dbMeta.getColumns(null, null, "tab1", null)) { + ResultSetMetaData rsMeta = rs.getMetaData(); + assertEquals(rsMeta.getColumnName(11), "NULLABLE"); + assertEquals(rsMeta.getColumnType(11), Types.INTEGER); + assertEquals(rsMeta.getColumnName(18), "IS_NULLABLE"); + assertEquals(rsMeta.getColumnType(18), Types.VARCHAR); + + assertTrue(rs.next()); + assertEquals(rs.getInt("NULLABLE"), DatabaseMetaData.columnNullable); + assertEquals(rs.getString("IS_NULLABLE"), "YES"); + assertTrue(rs.next()); + assertEquals(rs.getInt("NULLABLE"), DatabaseMetaData.columnNoNulls); + assertEquals(rs.getString("IS_NULLABLE"), "NO"); + assertFalse(rs.next()); + } + } + } }