From 28747e8d29538f09ee1a2c4c3c87287e1da4564e Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 18 Aug 2026 03:19:20 +0000 Subject: [PATCH] fix(oceanbase): restore Oracle-mode system schema visibility Closes #6508 --- .../oceanbaseoracle/OceanBaseOracleAgent.java | 10 +-- .../OceanBaseOracleAgentTest.java | 53 +++++++++++++ .../src/lib/database/visibleDatabases.ts | 75 +++++++++++-------- .../connectionVisibleDatabases.test.ts | 9 +++ 4 files changed, 106 insertions(+), 41 deletions(-) diff --git a/agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java b/agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java index dcc218dfae..7cf380ff7c 100644 --- a/agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java +++ b/agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; public final class OceanBaseOracleAgent extends ConfiguredJdbcAgent { private static final long MICROS_PER_SECOND = 1_000_000L; @@ -721,23 +720,16 @@ public final class OceanBaseOracleAgent extends ConfiguredJdbcAgent { } private List querySchemaNames() throws SQLException { - String placeholders = SYSTEM_SCHEMAS.stream() - .map(schema -> "'" + schema + "'") - .collect(Collectors.joining(",")); String sql = """ SELECT username FROM ALL_USERS WHERE username IS NOT NULL - AND username NOT IN (%s) - AND username NOT LIKE 'APEX_%%' - AND username NOT LIKE 'FLOWS_%%' - AND username NOT LIKE '%%$%%' ORDER BY CASE WHEN username = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') THEN 0 WHEN username = SYS_CONTEXT('USERENV', 'SESSION_USER') THEN 1 ELSE 2 END, username - """.formatted(placeholders).stripIndent().trim(); + """.stripIndent().trim(); List result = new ArrayList<>(); try (var stmt = requireConnection().createStatement(); diff --git a/agents/drivers/oceanbase-oracle/src/test/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgentTest.java b/agents/drivers/oceanbase-oracle/src/test/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgentTest.java index 7333f0ea34..7e5709cdd4 100644 --- a/agents/drivers/oceanbase-oracle/src/test/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgentTest.java +++ b/agents/drivers/oceanbase-oracle/src/test/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgentTest.java @@ -27,6 +27,7 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; class OceanBaseOracleAgentTest { @Test @@ -70,6 +71,36 @@ class OceanBaseOracleAgentTest { ); } + @Test + void schemaListingReturnsEveryNonBlankUserWithoutHardSystemExclusions() { + List sql = new ArrayList<>(); + OceanBaseOracleAgent agent = new OceanBaseOracleAgent(); + TestSupport.setPrivateConnection(agent, schemaConnection(sql, resultSet( + new String[]{"USERNAME"}, + new Object[][]{ + {"SYS"}, + {null}, + {" "}, + {"APEX_240100"}, + {"APP$USER"}, + {"REPORTING"} + } + ))); + + Assertions.assertEquals( + List.of("SYS", "APEX_240100", "APP$USER", "REPORTING"), + agent.listSchemas() + ); + String schemaSql = sql.get(0).toUpperCase(Locale.ROOT); + Assertions.assertTrue(schemaSql.contains("FROM ALL_USERS"), schemaSql); + Assertions.assertTrue(schemaSql.contains("USERNAME IS NOT NULL"), schemaSql); + Assertions.assertFalse(schemaSql.contains("NOT IN"), schemaSql); + Assertions.assertFalse(schemaSql.contains("USERNAME NOT LIKE"), schemaSql); + Assertions.assertTrue(schemaSql.contains("CURRENT_SCHEMA') THEN 0"), schemaSql); + Assertions.assertTrue(schemaSql.contains("SESSION_USER') THEN 1"), schemaSql); + Assertions.assertTrue(schemaSql.endsWith("ELSE 2\nEND, USERNAME"), schemaSql); + } + @Test void appendsCompatibleOjdbcVersionToCustomJdbcUrl() { ConnectParams params = new ConnectParams(); @@ -443,6 +474,28 @@ class OceanBaseOracleAgentTest { }); } + private static Connection schemaConnection(List sql, ResultSet resultSet) { + Statement statement = proxy(Statement.class, (method, args) -> { + if ("executeQuery".equals(method.getName())) { + sql.add(String.valueOf(args[0])); + return resultSet; + } + if ("close".equals(method.getName())) { + return null; + } + return defaultValue(method.getReturnType()); + }); + return proxy(Connection.class, (method, args) -> { + if ("createStatement".equals(method.getName())) { + return statement; + } + if ("isClosed".equals(method.getName())) { + return false; + } + return defaultValue(method.getReturnType()); + }); + } + private static Connection objectSourceConnection(List sql, List params, ResultSet resultSet) { PreparedStatement statement = objectSourceStatement(params, resultSet, false); return proxy(Connection.class, (method, args) -> { diff --git a/apps/desktop/src/lib/database/visibleDatabases.ts b/apps/desktop/src/lib/database/visibleDatabases.ts index f4c7ce9c69..774afdd2a4 100644 --- a/apps/desktop/src/lib/database/visibleDatabases.ts +++ b/apps/desktop/src/lib/database/visibleDatabases.ts @@ -6,6 +6,7 @@ type VisibleDatabaseConnection = Partial; type SystemNameRules = { exact?: ReadonlySet; prefixes?: readonly string[]; + contains?: readonly string[]; }; type SchemaFilterOptions = { @@ -80,39 +81,48 @@ const POSTGRES_LIKE_SYSTEM_SCHEMA_RULES: SystemNameRules = { prefixes: ["pg_temp_", "pg_toast_temp_"], }; +const ORACLE_SYSTEM_SCHEMA_NAMES = new Set([ + "anonymous", + "appqossys", + "audsys", + "ctxsys", + "dbsnmp", + "dvf", + "dvsys", + "exfsys", + "flows_files", + "gsmadmin_internal", + "mddata", + "mdsys", + "mgmt_view", + "olapsys", + "orddata", + "ordplugins", + "ordsys", + "outln", + "owbsys", + "remote_scheduler_agent", + "si_informtn_schema", + "sys", + "sysback", + "sysdg", + "syskm", + "system", + "wmsys", + "xdb", + "xs$null", +]); + +const OCEANBASE_ORACLE_SYSTEM_SCHEMA_NAMES = new Set([...ORACLE_SYSTEM_SCHEMA_NAMES, "apex_public_user", "dbsfwuser", "dgpdb_int", "dip", "ggsys", "gsmcatuser", "gsmrootuser", "gsmuser", "lbacsys", "ojvmsys", "ops$oracle", "oracle_ocm", "pdbadmin", "sysbackup", "sysman", "sysrac"]); + const SYSTEM_SCHEMA_RULES: Partial> = { oracle: { - exact: new Set([ - "anonymous", - "appqossys", - "audsys", - "ctxsys", - "dbsnmp", - "dvf", - "dvsys", - "exfsys", - "flows_files", - "gsmadmin_internal", - "mddata", - "mdsys", - "mgmt_view", - "olapsys", - "orddata", - "ordplugins", - "ordsys", - "outln", - "owbsys", - "remote_scheduler_agent", - "si_informtn_schema", - "sys", - "sysback", - "sysdg", - "syskm", - "system", - "wmsys", - "xdb", - "xs$null", - ]), + exact: ORACLE_SYSTEM_SCHEMA_NAMES, + }, + "oceanbase-oracle": { + exact: OCEANBASE_ORACLE_SYSTEM_SCHEMA_NAMES, + prefixes: ["apex_", "flows_"], + contains: ["$"], }, dameng: { exact: new Set(["_sys_statistics", "ctisys", "dba", "sys", "sys_dba", "sys_phm", "sysauditor", "sysdba", "sysdbo", "syssso", "system"]), @@ -171,7 +181,8 @@ export function isSystemSchemaName(databaseType: DatabaseType | undefined, schem const rules = SYSTEM_SCHEMA_RULES[databaseType]; if (!rules) return false; if (rules.exact?.has(normalized)) return true; - return rules.prefixes?.some((prefix) => normalized.startsWith(prefix)) ?? false; + if (rules.prefixes?.some((prefix) => normalized.startsWith(prefix))) return true; + return rules.contains?.some((part) => normalized.includes(part)) ?? false; } export function filterDatabaseNamesForConnection(databaseNames: string[], connection: VisibleDatabaseConnection | undefined): string[] { diff --git a/packages/app-tests/connectionVisibleDatabases.test.ts b/packages/app-tests/connectionVisibleDatabases.test.ts index c3067575d6..4d827ebca0 100644 --- a/packages/app-tests/connectionVisibleDatabases.test.ts +++ b/packages/app-tests/connectionVisibleDatabases.test.ts @@ -100,6 +100,15 @@ test("OceanBase Oracle uses schema filtering for visible object selection", () = assert.equal(connectionUsesVisibleSchemaFilter(config({ db_type: "mysql", driver_profile: "oceanbase" })), false); }); +test("OceanBase Oracle applies Oracle system schema visibility rules", () => { + const schemas = ["APP", "SYS", "SYSTEM", "APEX_240100", "FLOWS_240100", "APP$USER"]; + + assert.deepEqual(filterSchemaNamesForVisiblePicker(schemas, config({ db_type: "oceanbase-oracle", username: "APP" })), ["APP"]); + assert.deepEqual(filterSchemaNamesForVisiblePicker(schemas, config({ db_type: "oceanbase-oracle", username: "SYS" })), ["APP", "SYS"]); + assert.deepEqual(filterSchemaNamesForConnection(schemas, config({ db_type: "oceanbase-oracle", show_system_schemas: true }), ""), schemas); + assert.deepEqual(filterSchemaNamesForConnection(schemas, config({ db_type: "oceanbase-oracle", visible_schemas: { "": ["SYS", "APEX_240100", "FLOWS_240100", "APP$USER"] } }), ""), ["SYS", "APEX_240100", "FLOWS_240100", "APP$USER"]); +}); + test("Oracle JDBC uses schema filtering for visible object selection", () => { const connection = config({ db_type: "jdbc",