Artifact 1631ffd5a3715c9e0810cf8069416afb160f57865209b62865eb303407b4af6b:
- File
internal/oltp/subs.go
— part of check-in
[81e89bc4d8]
at
2026-08-22 16:50:43
on branch ticket-list
— gofmt: format three files carried in from trunk
subs.go was dirty because gofmt rewrites a pair of apostrophes in a comment into a typographic closing quote; reworded to say empty plainly so formatting is now a fixed point. (user: dmestas, size: 2354) [annotate] [blame] [check-ins using] [more...]
// subs.go stores forum subscriptions. The rule that matters: an // EXPLICIT unsubscribe outranks every auto-subscribe forever — "leave // me alone" must survive the subscriber's own later replies. package oltp const subsSchema = ` CREATE TABLE IF NOT EXISTS forum_sub ( repo TEXT NOT NULL, account TEXT NOT NULL, thread TEXT NOT NULL, -- thread root hash state TEXT NOT NULL, -- 'sub' | 'unsub' at TEXT NOT NULL, PRIMARY KEY (repo, account, thread) ); CREATE INDEX IF NOT EXISTS forum_sub_by_thread ON forum_sub (repo, thread, state);` // SetForumSub records an EXPLICIT choice, replacing whatever stood. func (d *DB) SetForumSub(repo, account, thread, state string) error { _, err := d.db.Exec(`INSERT INTO forum_sub (repo, account, thread, state, at) VALUES (?, ?, ?, ?, ?) ON CONFLICT (repo, account, thread) DO UPDATE SET state = excluded.state, at = excluded.at`, repo, account, thread, state, now()) return err } // AutoSubscribe subscribes an author to a thread they touched — unless // ANY explicit row exists. INSERT OR IGNORE is the whole mechanism: an // earlier unsubscribe is a row, so it is never overridden. func (d *DB) AutoSubscribe(repo, account, thread string) error { _, err := d.db.Exec(`INSERT OR IGNORE INTO forum_sub (repo, account, thread, state, at) VALUES (?, ?, ?, 'sub', ?)`, repo, account, thread, now()) return err } // ForumSubscribers lists the accounts subscribed to a thread. func (d *DB) ForumSubscribers(repo, thread string) ([]string, error) { rows, err := d.db.Query(`SELECT account FROM forum_sub WHERE repo = ? AND thread = ? AND state = 'sub' ORDER BY account`, repo, thread) if err != nil { return nil, err } defer rows.Close() var out []string for rows.Next() { var a string if err := rows.Scan(&a); err != nil { return nil, err } out = append(out, a) } return out, rows.Err() } // ForumSubState answers one account's standing: "sub", "unsub", or empty. // Double quotes deliberately: gofmt rewrites a pair of apostrophes in a // comment into a typographic closing quote, which reads as a typo. func (d *DB) ForumSubState(repo, account, thread string) string { var s string if account == "" { return "" } d.db.QueryRow(`SELECT state FROM forum_sub WHERE repo = ? AND account = ? AND thread = ?`, repo, account, thread).Scan(&s) return s }