From ac6b2f6cb5a508dc2f5b08dc37012944de604adf Mon Sep 17 00:00:00 2001 From: zipg Date: Wed, 8 Jul 2026 16:09:40 +0800 Subject: [PATCH] fix(oracle): use USER views for current schema objects --- agents/drivers/oracle-go/main.go | 75 +++++++++++++++++++++++++++ agents/drivers/oracle-go/main_test.go | 69 ++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/agents/drivers/oracle-go/main.go b/agents/drivers/oracle-go/main.go index ea5d13453..8cfd6d57f 100644 --- a/agents/drivers/oracle-go/main.go +++ b/agents/drivers/oracle-go/main.go @@ -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) { diff --git a/agents/drivers/oracle-go/main_test.go b/agents/drivers/oracle-go/main_test.go index d9364bc81..2ab25a77f 100644 --- a/agents/drivers/oracle-go/main_test.go +++ b/agents/drivers/oracle-go/main_test.go @@ -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%`