diff --git a/frontend/src/utils/sqlErrorSemantics.test.ts b/frontend/src/utils/sqlErrorSemantics.test.ts index 819229b4..02d1b3f3 100644 --- a/frontend/src/utils/sqlErrorSemantics.test.ts +++ b/frontend/src/utils/sqlErrorSemantics.test.ts @@ -37,6 +37,13 @@ describe('formatSqlExecutionError', () => { expect(formatted).toContain('Raw error: driver returned unexpected status 123'); }); + it('recognizes driver bad connection during SQL execution as timeout semantics', () => { + const formatted = formatSqlExecutionError('第 1 条语句执行失败:driver: bad connection'); + + expect(formatted).toContain('Semantic meaning: Query timed out or was canceled'); + expect(formatted).toContain('Raw error: 第 1 条语句执行失败:driver: bad connection'); + }); + it('recognizes localized connection-timeout wrappers as timeout semantics', () => { const translate = (key: string, params?: Record) => { if (key === 'query_editor.sql_error.wrapper.semantic_line') { diff --git a/frontend/src/utils/sqlErrorSemantics.ts b/frontend/src/utils/sqlErrorSemantics.ts index 987ff53d..f6f5f8c2 100644 --- a/frontend/src/utils/sqlErrorSemantics.ts +++ b/frontend/src/utils/sqlErrorSemantics.ts @@ -135,6 +135,7 @@ const SQL_ERROR_RULES: SqlErrorSemanticRule[] = [ /context cancelled/i, /timeout/i, /timed out/i, + /driver:\s*bad connection/i, ], }, { diff --git a/internal/app/db_context.go b/internal/app/db_context.go index e47f3fb6..98323571 100644 --- a/internal/app/db_context.go +++ b/internal/app/db_context.go @@ -9,6 +9,8 @@ import ( "GoNavi-Wails/internal/db" ) +const defaultOceanBaseOracleQueryTimeoutSeconds = 120 + func normalizeRunConfig(config connection.ConnectionConfig, dbName string) connection.ConnectionConfig { runConfig := config name := strings.TrimSpace(dbName) @@ -54,6 +56,10 @@ func applyOceanBaseOracleCurrentSchemaInit(config connection.ConnectionConfig, s if normalizedSchema == "" { return config } + if config.Timeout <= 0 { + // OceanBase Oracle 查询经常需要经过 OBProxy/Oracle driver 的读等待;默认 30s 容易把慢查询误报成 driver: bad connection。 + config.Timeout = defaultOceanBaseOracleQueryTimeoutSeconds + } values, err := url.ParseQuery(strings.TrimSpace(config.ConnectionParams)) if err != nil { return config diff --git a/internal/app/db_context_oceanbase_oracle_test.go b/internal/app/db_context_oceanbase_oracle_test.go index bd831bac..3f3cebc8 100644 --- a/internal/app/db_context_oceanbase_oracle_test.go +++ b/internal/app/db_context_oceanbase_oracle_test.go @@ -20,6 +20,9 @@ func TestNormalizeRunConfig_OceanBaseOracleAddsCurrentSchemaInit(t *testing.T) { if runConfig.Database != "OBORCL" { t.Fatalf("expected OceanBase Oracle service name to stay OBORCL, got %q", runConfig.Database) } + if runConfig.Timeout != defaultOceanBaseOracleQueryTimeoutSeconds { + t.Fatalf("expected OceanBase Oracle default query timeout %d, got %d", defaultOceanBaseOracleQueryTimeoutSeconds, runConfig.Timeout) + } values, err := url.ParseQuery(runConfig.ConnectionParams) if err != nil { t.Fatalf("unexpected connection params parse error: %v", err) @@ -30,17 +33,21 @@ func TestNormalizeRunConfig_OceanBaseOracleAddsCurrentSchemaInit(t *testing.T) { } } -func TestNormalizeRunConfig_OceanBaseOraclePreservesExistingInit(t *testing.T) { +func TestNormalizeRunConfig_OceanBaseOraclePreservesExplicitTimeoutAndExistingInit(t *testing.T) { t.Parallel() config := connection.ConnectionConfig{ Type: "oceanbase", Database: "OBORCL", OceanBaseProtocol: "oracle", + Timeout: 45, ConnectionParams: "init=ALTER+SESSION+SET+NLS_DATE_FORMAT%3D%27YYYY-MM-DD%27&timeout=10s", } runConfig := normalizeRunConfig(config, "sbdev") + if runConfig.Timeout != 45 { + t.Fatalf("expected explicit timeout to be preserved, got %d", runConfig.Timeout) + } values, err := url.ParseQuery(runConfig.ConnectionParams) if err != nil { t.Fatalf("unexpected connection params parse error: %v", err)