From e40ce9336fe665cbc05f6a487ef17149a9cd1bc3 Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 20 Jul 2026 18:16:28 +0800 Subject: [PATCH] fix(kingbase): load V8R6 column metadata --- .../com/dbx/agent/kingbase/KingbaseAgent.java | 39 ++++++++++- .../dbx/agent/kingbase/KingbaseAgentTest.java | 64 +++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/agents/drivers/kingbase/src/main/java/com/dbx/agent/kingbase/KingbaseAgent.java b/agents/drivers/kingbase/src/main/java/com/dbx/agent/kingbase/KingbaseAgent.java index 35fecc222..d380845c6 100644 --- a/agents/drivers/kingbase/src/main/java/com/dbx/agent/kingbase/KingbaseAgent.java +++ b/agents/drivers/kingbase/src/main/java/com/dbx/agent/kingbase/KingbaseAgent.java @@ -56,6 +56,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { ); private boolean postgresCatalogMode; private boolean sqlServerIdentityCatalogMode; + private volatile boolean usePgDefaultExpressionFunction; public static final PostgresLikeAgentProfile KINGBASE_PROFILE = new PostgresLikeAgentProfile( "com.kingbase8.Driver", @@ -70,6 +71,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { protected void afterConnect(ConnectParams params, Connection connection) { postgresCatalogMode = false; sqlServerIdentityCatalogMode = false; + usePgDefaultExpressionFunction = false; setMysqlCompatMode(params.isMysql_compat_mode()); if (params.isMysql_compat_mode()) { return; @@ -489,12 +491,30 @@ public final class KingbaseAgent extends PostgresLikeAgent { } private List getRegularColumns(String schema, String table, Set primaryKeys) { + boolean usePgFunction = usePgDefaultExpressionFunction; + try { + return queryRegularColumns(schema, table, primaryKeys, usePgFunction ? "pg_get_expr" : "sys_get_expr"); + } catch (RuntimeException error) { + if (usePgFunction || !isUndefinedFunction(error, "sys_get_expr")) { + throw error; + } + usePgDefaultExpressionFunction = true; + return queryRegularColumns(schema, table, primaryKeys, "pg_get_expr"); + } + } + + private List queryRegularColumns( + String schema, + String table, + Set primaryKeys, + String defaultExpressionFunction + ) { return unchecked(() -> { List result = new ArrayList<>(); String sql = "SELECT a.attname AS column_name, " + "format_type(a.atttypid, a.atttypmod) AS data_type, " + "NOT a.attnotnull AS is_nullable, " + - "sys_get_expr(ad.adbin, ad.adrelid) AS column_default, " + + defaultExpressionFunction + "(ad.adbin, ad.adrelid) AS column_default, " + "d.description AS column_comment, " + "CASE WHEN t.typname = 'numeric' AND a.atttypmod > 0 " + "THEN ((a.atttypmod - 4) >> 16) & 65535 ELSE NULL END AS numeric_precision, " + @@ -988,6 +1008,23 @@ public final class KingbaseAgent extends PostgresLikeAgent { return insufficientPrivilege && mentionsSysFreespace; } + private static boolean isUndefinedFunction(Throwable error, String functionName) { + boolean undefinedFunction = false; + boolean mentionsFunction = false; + for (Throwable current = error; current != null; current = current.getCause()) { + if (current instanceof SQLException && "42883".equals(((SQLException) current).getSQLState())) { + undefinedFunction = true; + } + String message = current.getMessage(); + if (message != null) { + String normalized = message.toLowerCase(Locale.ROOT); + mentionsFunction |= normalized.contains(functionName.toLowerCase(Locale.ROOT)); + undefinedFunction |= normalized.contains("does not exist") || normalized.contains("不存在"); + } + } + return undefinedFunction && mentionsFunction; + } + private static void appendRoutineKindPredicate(StringBuilder sql, List args, MetadataListConstraints constraints) { if (!constraints.hasObjectTypes()) { return; diff --git a/agents/drivers/kingbase/src/test/java/com/dbx/agent/kingbase/KingbaseAgentTest.java b/agents/drivers/kingbase/src/test/java/com/dbx/agent/kingbase/KingbaseAgentTest.java index a1ee404b0..14eeaabe2 100644 --- a/agents/drivers/kingbase/src/test/java/com/dbx/agent/kingbase/KingbaseAgentTest.java +++ b/agents/drivers/kingbase/src/test/java/com/dbx/agent/kingbase/KingbaseAgentTest.java @@ -598,6 +598,21 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest { Assertions.assertNull(columns.get(0).getExtra()); } + @Test + void regularGetColumnsFallsBackToPgGetExprAndCachesTheChoice() { + List sql = new ArrayList<>(); + KingbaseAgent agent = new KingbaseAgent(); + TestSupport.setPrivateConnection(agent, defaultExpressionFallbackConnection(sql)); + + List first = agent.getColumns("public", "orders"); + List second = agent.getColumns("public", "orders"); + + Assertions.assertEquals("nextval('orders_id_seq'::regclass)", first.get(0).getColumn_default()); + Assertions.assertEquals("nextval('orders_id_seq'::regclass)", second.get(0).getColumn_default()); + Assertions.assertEquals(1, sql.stream().filter(query -> query.contains("sys_get_expr(")).count(), sql.toString()); + Assertions.assertEquals(2, sql.stream().filter(query -> query.contains("pg_get_expr(")).count(), sql.toString()); + } + @Test void mysqlCompatGetColumnsRestoresBoundedCatalogCharacterTypes() { List sql = new ArrayList<>(); @@ -902,6 +917,55 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest { }); } + private static Connection defaultExpressionFallbackConnection(List sql) { + return proxy(Connection.class, (method, args) -> { + if ("prepareStatement".equals(method.getName())) { + String query = String.valueOf(args[0]); + sql.add(query); + return proxy(PreparedStatement.class, (statementMethod, statementArgs) -> { + if ("executeQuery".equals(statementMethod.getName())) { + return resultSet(new String[]{"column_name"}, new Object[][]{{"id"}}); + } + if ("close".equals(statementMethod.getName())) return null; + return defaultValue(statementMethod.getReturnType()); + }); + } + if ("createStatement".equals(method.getName())) { + return proxy(Statement.class, (statementMethod, statementArgs) -> { + if ("executeQuery".equals(statementMethod.getName())) { + String query = String.valueOf(statementArgs[0]); + sql.add(query); + if (query.contains("sys_get_expr(")) { + throw new SQLException( + "ERROR: function sys_get_expr(pg_node_tree, oid) does not exist", + "42883" + ); + } + return resultSet( + new String[]{ + "column_name", + "data_type", + "is_nullable", + "column_default", + "column_comment", + "numeric_precision", + "numeric_scale", + "character_maximum_length" + }, + new Object[][]{ + {"id", "integer", false, "nextval('orders_id_seq'::regclass)", null, 32, 0, null} + } + ); + } + if ("close".equals(statementMethod.getName())) return null; + return defaultValue(statementMethod.getReturnType()); + }); + } + if ("isClosed".equals(method.getName())) return false; + return defaultValue(method.getReturnType()); + }); + } + private static Connection preparedConnectionWithMetadataFailure( List sql, String message,