From 6368c49c1641f24013f3ca814a9f5f4eccd6f734 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 12 Jul 2026 21:28:46 +0800 Subject: [PATCH] fix(kingbase): support PostgreSQL catalog mode --- .../com/dbx/agent/kingbase/KingbaseAgent.java | 27 ++++++++++ .../dbx/agent/kingbase/KingbaseAgentTest.java | 50 +++++++++++++++++++ 2 files changed, 77 insertions(+) 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 bc34878d8..94b0d79df 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 @@ -44,6 +44,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { private static final String KINGBASE_VIEW_SCHEMA = "CAST(v.schemaname AS varchar(256))"; private static final String KINGBASE_MATVIEW_NAME = "CAST(mv.matviewname AS varchar(256))"; private static final String KINGBASE_MATVIEW_SCHEMA = "CAST(mv.schemaname AS varchar(256))"; + private boolean postgresCatalogMode; public static final PostgresLikeAgentProfile KINGBASE_PROFILE = new PostgresLikeAgentProfile( "com.kingbase8.Driver", @@ -56,13 +57,28 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override protected void afterConnect(ConnectParams params, Connection connection) { + postgresCatalogMode = false; if (params.isMysql_compat_mode()) { setMysqlCompatMode(true); + return; + } + postgresCatalogMode = !catalogExists(connection, "sys_catalog.sys_namespace") + && catalogExists(connection, "pg_catalog.pg_namespace"); + } + + private static boolean catalogExists(Connection connection, String catalog) { + try (Statement stmt = connection.createStatement(); + ResultSet ignored = stmt.executeQuery("SELECT 1 FROM " + catalog + " WHERE 1 = 0")) { + return true; + } catch (Exception ignored) { + // Kingbase compatibility modes expose different catalog families. + return false; } } @Override public List listDatabases() { + if (postgresCatalogMode) return super.listDatabases(); return unchecked(() -> { if (isMysqlCompatMode()) { List result = queryDatabases("SELECT current_database() AS database_name"); @@ -96,6 +112,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listSchemas() { + if (postgresCatalogMode) return super.listSchemas(); return unchecked(() -> { List result = new ArrayList<>(); String sql = isMysqlCompatMode() @@ -122,6 +139,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listTables(String schema) { + if (postgresCatalogMode) return super.listTables(schema); if (isMysqlCompatMode()) { return listTables(schema, "table_type IN ('BASE TABLE', 'VIEW')"); } @@ -130,6 +148,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listTables(String schema, MetadataListConstraints constraints) { + if (postgresCatalogMode) return super.listTables(schema, constraints); MetadataListConstraints normalized = MetadataListConstraints.orNone(constraints); if (isUnconstrained(normalized)) { return listTables(schema); @@ -199,6 +218,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listObjects(String schema) { + if (postgresCatalogMode) return super.listObjects(schema); return unchecked(() -> { String effectiveSchema = effectiveSchema(schema); List result = new ArrayList<>(); @@ -235,6 +255,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listObjects(String schema, MetadataListConstraints constraints) { + if (postgresCatalogMode) return super.listObjects(schema, constraints); MetadataListConstraints normalized = MetadataListConstraints.orNone(constraints); if (isUnconstrained(normalized)) { return listObjects(schema); @@ -299,6 +320,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public ObjectSource getObjectSource(String schema, String name, String objectType) { + if (postgresCatalogMode) return super.getObjectSource(schema, name, objectType); if ("FUNCTION".equalsIgnoreCase(objectType) || "PROCEDURE".equalsIgnoreCase(objectType)) { return routineSource(schema, name, objectType); } @@ -354,6 +376,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List getColumns(String schema, String table) { + if (postgresCatalogMode) return super.getColumns(schema, table); return unchecked(() -> { Set primaryKeys = primaryKeys(schema, table); if (!isMysqlCompatMode()) { @@ -444,6 +467,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listIndexes(String schema, String table) { + if (postgresCatalogMode) return super.listIndexes(schema, table); return unchecked(() -> { Map indexes = new LinkedHashMap<>(); String sql = "SELECT i.relname AS index_name, am.amname AS index_type, " + @@ -487,6 +511,7 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listForeignKeys(String schema, String table) { + if (postgresCatalogMode) return super.listForeignKeys(schema, table); return unchecked(() -> { List result = new ArrayList<>(); String sql = "SELECT fk.constraint_name, fk.column_name, pk.table_name AS ref_table, pk.column_name AS ref_column " + @@ -525,11 +550,13 @@ public final class KingbaseAgent extends PostgresLikeAgent { @Override public List listTriggers(String schema, String table) { + if (postgresCatalogMode) return super.listTriggers(schema, table); return Collections.emptyList(); } @Override public String setSchemaSQL(String schema) { + if (postgresCatalogMode) return super.setSchemaSQL(schema); // Kingbase searches sys_catalog implicitly before user schemas unless it // is listed explicitly. Put it after the selected schema so business // tables named like system tables (for example sys_config) win. 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 1bc199ad5..0e7a55be8 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 @@ -1,6 +1,7 @@ package com.dbx.agent.kingbase; import com.dbx.agent.ColumnInfo; +import com.dbx.agent.ConnectParams; import com.dbx.agent.DatabaseAgent; import com.dbx.agent.DatabaseInfo; import com.dbx.agent.IndexInfo; @@ -115,6 +116,27 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest { Assertions.assertFalse(sql.get(0).contains("SYS%"), sql.get(0)); } + @Test + void postgresCompatModeUsesPostgresCatalogForMetadata() throws Exception { + List sql = new ArrayList<>(); + KingbaseAgent agent = new KingbaseAgent(); + Connection connection = postgresCatalogConnection(sql, resultSet( + new String[]{"schema_name"}, + new Object[][]{{"public"}} + )); + + Method afterConnect = KingbaseAgent.class.getDeclaredMethod("afterConnect", ConnectParams.class, Connection.class); + afterConnect.setAccessible(true); + afterConnect.invoke(agent, new ConnectParams(), connection); + TestSupport.setPrivateConnection(agent, connection); + + Assertions.assertEquals(List.of("public"), agent.listSchemas()); + Assertions.assertEquals("SELECT 1 FROM sys_catalog.sys_namespace WHERE 1 = 0", sql.get(0)); + Assertions.assertEquals("SELECT 1 FROM pg_catalog.pg_namespace WHERE 1 = 0", sql.get(1)); + Assertions.assertTrue(sql.get(2).contains("FROM pg_catalog.pg_namespace"), sql.get(2)); + Assertions.assertEquals("SET search_path TO \"app\"", agent.setSchemaSQL("app")); + } + @Test void mysqlCompatListTablesUsesInformationSchema() { List sql = new ArrayList<>(); @@ -462,6 +484,34 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest { }); } + private static Connection postgresCatalogConnection(List sql, ResultSet metadataResult) { + return proxy(Connection.class, (method, args) -> { + 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_catalog.sys_namespace")) { + throw new SQLException("relation does not exist: sys_catalog.sys_namespace"); + } + return resultSet(new String[]{"probe"}, new Object[][]{}); + } + return defaultValue(statementMethod.getReturnType()); + }); + } + 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 metadataResult; + return defaultValue(statementMethod.getReturnType()); + }); + } + if ("isClosed".equals(method.getName())) return false; + return defaultValue(method.getReturnType()); + }); + } + private static ResultSet resultSet(String[] columns, Object[][] rows) { int[] index = {-1}; return proxy(ResultSet.class, (method, args) -> {