From 2428fe6cc5f75d08cd7c534a888fe0c7a29b0619 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Sun, 19 Jul 2026 13:32:52 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(tdengine):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9F=A5=E8=AF=A2=E7=BB=93=E6=9E=9C=E4=BB=85=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=89=A7=E8=A1=8C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TDengine 只读查询跳过不支持的多结果集探测 - 保证可选驱动代理通过普通查询返回完整行列数据 - 补充应用层与驱动层回归测试 Fixes #610 --- internal/app/methods_db.go | 5 ++ ...methods_db_sqlite_agent_regression_test.go | 4 +- ...thods_db_tdengine_agent_regression_test.go | 64 +++++++++++++++++++ internal/db/tdengine_applychanges_test.go | 25 ++++++++ 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 internal/app/methods_db_tdengine_agent_regression_test.go diff --git a/internal/app/methods_db.go b/internal/app/methods_db.go index e247338e..c027f322 100644 --- a/internal/app/methods_db.go +++ b/internal/app/methods_db.go @@ -1700,6 +1700,11 @@ func shouldPreferPlainReadQueryResult(dbType string) bool { "gaussdb", "gauss_db", "gauss-db", "dameng", "dm", "dm8": return true + case "tdengine": + // TDengine only implements the plain query API. The optional driver-agent + // exposes a transport-level multi-result method, but reports it unsupported. + // Skipping that probe prevents a successful SELECT from becoming an empty result. + return true default: return false } diff --git a/internal/app/methods_db_sqlite_agent_regression_test.go b/internal/app/methods_db_sqlite_agent_regression_test.go index d531534f..13d098f9 100644 --- a/internal/app/methods_db_sqlite_agent_regression_test.go +++ b/internal/app/methods_db_sqlite_agent_regression_test.go @@ -99,7 +99,7 @@ func (s *fakeUnsupportedMultiResultSession) QueryMultiContextWithMessages(contex return nil, nil, nil } -func installFakeOptionalSQLiteDatabase(t *testing.T, fakeDB db.Database) { +func installFakeOptionalDriverDatabase(t *testing.T, fakeDB db.Database) { t.Helper() originalNewDatabaseFunc := newDatabaseFunc originalDriverRuntimeSupportStatusFunc := driverRuntimeSupportStatusFunc @@ -126,7 +126,7 @@ func TestDBQueryMultiSQLiteAgentStyleQueryReturnsRowsAndAuditCount(t *testing.T) queryErr: map[string]error{}, } fakeDB := &fakeUnsupportedMultiResultDB{fakeBatchWriteDB: baseDB} - installFakeOptionalSQLiteDatabase(t, fakeDB) + installFakeOptionalDriverDatabase(t, fakeDB) app := newSQLAuditTestApp(t) config := connection.ConnectionConfig{Type: "sqlite", Host: "/tmp/orders.sqlite"} diff --git a/internal/app/methods_db_tdengine_agent_regression_test.go b/internal/app/methods_db_tdengine_agent_regression_test.go new file mode 100644 index 00000000..678251a1 --- /dev/null +++ b/internal/app/methods_db_tdengine_agent_regression_test.go @@ -0,0 +1,64 @@ +package app + +import ( + "context" + "errors" + "reflect" + "testing" + + "GoNavi-Wails/internal/connection" + "GoNavi-Wails/internal/db" +) + +type fakeTDengineOptionalAgentDB struct { + *fakeUnsupportedMultiResultDB +} + +func (*fakeTDengineOptionalAgentDB) OpenSessionExecer(context.Context) (db.StatementExecer, error) { + return nil, errors.New("TDengine driver does not support pinned sessions") +} + +func TestDBQueryMultiTDengineAgentSelectUsesPlainQuery(t *testing.T) { + query := "SELECT ts, current FROM meters ORDER BY ts DESC LIMIT 1" + baseDB := &fakeBatchWriteDB{ + queryMap: map[string][]map[string]interface{}{ + query: {{"ts": "2026-07-19T00:00:00+08:00", "current": 10.2}}, + }, + fieldMap: map[string][]string{ + query: {"ts", "current"}, + }, + queryErr: map[string]error{}, + } + fakeDB := &fakeTDengineOptionalAgentDB{ + fakeUnsupportedMultiResultDB: &fakeUnsupportedMultiResultDB{fakeBatchWriteDB: baseDB}, + } + installFakeOptionalDriverDatabase(t, fakeDB) + + app := NewApp() + result := app.DBQueryMulti( + connection.ConnectionConfig{Type: "tdengine", Host: "127.0.0.1", Port: 6041}, + "power", + query, + "tdengine-agent-select", + ) + if !result.Success { + t.Fatalf("TDengine agent SELECT returned failure: %s", result.Message) + } + + resultSets, ok := result.Data.([]connection.ResultSetData) + if !ok || len(resultSets) != 1 { + t.Fatalf("TDengine agent SELECT result sets = %#v, want one result set", result.Data) + } + if !reflect.DeepEqual(resultSets[0].Columns, []string{"ts", "current"}) { + t.Fatalf("TDengine agent SELECT columns = %#v", resultSets[0].Columns) + } + if len(resultSets[0].Rows) != 1 || resultSets[0].Rows[0]["current"] != 10.2 { + t.Fatalf("TDengine agent SELECT rows = %#v, want one data row", resultSets[0].Rows) + } + if fakeDB.multiCalls != 0 { + t.Fatalf("TDengine SELECT must not probe unsupported multi-result API, calls=%d", fakeDB.multiCalls) + } + if baseDB.queryCalls != 1 { + t.Fatalf("TDengine SELECT should execute exactly once through plain Query, calls=%d", baseDB.queryCalls) + } +} diff --git a/internal/db/tdengine_applychanges_test.go b/internal/db/tdengine_applychanges_test.go index 4e77b7c9..f4c50333 100644 --- a/internal/db/tdengine_applychanges_test.go +++ b/internal/db/tdengine_applychanges_test.go @@ -161,6 +161,31 @@ func openTDengineRecordingDB(t *testing.T) (*sql.DB, *tdengineRecordingState) { return dbConn, state } +func TestTDengineQueryContextReturnsRowsAndColumns(t *testing.T) { + t.Parallel() + + dbConn, state := openTDengineRecordingDB(t) + td := &TDengineDB{conn: dbConn} + query := "SELECT ts, current FROM meters ORDER BY ts DESC LIMIT 1" + state.queryResults[query] = tdengineQueryResult{ + columns: []string{"ts", "current"}, + rows: [][]driver.Value{ + {"2026-07-19 00:00:00.000", 10.2}, + }, + } + + rows, columns, err := td.QueryContext(context.Background(), query) + if err != nil { + t.Fatalf("TDengine QueryContext returned error: %v", err) + } + if !reflect.DeepEqual(columns, []string{"ts", "current"}) { + t.Fatalf("TDengine QueryContext columns = %#v", columns) + } + if len(rows) != 1 || rows[0]["ts"] != "2026-07-19 00:00:00.000" || rows[0]["current"] != 10.2 { + t.Fatalf("TDengine QueryContext rows = %#v, want one data row", rows) + } +} + func TestTDengineApplyChanges_InsertsIntoQualifiedTable(t *testing.T) { t.Parallel()