fix(oceanbase): restore Oracle-mode system schema visibility

Closes #6508
This commit is contained in:
t8y2
2026-08-18 03:45:51 +00:00
parent 99066319cd
commit 28747e8d29
4 changed files with 106 additions and 41 deletions
@@ -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<String> 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<String> result = new ArrayList<>();
try (var stmt = requireConnection().createStatement();
@@ -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<String> 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<String> 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<String> sql, List<String> params, ResultSet resultSet) {
PreparedStatement statement = objectSourceStatement(params, resultSet, false);
return proxy(Connection.class, (method, args) -> {
@@ -6,6 +6,7 @@ type VisibleDatabaseConnection = Partial<ConnectionConfig>;
type SystemNameRules = {
exact?: ReadonlySet<string>;
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<Record<DatabaseType, SystemNameRules>> = {
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[] {
+9
View File
@@ -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",