From 8d82408bb72c1cd3beab2051b83290ed735e6ae8 Mon Sep 17 00:00:00 2001 From: weichen Date: Mon, 5 Oct 2026 03:18:10 +0000 Subject: [PATCH] Bind archived filter as bool in ListTasks Why: GET /api/conversations on the default unarchived view appended AND archived=0. PostgreSQL stores conversations.archived as BOOLEAN and rejects boolean = integer (42883), so listTasks returned 500. The previous dialect fix bound parameters and scans but left this literal. Evidence: before the change, TestConversationFlagsAndTaskList against pg1 failed with SQLSTATE 42883. After: AGENT_PG_TEST_DSN set to pg1, go test ./internal/store -count=1 passed; go vet ./... && go test ./... passed on SQLite. Impact: the default task list works on the postgres tier. SQLite still binds the bool as 0/1. openTestStore accepts URL and keyword DSNs and keeps public on search_path so an existing pg_trgm extension stays visible. Refs: Agent-System-Dev feature/store-pg-archived-literal Co-authored-by: Cursor --- internal/store/projects.go | 6 ++++-- internal/store/store_test.go | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/internal/store/projects.go b/internal/store/projects.go index 4ec6511..fc529d5 100644 --- a/internal/store/projects.go +++ b/internal/store/projects.go @@ -158,11 +158,13 @@ func (s *Store) ListConversations(owner, projectID string) ([]core.Conversation, // ListTasks 跨项目任务列表(方案 A 侧栏数据源):默认仅未归档;includeArchived 含归档。 func (s *Store) ListTasks(owner string, includeArchived bool) ([]core.Conversation, error) { q := `SELECT id,project_id,title,profile_id,pinned,archived,allow_cross_read,cross_private,permission_mode,session_note,created_at,updated_at FROM conversations WHERE owner=?` + args := []any{owner} if !includeArchived { - q += ` AND archived=0` + q += ` AND archived=?` + args = append(args, false) } q += ` ORDER BY pinned DESC, updated_at DESC` - rows, err := s.db.Query(s.q(q), owner) + rows, err := s.db.Query(s.q(q), args...) if err != nil { return nil, err } diff --git a/internal/store/store_test.go b/internal/store/store_test.go index a8720cb..f2c77d5 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -39,7 +39,7 @@ func openTestStore(t *testing.T) *Store { admin.Exec("DROP SCHEMA IF EXISTS " + schema + " CASCADE") admin.Close() }) - st, err := OpenPostgres(dsn+"&search_path="+schema, filepath.Join(t.TempDir(), "artifacts")) + st, err := OpenPostgres(withTestSearchPath(dsn, schema), filepath.Join(t.TempDir(), "artifacts")) if err != nil { t.Fatal(err) } @@ -47,6 +47,21 @@ func openTestStore(t *testing.T) *Store { return st } +// withTestSearchPath 把用例 schema 放到 search_path 最前,并保留 public, +// 以便看到已安装在 public 的 pg_trgm。svc.sh 给出的是无查询串的 URL; +// 键值对 DSN(不含 ://)用空格追加。 +func withTestSearchPath(dsn, schema string) string { + path := schema + ",public" + if !strings.Contains(dsn, "://") { + return dsn + " search_path=" + path + } + sep := "?" + if strings.Contains(dsn, "?") { + sep = "&" + } + return dsn + sep + "search_path=" + path +} + func isSQLite(st *Store) bool { return st.Dialect() == "sqlite" } // 迁移验收:全量应用 → 状态 1..4 → 回滚到 1(老库形态)→ 再次前进(既有部署升级路径)。 -- 2.54.0