🐛 fix(db): 修复金仓读查询结果空页

- 调整 PG 类数据库只读查询路径,优先使用普通 Query 返回真实结果集
- 避免金仓原生多结果集返回空行列时吞掉可展示数据
- 补充金仓读查询与原生空结果回退测试覆盖
This commit is contained in:
Syngnat
2026-06-29 15:17:47 +08:00
parent c940e930bc
commit 4798d3e8ec
2 changed files with 76 additions and 3 deletions

View File

@@ -1225,6 +1225,7 @@ func (a *App) DBQueryMulti(config connection.ConnectionConfig, dbName string, qu
isReadStmt := isReadOnlySQLQuery(runConfig.Type, stmt)
tryQueryStmtFirst := shouldTryQueryResultFirst(runConfig.Type, stmt)
if isReadStmt || tryQueryStmtFirst {
preferPlainReadQuery := isReadStmt && shouldPreferPlainReadQueryResult(runConfig.Type)
var (
data []map[string]interface{}
columns []string
@@ -1248,7 +1249,9 @@ func (a *App) DBQueryMulti(config connection.ConnectionConfig, dbName string, qu
}
return err
}
if sessionMultiQueryMessageTarget != nil {
if preferPlainReadQuery {
err = runStatementQuery()
} else if sessionMultiQueryMessageTarget != nil {
statementResults, messages, err = sessionMultiQueryMessageTarget.QueryMultiContextWithMessages(ctx, stmt)
usedMultiResult = true
} else if sessionMultiQueryTarget != nil {
@@ -1394,7 +1397,7 @@ func nativeReadOnlyResultsMissingTabularPayload(allReadOnly bool, results []conn
func shouldUseNativeMultiResultBatch(dbType string, statements []string, allReadOnly bool) bool {
if allReadOnly {
return true
return !shouldPreferPlainReadQueryResult(dbType)
}
if !strings.EqualFold(strings.TrimSpace(dbType), "sqlserver") {
return false
@@ -1412,6 +1415,15 @@ func shouldUseNativeMultiResultBatch(dbType string, statements []string, allRead
return true
}
func shouldPreferPlainReadQueryResult(dbType string) bool {
switch resolveDDLDBType(connection.ConnectionConfig{Type: dbType}) {
case "postgres", "kingbase", "highgo", "vastbase", "opengauss", "gaussdb":
return true
default:
return false
}
}
func shouldTryQueryResultFirst(dbType string, query string) bool {
isSQLServer := isSQLServerDBType(dbType)
if sqlWriteStatementReturnsRows(dbType, query) {

View File

@@ -1787,7 +1787,7 @@ func TestDBQueryMultiFallsBackToPlainQueryWhenSequentialMultiStillReturnsBlankRe
}
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
config := connection.ConnectionConfig{Type: "kingbase", Host: "127.0.0.1", Port: 54321, User: "system"}
config := connection.ConnectionConfig{Type: "mysql", Host: "127.0.0.1", Port: 3306, User: "root"}
result := app.DBQueryMulti(config, "ldf_server_dbs_dev", query, "sequential-blank-native-read-fallback-test")
if !result.Success {
@@ -1817,6 +1817,67 @@ func TestDBQueryMultiFallsBackToPlainQueryWhenSequentialMultiStillReturnsBlankRe
}
}
func TestDBQueryMultiPrefersPlainQueryForKingbaseReadResults(t *testing.T) {
originalNewDatabaseFunc := newDatabaseFunc
t.Cleanup(func() {
newDatabaseFunc = originalNewDatabaseFunc
})
query := "SELECT * FROM ldf_server.mes_work_order"
nativeEmptyRowsResult := []connection.ResultSetData{{
Rows: []map[string]interface{}{},
Columns: []string{"id", "work_order"},
}}
baseDB := &fakeBatchWriteDB{
queryMap: map[string][]map[string]interface{}{
query: {
{"id": 1001, "work_order": "MO-20260629"},
},
},
fieldMap: map[string][]string{
query: {"id", "work_order"},
},
multiResult: map[string][]connection.ResultSetData{
query: nativeEmptyRowsResult,
},
queryErr: map[string]error{},
}
fakeDB := &fakeNativeMultiResultDB{fakeBatchWriteDB: baseDB}
newDatabaseFunc = func(dbType string) (db.Database, error) {
return fakeDB, nil
}
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
config := connection.ConnectionConfig{Type: "kingbase", Host: "127.0.0.1", Port: 54321, User: "system"}
result := app.DBQueryMulti(config, "ldf_server_dbs_dev", query, "kingbase-plain-query-result-test")
if !result.Success {
t.Fatalf("expected DBQueryMulti success, got failure: %s", result.Message)
}
if fakeDB.multiCalls != 0 {
t.Fatalf("expected kingbase read query to skip top-level native multi-result path, got %d calls", fakeDB.multiCalls)
}
if baseDB.session == nil {
t.Fatal("expected DBQueryMulti to open a pinned session for kingbase read query")
}
if baseDB.session.queryCalls != 1 {
t.Fatalf("expected kingbase read query to use plain session query once, got %d calls", baseDB.session.queryCalls)
}
resultSets, ok := result.Data.([]connection.ResultSetData)
if !ok {
t.Fatalf("expected []connection.ResultSetData, got %T", result.Data)
}
if len(resultSets) != 1 {
t.Fatalf("expected one result set, got %#v", resultSets)
}
if !reflect.DeepEqual(resultSets[0].Columns, []string{"id", "work_order"}) {
t.Fatalf("expected plain query columns, got %#v", resultSets[0].Columns)
}
if got := resultSets[0].Rows[0]["work_order"]; got != "MO-20260629" {
t.Fatalf("expected plain query SELECT result work_order=MO-20260629, got %#v", got)
}
}
func TestDBQueryMultiUsesPinnedSessionForSequentialFallback(t *testing.T) {
originalNewDatabaseFunc := newDatabaseFunc
t.Cleanup(func() {