mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-27 00:42:03 +08:00
🐛 fix(query-editor): 补齐SQL Server冗余结果集过滤
- 为 SQL Server 原生多结果集缺失 statementIndex 的场景补齐结果归属 - 修复单条 SELECT 查询尾随 affectedRows 结果被误渲染为额外结果页签的问题 - 补充前后端回归测试并保持存储过程多结果集展示不变
This commit is contained in:
@@ -850,11 +850,10 @@ describe('QueryEditor external SQL save', () => {
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
statementIndex: 1,
|
||||
columns: ['dddwno', 'dddwlist'],
|
||||
rows: [{ dddwno: '001', dddwlist: 'demo' }],
|
||||
},
|
||||
{ statementIndex: 1, columns: ['affectedRows'], rows: [{ affectedRows: 846 }] },
|
||||
{ columns: ['affectedRows'], rows: [{ affectedRows: 846 }] },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -3479,10 +3479,20 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
const maxRows = Number(queryOptions?.maxRows) || 0;
|
||||
let anyTruncated = false;
|
||||
const statementResultCounts = new Map<number, number>();
|
||||
const resolveSourceStatementIndex = (rsData: any, idx: number): number => {
|
||||
const explicitStatementIndex = Number(rsData?.statementIndex || 0);
|
||||
if (explicitStatementIndex > 0) {
|
||||
return explicitStatementIndex;
|
||||
}
|
||||
if (normalizedDbType === 'sqlserver' && sourceStatements.length === 1) {
|
||||
return 1;
|
||||
}
|
||||
return idx + 1;
|
||||
};
|
||||
const sqlServerStatementsWithConcreteResults = new Set<number>();
|
||||
if (normalizedDbType === 'sqlserver') {
|
||||
resultSetDataArray.forEach((rsData, idx) => {
|
||||
const sourceStatementIndex = Number(rsData?.statementIndex || idx + 1);
|
||||
const sourceStatementIndex = resolveSourceStatementIndex(rsData, idx);
|
||||
const resultMessages = normalizeQueryResultMessages(rsData?.messages);
|
||||
if (hasConcreteQueryResultSetData(rsData, resultMessages)) {
|
||||
sqlServerStatementsWithConcreteResults.add(sourceStatementIndex);
|
||||
@@ -3495,7 +3505,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
|
||||
for (let idx = 0; idx < resultSetDataArray.length; idx++) {
|
||||
const rsData = resultSetDataArray[idx];
|
||||
const sourceStatementIndex = Number(rsData?.statementIndex || idx + 1);
|
||||
const sourceStatementIndex = resolveSourceStatementIndex(rsData, idx);
|
||||
const plan = executablePlans[Math.max(0, sourceStatementIndex - 1)];
|
||||
const originalSql = plan?.originalSql || '';
|
||||
const executedSql = plan?.executedSql || originalSql;
|
||||
|
||||
@@ -1038,6 +1038,9 @@ func (a *App) DBQueryMulti(config connection.ConnectionConfig, dbName string, qu
|
||||
logger.Warnf("DBQueryMulti 原生多结果集返回空结果,将回退逐条执行:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
|
||||
results = nil
|
||||
}
|
||||
if useNativeMultiResult && results != nil {
|
||||
normalizeNativeResultStatementIndexes(runConfig.Type, statements, results)
|
||||
}
|
||||
|
||||
// 驱动支持多结果集,直接返回
|
||||
if results != nil {
|
||||
@@ -1276,6 +1279,33 @@ func (a *App) DBQueryMulti(config connection.ConnectionConfig, dbName string, qu
|
||||
return connection.QueryResult{Success: true, Data: resultSets, QueryID: queryID, Message: fallbackMsg}
|
||||
}
|
||||
|
||||
func normalizeNativeResultStatementIndexes(dbType string, statements []string, results []connection.ResultSetData) {
|
||||
if !isSQLServerDBType(dbType) || len(results) == 0 {
|
||||
return
|
||||
}
|
||||
hasExplicitStatementIndex := false
|
||||
for _, result := range results {
|
||||
if result.StatementIndex > 0 {
|
||||
hasExplicitStatementIndex = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasExplicitStatementIndex {
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(statements) <= 1:
|
||||
for idx := range results {
|
||||
results[idx].StatementIndex = 1
|
||||
}
|
||||
case len(results) == len(statements):
|
||||
for idx := range results {
|
||||
results[idx].StatementIndex = idx + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shouldUseNativeMultiResultBatch(dbType string, statements []string, allReadOnly bool) bool {
|
||||
if allReadOnly {
|
||||
return true
|
||||
|
||||
@@ -1525,6 +1525,51 @@ func TestDBQueryMultiKeepsAllResultSetsFromSingleSQLServerStatement(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBQueryMultiNormalizesSingleSQLServerSelectAffectedRowsStatementIndex(t *testing.T) {
|
||||
originalNewDatabaseFunc := newDatabaseFunc
|
||||
t.Cleanup(func() {
|
||||
newDatabaseFunc = originalNewDatabaseFunc
|
||||
})
|
||||
|
||||
query := "select * from c_dddw"
|
||||
fakeDB := &fakeBatchWriteDB{
|
||||
multiResult: map[string][]connection.ResultSetData{
|
||||
query: {
|
||||
{
|
||||
Rows: []map[string]interface{}{{"dddwno": "001", "dddwlist": "demo"}},
|
||||
Columns: []string{"dddwno", "dddwlist"},
|
||||
},
|
||||
{
|
||||
Rows: []map[string]interface{}{{"affectedRows": int64(846)}},
|
||||
Columns: []string{"affectedRows"},
|
||||
},
|
||||
},
|
||||
},
|
||||
queryErr: map[string]error{},
|
||||
}
|
||||
newDatabaseFunc = func(dbType string) (db.Database, error) {
|
||||
return fakeDB, nil
|
||||
}
|
||||
|
||||
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
|
||||
config := connection.ConnectionConfig{Type: "sqlserver", Host: "127.0.0.1", Port: 1433, User: "sa"}
|
||||
|
||||
result := app.DBQueryMulti(config, "hydee", query, "sqlserver-select-affectedrows-index-test")
|
||||
if !result.Success {
|
||||
t.Fatalf("expected DBQueryMulti success, got failure: %s", result.Message)
|
||||
}
|
||||
resultSets, ok := result.Data.([]connection.ResultSetData)
|
||||
if !ok {
|
||||
t.Fatalf("expected []connection.ResultSetData, got %T", result.Data)
|
||||
}
|
||||
if len(resultSets) != 2 {
|
||||
t.Fatalf("expected two result sets, got %#v", resultSets)
|
||||
}
|
||||
if resultSets[0].StatementIndex != 1 || resultSets[1].StatementIndex != 1 {
|
||||
t.Fatalf("expected select result and trailing affectedRows result to share statementIndex=1, got %#v", resultSets)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBQueryMultiTreatsBareSQLServerProcedureCallAsQueryFirst(t *testing.T) {
|
||||
originalNewDatabaseFunc := newDatabaseFunc
|
||||
t.Cleanup(func() {
|
||||
|
||||
Reference in New Issue
Block a user