From b22cd9079af2ed2af2f043b4bddb3d35fb5f28b9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 13 Aug 2026 23:57:30 +0800 Subject: [PATCH] fix(jdbc): preserve multi-bit values when reading booleans --- .../main/java/app/dbx/jdbc/DbxJdbcPlugin.java | 13 +++++ .../java/app/dbx/jdbc/DbxJdbcPluginTest.java | 56 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java index 830266f6f5..87cc2c4abf 100644 --- a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java +++ b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java @@ -3026,11 +3026,24 @@ public final class DbxJdbcPlugin { int index, boolean preserveOracleDateTime ) throws SQLException { + int columnType = meta.getColumnType(index); + + if (columnType == Types.BOOLEAN) { + boolean boolValue = rs.getBoolean(index); + if (!rs.wasNull()) { + return boolValue; + } + return null; + } + Object value = rs.getObject(index); if (value == null) { return null; } if (value instanceof byte[] bytes) { + if (columnType == Types.BIT && bytes.length == 1 && (bytes[0] == 't' || bytes[0] == 'f')) { + return bytes[0] == 't'; + } return binaryToHex(bytes); } if (value instanceof Clob clob) { diff --git a/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java b/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java index dc519eb31e..92a35fb507 100644 --- a/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java +++ b/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java @@ -425,6 +425,39 @@ final class DbxJdbcPluginTest { assertEquals("GaussDB CLOB 中文内容", method.invoke(null, rs, columnMeta(Types.CLOB), 1, false)); } + @Test + void readValueConvertsGaussDbBooleanBytesWithoutCollapsingMultiBitValues() throws Exception { + Method method = DbxJdbcPlugin.class.getDeclaredMethod( + "readValue", + ResultSet.class, + ResultSetMetaData.class, + int.class, + boolean.class + ); + method.setAccessible(true); + + assertEquals(true, method.invoke(null, objectResultSet(new byte[] { 't' }), columnMeta(Types.BIT), 1, false)); + assertEquals(false, method.invoke(null, objectResultSet(new byte[] { 'f' }), columnMeta(Types.BIT), 1, false)); + assertEquals(null, method.invoke(null, objectResultSet(null), columnMeta(Types.BIT), 1, false)); + assertEquals("0x0102", method.invoke(null, objectResultSet(new byte[] { 1, 2 }), columnMeta(Types.BIT), 1, false)); + } + + @Test + void readValueUsesJdbcBooleanAccessForBooleanColumns() throws Exception { + Method method = DbxJdbcPlugin.class.getDeclaredMethod( + "readValue", + ResultSet.class, + ResultSetMetaData.class, + int.class, + boolean.class + ); + method.setAccessible(true); + + assertEquals(true, method.invoke(null, booleanResultSet(true), columnMeta(Types.BOOLEAN), 1, false)); + assertEquals(false, method.invoke(null, booleanResultSet(false), columnMeta(Types.BOOLEAN), 1, false)); + assertEquals(null, method.invoke(null, booleanResultSet(null), columnMeta(Types.BOOLEAN), 1, false)); + } + @Test void executeQueryPreservesOracleDateTimeComponent() throws Exception { List calls = new ArrayList<>(); @@ -2192,6 +2225,29 @@ final class DbxJdbcPluginTest { ); } + private static ResultSet objectResultSet(Object value) { + return (ResultSet) Proxy.newProxyInstance( + DbxJdbcPluginTest.class.getClassLoader(), + new Class[] { ResultSet.class }, + (proxy, method, args) -> switch (method.getName()) { + case "getObject" -> value; + default -> defaultValue(method.getReturnType()); + } + ); + } + + private static ResultSet booleanResultSet(Boolean value) { + return (ResultSet) Proxy.newProxyInstance( + DbxJdbcPluginTest.class.getClassLoader(), + new Class[] { ResultSet.class }, + (proxy, method, args) -> switch (method.getName()) { + case "getBoolean" -> Boolean.TRUE.equals(value); + case "wasNull" -> value == null; + default -> defaultValue(method.getReturnType()); + } + ); + } + private static DatabaseMetaData tableTypesMeta(String... types) { return (DatabaseMetaData) Proxy.newProxyInstance( DbxJdbcPluginTest.class.getClassLoader(),