fix(oracle): include system schemas in schema list

This commit is contained in:
t8y2
2026-07-23 19:04:46 +08:00
parent 08e907c3db
commit 15972b222b
2 changed files with 103 additions and 12 deletions
+56 -6
View File
@@ -63,6 +63,17 @@ ORDER BY CASE
WHEN username = SYS_CONTEXT('USERENV', 'SESSION_USER') THEN 1
ELSE 2
END, username`
// Oracle schemas are users, so expose every user visible through ALL_USERS; system filtering remains database-picker behavior.
const oracleListSchemasSQL = `
SELECT username AS owner
FROM all_users
WHERE username IS NOT NULL
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`
const oracleListTablesBaseSQL = `
SELECT OBJECT_NAME, TABLE_TYPE, COMMENTS
FROM (
@@ -1062,13 +1073,44 @@ func (s *server) listSchemas(visibleSchemas []string) ([]string, error) {
if visibleSchemas != nil && len(visibleSchemas) == 0 {
return []string{}, nil
}
databases, err := s.listDatabasesFiltered(visibleSchemas)
sqlText, args := oracleListSchemasSQLWithVisibleSchemas(visibleSchemas)
rows, err := s.queryRows(sqlText, args)
if err != nil {
if isOraclePGALimitError(err) {
databases, fallbackErr := s.currentSchemaDatabase()
if fallbackErr != nil {
return nil, fallbackErr
}
result := make([]string, 0, len(databases))
for _, database := range databases {
result = append(result, database.Name)
}
return emptyIfNil(result), nil
}
return nil, err
}
result := make([]string, 0, len(databases))
for _, database := range databases {
result = append(result, database.Name)
defer s.closeRows(rows)
var result []string
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
return nil, err
}
result = append(result, name)
}
if err := rows.Err(); err != nil {
if isOraclePGALimitError(err) {
databases, fallbackErr := s.currentSchemaDatabase()
if fallbackErr != nil {
return nil, fallbackErr
}
result = result[:0]
for _, database := range databases {
result = append(result, database.Name)
}
return emptyIfNil(result), nil
}
return nil, err
}
return emptyIfNil(result), nil
}
@@ -1104,8 +1146,16 @@ func (s *server) listDatabasesFiltered(visibleSchemas []string) ([]databaseInfo,
}
func oracleListDatabasesSQLWithVisibleSchemas(visibleSchemas []string) (string, []any) {
return oracleListSQLWithVisibleSchemas(oracleListDatabasesSQL, visibleSchemas)
}
func oracleListSchemasSQLWithVisibleSchemas(visibleSchemas []string) (string, []any) {
return oracleListSQLWithVisibleSchemas(oracleListSchemasSQL, visibleSchemas)
}
func oracleListSQLWithVisibleSchemas(baseSQL string, visibleSchemas []string) (string, []any) {
if len(visibleSchemas) == 0 {
return oracleListDatabasesSQL, nil
return baseSQL, nil
}
placeholders := make([]string, 0, len(visibleSchemas))
args := make([]any, 0, len(visibleSchemas))
@@ -1114,7 +1164,7 @@ func oracleListDatabasesSQLWithVisibleSchemas(visibleSchemas []string) (string,
args = append(args, schema)
}
sqlText := strings.Replace(
oracleListDatabasesSQL,
baseSQL,
"\nORDER BY CASE",
"\n AND username IN ("+strings.Join(placeholders, ",")+")\nORDER BY CASE",
1,
+47 -6
View File
@@ -677,13 +677,16 @@ func TestListDatabasesSQLUsesUserDictionaryInsteadOfObjectDictionary(t *testing.
sqlText := strings.ToUpper(oracleListDatabasesSQL)
if !strings.Contains(sqlText, "ALL_USERS") {
t.Fatalf("schema listing should query ALL_USERS, got: %s", oracleListDatabasesSQL)
t.Fatalf("database listing should query ALL_USERS, got: %s", oracleListDatabasesSQL)
}
if strings.Contains(sqlText, "ALL_TABLES") || strings.Contains(sqlText, "ALL_VIEWS") {
t.Fatalf("schema listing should not scan object dictionaries, got: %s", oracleListDatabasesSQL)
t.Fatalf("database listing should not scan object dictionaries, got: %s", oracleListDatabasesSQL)
}
if strings.Contains(sqlText, "'DIP'") {
t.Fatalf("schema listing should not hide an existing user named DIP, got: %s", oracleListDatabasesSQL)
t.Fatalf("database listing should not hide an existing user named DIP, got: %s", oracleListDatabasesSQL)
}
if !strings.Contains(sqlText, "'SYS','SYSTEM'") || !strings.Contains(sqlText, "USERNAME NOT LIKE 'APEX_%'") {
t.Fatalf("database listing should retain system schema filtering, got: %s", oracleListDatabasesSQL)
}
}
@@ -692,16 +695,54 @@ func TestListDatabasesSQLCanApplyVisibleSchemaFilter(t *testing.T) {
upperSQL := strings.ToUpper(sqlText)
if !strings.Contains(upperSQL, "ALL_USERS") {
t.Fatalf("schema listing should query ALL_USERS, got: %s", sqlText)
t.Fatalf("database listing should query ALL_USERS, got: %s", sqlText)
}
if !strings.Contains(upperSQL, "USERNAME IN (:1,:2)") {
t.Fatalf("schema listing should apply visible schema filter, got: %s", sqlText)
t.Fatalf("database listing should apply visible schema filter, got: %s", sqlText)
}
if len(args) != 2 || args[0] != "APP" || args[1] != "REPORTING" {
t.Fatalf("visible schema args were not preserved: %#v", args)
}
if strings.Contains(upperSQL, "ALL_TABLES") || strings.Contains(upperSQL, "ALL_VIEWS") {
t.Fatalf("schema listing should not scan object dictionaries, got: %s", sqlText)
t.Fatalf("database listing should not scan object dictionaries, got: %s", sqlText)
}
if !strings.Contains(upperSQL, "'SYS','SYSTEM'") {
t.Fatalf("database visible-schema filtering should retain system exclusions, got: %s", sqlText)
}
}
func TestListSchemasSQLIncludesSystemUsersWithoutSynthesizingPublic(t *testing.T) {
sqlText := strings.ToUpper(oracleListSchemasSQL)
if !strings.Contains(sqlText, "FROM ALL_USERS") {
t.Fatalf("schema listing should query ALL_USERS, got: %s", oracleListSchemasSQL)
}
if strings.Contains(sqlText, "NOT IN") || strings.Contains(sqlText, "'SYS'") || strings.Contains(sqlText, "'SYSTEM'") {
t.Fatalf("schema listing should not hard-exclude SYS or SYSTEM, got: %s", oracleListSchemasSQL)
}
if strings.Contains(sqlText, "PUBLIC") || strings.Contains(sqlText, "UNION") {
t.Fatalf("schema listing should not synthesize PUBLIC, got: %s", oracleListSchemasSQL)
}
if !strings.Contains(sqlText, "CURRENT_SCHEMA') THEN 0") || !strings.Contains(sqlText, "SESSION_USER') THEN 1") {
t.Fatalf("schema listing should prioritize CURRENT_SCHEMA then SESSION_USER, got: %s", oracleListSchemasSQL)
}
}
func TestListSchemasSQLCanApplyVisibleSchemaFilter(t *testing.T) {
sqlText, args := oracleListSchemasSQLWithVisibleSchemas([]string{"SYS", "SYSTEM"})
upperSQL := strings.ToUpper(sqlText)
if !strings.Contains(upperSQL, "USERNAME IN (:1,:2)") {
t.Fatalf("schema listing should parameterize visible schemas, got: %s", sqlText)
}
if len(args) != 2 || args[0] != "SYS" || args[1] != "SYSTEM" {
t.Fatalf("visible schema args were not preserved: %#v", args)
}
if strings.Contains(upperSQL, "NOT IN") || strings.Contains(upperSQL, "'SYS'") || strings.Contains(upperSQL, "'SYSTEM'") {
t.Fatalf("visible schema query should not hard-exclude SYS or SYSTEM, got: %s", sqlText)
}
if !strings.Contains(upperSQL, "CURRENT_SCHEMA') THEN 0") || !strings.Contains(upperSQL, "SESSION_USER') THEN 1") {
t.Fatalf("visible schema query should preserve schema ordering, got: %s", sqlText)
}
}