fix(oracle): use USER views for current schema objects

This commit is contained in:
zipg 2026-07-08 16:09:40 +08:00 committed by GitHub
parent 9319efb9e5
commit ac6b2f6cb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 144 additions and 0 deletions

View File

@ -75,6 +75,21 @@ FROM ALL_OBJECTS o
WHERE o.OWNER = :2
AND o.OBJECT_TYPE = 'VIEW'
)`
const oracleListTablesSessionUserBaseSQL = `
SELECT OBJECT_NAME, TABLE_TYPE, COMMENTS
FROM (
SELECT t.TABLE_NAME AS OBJECT_NAME,
'TABLE' AS TABLE_TYPE,
CAST(NULL AS VARCHAR2(4000)) AS COMMENTS
FROM USER_TABLES t
WHERE t.NESTED = 'NO'
UNION ALL
SELECT o.OBJECT_NAME,
'VIEW' AS TABLE_TYPE,
CAST(NULL AS VARCHAR2(4000)) AS COMMENTS
FROM USER_OBJECTS o
WHERE o.OBJECT_TYPE = 'VIEW'
)`
const oracleListTablesOrderSQL = `ORDER BY OBJECT_NAME`
const oracleListTablesSQL = oracleListTablesBaseSQL + "\n" + oracleListTablesOrderSQL
const oracleListObjectsBaseSQL = `
@ -94,6 +109,21 @@ FROM ALL_OBJECTS o
WHERE o.OWNER = :2
AND o.OBJECT_TYPE IN ('VIEW', 'PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY')
)`
const oracleListObjectsSessionUserBaseSQL = `
SELECT OBJECT_NAME, OBJECT_TYPE, COMMENTS
FROM (
SELECT t.TABLE_NAME AS OBJECT_NAME,
'TABLE' AS OBJECT_TYPE,
CAST(NULL AS VARCHAR2(4000)) AS COMMENTS
FROM USER_TABLES t
WHERE t.NESTED = 'NO'
UNION ALL
SELECT o.OBJECT_NAME,
CASE o.OBJECT_TYPE WHEN 'PACKAGE BODY' THEN 'PACKAGE_BODY' ELSE o.OBJECT_TYPE END AS OBJECT_TYPE,
CAST(NULL AS VARCHAR2(4000)) AS COMMENTS
FROM USER_OBJECTS o
WHERE o.OBJECT_TYPE IN ('VIEW', 'PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY')
)`
const oracleListObjectsOrderSQL = `ORDER BY CASE OBJECT_TYPE
WHEN 'TABLE' THEN 0
WHEN 'VIEW' THEN 1
@ -785,6 +815,23 @@ func (s *server) normalizeSchema(schema string) (string, error) {
return strings.ToUpper(schema), nil
}
func (s *server) sessionUser() (string, error) {
db, err := s.requireDB()
if err != nil {
return "", err
}
var username string
if err := db.QueryRow("SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL").Scan(&username); err != nil {
return "", err
}
return strings.ToUpper(username), nil
}
func (s *server) schemaIsSessionUser(schema string) bool {
username, err := s.sessionUser()
return err == nil && strings.EqualFold(schema, username)
}
type oracleMetadataListQuery struct {
SQL string
Args []any
@ -822,6 +869,17 @@ func oracleListTablesQuery(schema string, constraints metadataListConstraints) o
)
}
func oracleListSessionUserTablesQuery(constraints metadataListConstraints) oracleMetadataListQuery {
return oracleConstrainedMetadataListQuery(
oracleListTablesSessionUserBaseSQL,
"OBJECT_NAME, TABLE_TYPE, COMMENTS",
"TABLE_TYPE",
oracleListTablesOrderSQL,
nil,
constraints,
)
}
func oracleListObjectsQuery(schema string, constraints metadataListConstraints) oracleMetadataListQuery {
return oracleConstrainedMetadataListQuery(
oracleListObjectsBaseSQL,
@ -833,6 +891,17 @@ func oracleListObjectsQuery(schema string, constraints metadataListConstraints)
)
}
func oracleListSessionUserObjectsQuery(constraints metadataListConstraints) oracleMetadataListQuery {
return oracleConstrainedMetadataListQuery(
oracleListObjectsSessionUserBaseSQL,
"OBJECT_NAME, OBJECT_TYPE, COMMENTS",
"OBJECT_TYPE",
oracleListObjectsOrderSQL,
nil,
constraints,
)
}
func oracleConstrainedMetadataListQuery(baseSQL, selectList, typeColumn, orderSQL string, baseArgs []any, constraints metadataListConstraints) oracleMetadataListQuery {
args := append([]any{}, baseArgs...)
where := make([]string, 0, 2)
@ -923,6 +992,9 @@ func (s *server) listTables(schema string, constraints metadataListConstraints)
return nil, err
}
query := oracleListTablesQuery(schema, constraints)
if s.schemaIsSessionUser(schema) {
query = oracleListSessionUserTablesQuery(constraints)
}
rows, err := s.queryRows(query.SQL, query.Args)
if err != nil {
if isOraclePGALimitError(err) {
@ -948,6 +1020,9 @@ func (s *server) listObjects(schema string, constraints metadataListConstraints)
return nil, err
}
query := oracleListObjectsQuery(schema, constraints)
if s.schemaIsSessionUser(schema) {
query = oracleListSessionUserObjectsQuery(constraints)
}
rows, err := s.queryRows(query.SQL, query.Args)
if err != nil {
if isOraclePGALimitError(err) {

View File

@ -474,6 +474,41 @@ func TestListTablesQueryAppliesMetadataConstraints(t *testing.T) {
}
}
func TestListSessionUserTablesQueryUsesUserDictionary(t *testing.T) {
query := oracleListSessionUserTablesQuery(metadataListConstraints{
Filter: "u_r",
Limit: 501,
Offset: 10,
ObjectTypes: []string{"view", "TABLE", "TABLE"},
})
sqlText := strings.ToUpper(query.SQL)
if !strings.Contains(sqlText, "USER_TABLES") || !strings.Contains(sqlText, "USER_OBJECTS") {
t.Fatalf("session-user table listing should use USER_* dictionaries, got: %s", query.SQL)
}
if strings.Contains(sqlText, "ALL_TABLES") || strings.Contains(sqlText, "ALL_OBJECTS") {
t.Fatalf("session-user table listing should avoid ALL_* dictionaries, got: %s", query.SQL)
}
if strings.Contains(sqlText, "OWNER =") {
t.Fatalf("session-user table listing should not add owner predicates, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "UPPER(OBJECT_NAME) LIKE :1 ESCAPE '\\'") {
t.Fatalf("table listing should push filter predicate, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "TABLE_TYPE IN (:2,:3)") {
t.Fatalf("table listing should push table type predicate, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "ROWNUM <= :4") || !strings.Contains(sqlText, "DBX_RN > :5") {
t.Fatalf("table listing should use rownum pagination, got: %s", query.SQL)
}
if len(query.Args) != 5 {
t.Fatalf("unexpected args: %#v", query.Args)
}
if query.Args[0] != "%U%\\_%R%" || query.Args[1] != "TABLE" || query.Args[2] != "VIEW" || query.Args[3] != 511 || query.Args[4] != 10 {
t.Fatalf("constraints args were not normalized: %#v", query.Args)
}
}
func TestListObjectsSQLUsesSplitDictionaryQuery(t *testing.T) {
sqlText := strings.ToUpper(oracleListObjectsSQL)
@ -516,6 +551,40 @@ func TestListObjectsQueryAppliesMetadataConstraints(t *testing.T) {
}
}
func TestListSessionUserObjectsQueryUsesUserDictionary(t *testing.T) {
query := oracleListSessionUserObjectsQuery(metadataListConstraints{
Filter: "pkg%",
Limit: 25,
ObjectTypes: []string{"FUNCTION", "package"},
})
sqlText := strings.ToUpper(query.SQL)
if !strings.Contains(sqlText, "USER_TABLES") || !strings.Contains(sqlText, "USER_OBJECTS") {
t.Fatalf("session-user object listing should use USER_* dictionaries, got: %s", query.SQL)
}
if strings.Contains(sqlText, "ALL_TABLES") || strings.Contains(sqlText, "ALL_OBJECTS") {
t.Fatalf("session-user object listing should avoid ALL_* dictionaries, got: %s", query.SQL)
}
if strings.Contains(sqlText, "OWNER =") {
t.Fatalf("session-user object listing should not add owner predicates, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "UPPER(OBJECT_NAME) LIKE :1 ESCAPE '\\'") {
t.Fatalf("object listing should push filter predicate, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "OBJECT_TYPE IN (:2,:3)") {
t.Fatalf("object listing should push object type predicate, got: %s", query.SQL)
}
if !strings.Contains(sqlText, "ROWNUM <= :4") || !strings.Contains(sqlText, "DBX_RN > :5") {
t.Fatalf("object listing should use rownum pagination, got: %s", query.SQL)
}
if len(query.Args) != 5 {
t.Fatalf("unexpected args: %#v", query.Args)
}
if query.Args[0] != "%P%K%G%\\%%" || query.Args[1] != "FUNCTION" || query.Args[2] != "PACKAGE" || query.Args[3] != 25 || query.Args[4] != 0 {
t.Fatalf("object constraints args were not normalized: %#v", query.Args)
}
}
func TestOracleFuzzyLikePatternEscapesSpecialCharacters(t *testing.T) {
got := oracleFuzzyLikePattern(`a_%\b`)
want := `%a%\_%\%%\\%b%`