diff --git a/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java b/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java index 203ee2e127..38279363eb 100644 --- a/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java +++ b/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java @@ -461,7 +461,21 @@ public final class DamengAgent extends AbstractJdbcAgent { if (legacyJdbcMetadata) { return unchecked(() -> listJdbcSchemas().stream().map(DatabaseInfo::new).toList()); } - return unchecked(() -> listVisibleUsers().stream().map(DatabaseInfo::new).toList()); + // DM8 ALL_USERS is privilege-filtered: a normal user only sees itself, so prefer the + // full SYS.SYSOBJECTS catalog (mirroring listSchemas; newer/hardened builds require + // the SOI role) and fall back to ALL_USERS when the catalog is not readable. + return unchecked(() -> { + try { + return listVisibleSchemas().stream().map(DatabaseInfo::new).toList(); + } catch (SQLException catalogError) { + try { + return listVisibleUsers().stream().map(DatabaseInfo::new).toList(); + } catch (Exception fallbackError) { + catalogError.addSuppressed(fallbackError); + throw catalogError; + } + } + }); } @Override diff --git a/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java b/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java index 12d9f0b1af..0a97b6b519 100644 --- a/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java +++ b/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java @@ -52,6 +52,48 @@ class DamengAgentMetadataTest { Assertions.assertTrue(metadataCalls.contains("getTables:DBX\\_TEST"), metadataCalls.toString()); } + @Test + void listDatabasesPrefersFullSchemaCatalogOverPrivilegeFilteredAllUsers() { + DamengAgent agent = new DamengAgent(); + List sqls = new ArrayList<>(); + TestSupport.setPrivateConnection(agent, databaseListConnection(sqls, null, null)); + + List databases = agent.listDatabases(); + + Assertions.assertEquals(List.of("APP", "E2E_NORMAL", "SYSDBA"), databases.stream().map(DatabaseInfo::getName).toList()); + Assertions.assertEquals(1, sqls.size(), String.join("\n", sqls)); + Assertions.assertTrue(sqls.get(0).contains("SYS.SYSOBJECTS"), sqls.get(0)); + Assertions.assertTrue(sqls.stream().noneMatch(sql -> sql.contains("ALL_USERS")), String.join("\n", sqls)); + } + + @Test + void listDatabasesFallsBackToAllUsersWithoutSysObjectsPrivilege() { + DamengAgent agent = new DamengAgent(); + List sqls = new ArrayList<>(); + TestSupport.setPrivateConnection(agent, databaseListConnection(sqls, "no SYS.SYSOBJECTS privilege", null)); + + List databases = agent.listDatabases(); + + // 无 SOI 角色的普通用户:ALL_USERS 受自主访问控制过滤,只能看到自己,按权限返回属正确行为。 + Assertions.assertEquals(List.of("E2E_NORMAL"), databases.stream().map(DatabaseInfo::getName).toList()); + Assertions.assertEquals(2, sqls.size(), String.join("\n", sqls)); + Assertions.assertTrue(sqls.get(0).contains("SYS.SYSOBJECTS"), sqls.get(0)); + Assertions.assertTrue(sqls.get(1).contains("ALL_USERS"), sqls.get(1)); + } + + @Test + void listDatabasesPreservesCatalogErrorWhenAllUsersFails() { + DamengAgent agent = new DamengAgent(); + SQLException usersError = new SQLException("ALL_USERS query failed"); + TestSupport.setPrivateConnection(agent, databaseListConnection(new ArrayList<>(), "no SYS.SYSOBJECTS privilege", usersError)); + + RuntimeException error = Assertions.assertThrows(RuntimeException.class, agent::listDatabases); + + Assertions.assertEquals("no SYS.SYSOBJECTS privilege", error.getCause().getMessage()); + Assertions.assertEquals(1, error.getCause().getSuppressed().length); + Assertions.assertSame(usersError, error.getCause().getSuppressed()[0]); + } + @Test void usesColumnCommentsMetadataQuery() { DamengAgent agent = new DamengAgent(); @@ -1890,6 +1932,43 @@ class DamengAgentMetadataTest { }); } + private static Connection databaseListConnection( + List sqls, + String catalogError, + SQLException usersError + ) { + return proxy(Connection.class, (method, args) -> { + String name = method.getName(); + if ("prepareStatement".equals(name)) { + String sql = (String) args[0]; + sqls.add(sql); + if (sql.contains("SYS.SYSOBJECTS")) { + if (catalogError != null) { + return failingMetadataStatement(catalogError); + } + return metadataStatement(List.of(List.of("APP"), List.of("E2E_NORMAL"), List.of("SYSDBA"))); + } + if (sql.contains("ALL_USERS")) { + if (usersError != null) { + return failingMetadataStatement(usersError); + } + return metadataStatement(List.of(List.of("E2E_NORMAL"))); + } + throw new AssertionError("Unexpected SQL: " + sql); + } + if ("getMetaData".equals(name)) { + throw new AssertionError("listDatabases must not fall back to JDBC metadata getSchemas"); + } + if ("close".equals(name)) { + return null; + } + if ("isClosed".equals(name)) { + return false; + } + return defaultValue(method.getReturnType()); + }); + } + private static Connection schemaConnection(List params) { return proxy(Connection.class, (method, args) -> { String name = method.getName();