From 1f7623e7f5974ac1127d46f7e1629cb623795387 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 10 Oct 2026 00:14:06 +0800 Subject: [PATCH] fix(oracle): resolve current schema on the manual transaction session --- agents/drivers/oracle-go/main.go | 15 +++++++++-- agents/drivers/oracle-go/main_test.go | 37 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/agents/drivers/oracle-go/main.go b/agents/drivers/oracle-go/main.go index 2463bd34a7..e852bd984e 100644 --- a/agents/drivers/oracle-go/main.go +++ b/agents/drivers/oracle-go/main.go @@ -1698,12 +1698,23 @@ func oracleListSQLWithVisibleSchemas(baseSQL string, visibleSchemas []string) (s } func (s *server) currentSchema() (string, error) { + const sqlText = "SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL" + var schema string + // CURRENT_SCHEMA is per Oracle session. A manual transaction sets it only on + // its pinned connection and skips per-statement setSchema, so a pooled + // session would report the login user's schema and resolve unqualified + // tables against the wrong owner (#11258). + if s.manualTx != nil { + if err := s.manualTx.QueryRow(sqlText).Scan(&schema); err != nil { + return "", err + } + return strings.ToUpper(schema), nil + } db, err := s.requireDB() if err != nil { return "", err } - var schema string - if err := db.QueryRow("SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL").Scan(&schema); err != nil { + if err := db.QueryRow(sqlText).Scan(&schema); err != nil { return "", err } return strings.ToUpper(schema), nil diff --git a/agents/drivers/oracle-go/main_test.go b/agents/drivers/oracle-go/main_test.go index a5a6cb4891..f10f577efe 100644 --- a/agents/drivers/oracle-go/main_test.go +++ b/agents/drivers/oracle-go/main_test.go @@ -3901,6 +3901,29 @@ func TestManualTransactionSkipsPerStatementSetSchema(t *testing.T) { } } +func TestManualTransactionCurrentSchemaUsesPinnedSession(t *testing.T) { + db, _ := openOracleManualTxTestDB(t) + // A second pooled session that never ran ALTER SESSION SET CURRENT_SCHEMA + // still reports the login user's schema. + db.SetMaxOpenConns(2) + s := newServer() + s.db = db + + if err := s.beginManualTransaction("APP_TEST"); err != nil { + t.Fatalf("begin: %v", err) + } + schema, err := s.normalizeSchema("") + if err != nil { + t.Fatalf("normalizeSchema: %v", err) + } + if schema != "APP_TEST" { + t.Fatalf("normalizeSchema(\"\") during manual tx = %q, want APP_TEST", schema) + } + if err := s.rollbackManualTransaction(); err != nil { + t.Fatalf("rollback: %v", err) + } +} + func TestManualTransactionDisconnectRollsBack(t *testing.T) { db, driver := openOracleManualTxTestDB(t) s := newServer() @@ -4003,6 +4026,7 @@ func (d *oracleManualTxDriver) Open(string) (driver.Conn, error) { type oracleManualTxConn struct { driver *oracleManualTxDriver closed bool + schema string } func (c *oracleManualTxConn) Prepare(string) (driver.Stmt, error) { @@ -4036,6 +4060,9 @@ func (c *oracleManualTxConn) ExecContext(_ context.Context, query string, _ []dr c.driver.mu.Lock() defer c.driver.mu.Unlock() c.driver.execs = append(c.driver.execs, query) + if schema, ok := strings.CutPrefix(query, "ALTER SESSION SET CURRENT_SCHEMA = "); ok { + c.schema = strings.Trim(schema, `"`) + } return driver.RowsAffected(1), nil } @@ -4043,6 +4070,16 @@ func (c *oracleManualTxConn) QueryContext(_ context.Context, query string, _ []d c.driver.mu.Lock() defer c.driver.mu.Unlock() c.driver.queries = append(c.driver.queries, query) + if strings.Contains(query, "'CURRENT_SCHEMA'") { + schema := c.schema + if schema == "" { + schema = "LOGIN_USER" + } + return &oracleManualTxRows{ + columns: []string{"SCHEMA"}, + values: [][]driver.Value{{schema}}, + }, nil + } return &oracleManualTxRows{ columns: []string{"A"}, values: [][]driver.Value{{int64(1)}},