Merge pull request 'Bind booleans as bool for the postgres dialect in auth and conversations' (#64) from feature/store-pg-bool-dialect into main
ci / vet-and-test (push) Successful in 39s
ci / vet-and-test (push) Successful in 39s
This commit was merged in pull request #64.
This commit is contained in:
+10
-13
@@ -226,15 +226,17 @@ func (s *Store) CreateAccount(owner, name, passwordHash string, isAdmin bool) er
|
||||
if err := s.EnsureUser(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := s.db.Exec(s.q(`UPDATE users SET name=?, password_hash=?, password_set_at=?, must_change_password=1, is_admin=? WHERE owner=?`),
|
||||
name, passwordHash, nowISO(), boolInt(isAdmin), owner)
|
||||
// 布尔列一律绑 Go bool 参数、不写 1/0 字面量:SQLite 两侧都容忍,
|
||||
// PostgreSQL 对 boolean 列收到 integer 会 42804(2026-10-05 user create 半失败根因)。
|
||||
_, err := s.db.Exec(s.q(`UPDATE users SET name=?, password_hash=?, password_set_at=?, must_change_password=?, is_admin=? WHERE owner=?`),
|
||||
name, passwordHash, nowISO(), true, isAdmin, owner)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetUserPassword 设置/重置密码:must_change=1(首登强制改密)。
|
||||
func (s *Store) SetUserPassword(owner, passwordHash string) error {
|
||||
res, err := s.db.Exec(s.q(`UPDATE users SET password_hash=?, password_set_at=?, must_change_password=1 WHERE owner=?`),
|
||||
passwordHash, nowISO(), owner)
|
||||
res, err := s.db.Exec(s.q(`UPDATE users SET password_hash=?, password_set_at=?, must_change_password=? WHERE owner=?`),
|
||||
passwordHash, nowISO(), true, owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -246,7 +248,7 @@ func (s *Store) SetUserPassword(owner, passwordHash string) error {
|
||||
|
||||
// ClearMustChangePassword 改密完成后清标志(改密者会话保留,其余吊销由调用方处理)。
|
||||
func (s *Store) ClearMustChangePassword(owner string) error {
|
||||
_, err := s.db.Exec(s.q(`UPDATE users SET must_change_password=0 WHERE owner=?`), owner)
|
||||
_, err := s.db.Exec(s.q(`UPDATE users SET must_change_password=? WHERE owner=?`), false, owner)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -263,17 +265,15 @@ type UserAuthState struct {
|
||||
func (s *Store) GetUserAuthState(owner string) (UserAuthState, error) {
|
||||
var st UserAuthState
|
||||
var hash, ca string
|
||||
var mustChange, isAdmin int
|
||||
// 直接扫进 bool:PG 返回原生 boolean;SQLite 整数 0/1 经 driver.Bool 转换同样成立。
|
||||
err := s.db.QueryRow(s.q(`SELECT owner,COALESCE(name,''),password_hash,must_change_password,is_admin,created_at FROM users WHERE owner=?`), owner).
|
||||
Scan(&st.Owner, &st.Name, &hash, &mustChange, &isAdmin, &ca)
|
||||
Scan(&st.Owner, &st.Name, &hash, &st.MustChangePassword, &st.IsAdmin, &ca)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return st, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return st, err
|
||||
}
|
||||
st.MustChangePassword = mustChange == 1
|
||||
st.IsAdmin = isAdmin == 1
|
||||
st.HasPassword = hash != ""
|
||||
return st, nil
|
||||
}
|
||||
@@ -350,12 +350,9 @@ func (s *Store) ListUsersAdmin() ([]AdminUser, error) {
|
||||
for rows.Next() {
|
||||
var u AdminUser
|
||||
var hash, psa, ca string
|
||||
var mustChange, isAdmin int
|
||||
if err := rows.Scan(&u.Owner, &u.Name, &hash, &mustChange, &isAdmin, &psa, &ca); err != nil {
|
||||
if err := rows.Scan(&u.Owner, &u.Name, &hash, &u.MustChangePassword, &u.IsAdmin, &psa, &ca); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.MustChangePassword = mustChange == 1
|
||||
u.IsAdmin = isAdmin == 1
|
||||
u.HasPassword = hash != ""
|
||||
if psa != "" {
|
||||
t, _ := time.Parse(time.RFC3339Nano, psa)
|
||||
|
||||
@@ -181,14 +181,10 @@ func (s *Store) ListTasks(owner string, includeArchived bool) ([]core.Conversati
|
||||
|
||||
func scanConversation(rows interface{ Scan(dest ...any) error }, c *core.Conversation) error {
|
||||
var ca, ua string
|
||||
var pinned, archived, allowCrossRead, crossPrivate int
|
||||
if err := rows.Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &pinned, &archived, &allowCrossRead, &crossPrivate, &c.PermissionMode, &c.SessionNote, &ca, &ua); err != nil {
|
||||
// 直接扫 bool:PG 原生 boolean;SQLite 整数 0/1 经 driver.Bool 转换同样成立。
|
||||
if err := rows.Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &c.Pinned, &c.Archived, &c.AllowCrossRead, &c.CrossPrivate, &c.PermissionMode, &c.SessionNote, &ca, &ua); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Pinned = pinned == 1
|
||||
c.Archived = archived == 1
|
||||
c.AllowCrossRead = allowCrossRead == 1
|
||||
c.CrossPrivate = crossPrivate == 1
|
||||
c.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
||||
c.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
||||
return nil
|
||||
@@ -197,9 +193,8 @@ func scanConversation(rows interface{ Scan(dest ...any) error }, c *core.Convers
|
||||
func (s *Store) GetConversation(owner, id string) (core.Conversation, error) {
|
||||
var c core.Conversation
|
||||
var ca, ua string
|
||||
var pinned, archived, allowCrossRead, crossPrivate int
|
||||
err := s.db.QueryRow(s.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 id=? AND owner=?`), id, owner).
|
||||
Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &pinned, &archived, &allowCrossRead, &crossPrivate, &c.PermissionMode, &c.SessionNote, &ca, &ua)
|
||||
Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &c.Pinned, &c.Archived, &c.AllowCrossRead, &c.CrossPrivate, &c.PermissionMode, &c.SessionNote, &ca, &ua)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return c, ErrNotFound
|
||||
}
|
||||
@@ -207,10 +202,6 @@ func (s *Store) GetConversation(owner, id string) (core.Conversation, error) {
|
||||
return c, err
|
||||
}
|
||||
c.Owner = owner
|
||||
c.Pinned = pinned == 1
|
||||
c.Archived = archived == 1
|
||||
c.AllowCrossRead = allowCrossRead == 1
|
||||
c.CrossPrivate = crossPrivate == 1
|
||||
c.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
||||
c.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
||||
return c, nil
|
||||
@@ -242,24 +233,25 @@ func (s *Store) UpdateConversation(owner, id, title, profileID string) error {
|
||||
}
|
||||
|
||||
// SetConversationFlags 更新会话标记(nil = 该字段不动;方案 A R1 + 跨会话读取提案)。
|
||||
// conversations 的四个标记列在 PG 档是 BOOLEAN——绑 Go bool,不绑 boolInt 整数。
|
||||
func (s *Store) SetConversationFlags(owner, id string, pinned, archived, allowCrossRead, crossPrivate *bool) error {
|
||||
var sets []string
|
||||
var args []any
|
||||
if pinned != nil {
|
||||
sets = append(sets, "pinned=?")
|
||||
args = append(args, boolInt(*pinned))
|
||||
args = append(args, *pinned)
|
||||
}
|
||||
if archived != nil {
|
||||
sets = append(sets, "archived=?")
|
||||
args = append(args, boolInt(*archived))
|
||||
args = append(args, *archived)
|
||||
}
|
||||
if allowCrossRead != nil {
|
||||
sets = append(sets, "allow_cross_read=?")
|
||||
args = append(args, boolInt(*allowCrossRead))
|
||||
args = append(args, *allowCrossRead)
|
||||
}
|
||||
if crossPrivate != nil {
|
||||
sets = append(sets, "cross_private=?")
|
||||
args = append(args, boolInt(*crossPrivate))
|
||||
args = append(args, *crossPrivate)
|
||||
}
|
||||
if len(sets) == 0 {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user