mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-03 09:11:23 +08:00
🐛 fix(sql-editor): 修复 Oracle 事务结束并补充 Redis 拓扑提示
- SQL 编辑器:Oracle 托管事务优先使用 transaction provider 完成提交和回滚 - Redis:拆分 Key 浏览工具栏并展示 Cluster/Sentinel 拓扑上下文 - 测试:补充 Oracle 事务结束和 Redis 拓扑头部回归用例
This commit is contained in:
@@ -683,6 +683,89 @@ func TestDBQueryMultiTransactionalUsesImplicitSessionTransactionForOracle(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBQueryMultiTransactionalOraclePrefersTransactionProviderForFinish(t *testing.T) {
|
||||
originalNewDatabaseFunc := newDatabaseFunc
|
||||
t.Cleanup(func() {
|
||||
newDatabaseFunc = originalNewDatabaseFunc
|
||||
})
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
finish func(*App, string) connection.QueryResult
|
||||
wantCommitCalls int
|
||||
wantRollbackCalls int
|
||||
}{
|
||||
{
|
||||
name: "commit",
|
||||
finish: func(app *App, transactionID string) connection.QueryResult {
|
||||
return app.DBCommitTransaction(transactionID)
|
||||
},
|
||||
wantCommitCalls: 1,
|
||||
},
|
||||
{
|
||||
name: "rollback",
|
||||
finish: func(app *App, transactionID string) connection.QueryResult {
|
||||
return app.DBRollbackTransaction(transactionID)
|
||||
},
|
||||
wantRollbackCalls: 1,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stmt := "UPDATE users SET name = 'new' WHERE id = 1"
|
||||
fakeDB := &fakeTransactionalDB{
|
||||
fakeBatchWriteDB: fakeBatchWriteDB{
|
||||
execAffected: map[string]int64{
|
||||
stmt: 1,
|
||||
},
|
||||
execErr: map[string]error{
|
||||
"COMMIT": errors.New("oracle commit rows affected unavailable"),
|
||||
"ROLLBACK": errors.New("oracle rollback rows affected unavailable"),
|
||||
},
|
||||
},
|
||||
}
|
||||
newDatabaseFunc = func(dbType string) (db.Database, error) {
|
||||
return fakeDB, nil
|
||||
}
|
||||
|
||||
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
|
||||
config := connection.ConnectionConfig{Type: "oracle", Host: "127.0.0.1", Port: 1521, User: "app"}
|
||||
|
||||
result := app.DBQueryMultiTransactional(config, "ORCLPDB1", stmt, "oracle-provider-finish-"+tt.name)
|
||||
if !result.Success {
|
||||
t.Fatalf("expected Oracle transactional query success, got failure: %s", result.Message)
|
||||
}
|
||||
if result.TransactionID == "" || !result.TransactionPending {
|
||||
t.Fatalf("expected pending transaction metadata, got id=%q pending=%v", result.TransactionID, result.TransactionPending)
|
||||
}
|
||||
if fakeDB.session != nil {
|
||||
t.Fatal("expected Oracle to use transaction provider instead of plain session provider")
|
||||
}
|
||||
if fakeDB.txSession == nil {
|
||||
t.Fatal("expected Oracle to open a transaction provider session")
|
||||
}
|
||||
|
||||
finishResult := tt.finish(app, result.TransactionID)
|
||||
if !finishResult.Success {
|
||||
t.Fatalf("expected Oracle transaction %s success through transaction provider, got failure: %s", tt.name, finishResult.Message)
|
||||
}
|
||||
if fakeDB.txSession.commitCalls != tt.wantCommitCalls {
|
||||
t.Fatalf("expected commitCalls=%d, got %d", tt.wantCommitCalls, fakeDB.txSession.commitCalls)
|
||||
}
|
||||
if fakeDB.txSession.rollbackCalls != tt.wantRollbackCalls {
|
||||
t.Fatalf("expected rollbackCalls=%d, got %d", tt.wantRollbackCalls, fakeDB.txSession.rollbackCalls)
|
||||
}
|
||||
if !fakeDB.txSession.closed {
|
||||
t.Fatal("expected transaction provider session to close after finish")
|
||||
}
|
||||
for _, query := range fakeDB.execQueries {
|
||||
if query == "COMMIT" || query == "ROLLBACK" {
|
||||
t.Fatalf("expected finish to avoid plain ExecContext(%q), got exec queries %#v", query, fakeDB.execQueries)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBQueryMultiTransactionalUsesOracleImplicitSessionForOceanBaseOracleProtocol(t *testing.T) {
|
||||
originalNewDatabaseFunc := newDatabaseFunc
|
||||
originalVerifyDriverAgentRevisionFunc := verifyDriverAgentRevisionFunc
|
||||
|
||||
@@ -68,21 +68,7 @@ func (a *App) DBQueryMultiTransactional(config connection.ConnectionConfig, dbNa
|
||||
transactionCancel context.CancelFunc
|
||||
startTextTransaction bool
|
||||
)
|
||||
if implicitTextTransaction {
|
||||
provider, ok := dbInst.(db.SessionExecerProvider)
|
||||
if !ok {
|
||||
return connection.QueryResult{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("当前数据源(%s)不支持 SQL 编辑器托管事务", transactionDBType),
|
||||
QueryID: queryID,
|
||||
}
|
||||
}
|
||||
sessionExecer, err = provider.OpenSessionExecer(ctx)
|
||||
if err != nil {
|
||||
logger.Error(err, "DBQueryMultiTransactional 打开隐式事务会话失败:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
|
||||
return connection.QueryResult{Success: false, Message: err.Error(), QueryID: queryID}
|
||||
}
|
||||
} else if provider, ok := dbInst.(db.TransactionExecerProvider); ok {
|
||||
if provider, ok := dbInst.(db.TransactionExecerProvider); ok {
|
||||
// database/sql rolls back a BeginTx transaction when its context is cancelled.
|
||||
// SQL editor transactions must outlive the execution RPC and be ended only by
|
||||
// explicit commit, rollback, or shutdown cleanup.
|
||||
@@ -96,6 +82,20 @@ func (a *App) DBQueryMultiTransactional(config connection.ConnectionConfig, dbNa
|
||||
}
|
||||
sessionExecer = transactionExecer
|
||||
transactor = transactionExecer
|
||||
} else if implicitTextTransaction {
|
||||
provider, ok := dbInst.(db.SessionExecerProvider)
|
||||
if !ok {
|
||||
return connection.QueryResult{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("当前数据源(%s)不支持 SQL 编辑器托管事务", transactionDBType),
|
||||
QueryID: queryID,
|
||||
}
|
||||
}
|
||||
sessionExecer, err = provider.OpenSessionExecer(ctx)
|
||||
if err != nil {
|
||||
logger.Error(err, "DBQueryMultiTransactional 打开隐式事务会话失败:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
|
||||
return connection.QueryResult{Success: false, Message: err.Error(), QueryID: queryID}
|
||||
}
|
||||
} else {
|
||||
if !hasTextTransaction {
|
||||
return connection.QueryResult{
|
||||
|
||||
Reference in New Issue
Block a user