// mention.go records that a handle was mentioned, before the trigger
// is raised (RFC 0011 §4). The row is the operational fact
// `chat.mentioned` is backed by — a mention that was never recorded is
// a mention notify was never told about, so this write happens on the
// path that decides to notify, not as an afterthought.
package oltp
const mentionSchema = `
CREATE TABLE IF NOT EXISTS mention (
repo TEXT NOT NULL,
ref TEXT NOT NULL, -- "chat:<seq>" | "forum:<hash>"
who TEXT NOT NULL,
by TEXT NOT NULL,
at TEXT NOT NULL,
PRIMARY KEY (repo, ref, who)
);`
// RecordMention records one mention, idempotently: the same (repo,
// ref, who) replayed is a no-op, exactly as the address-based
// idempotency keys in RFC 0011 §4 intend — a retried or re-ingested
// trigger must never appear as a second row.
func (d *DB) RecordMention(repo, ref, who, by string) error {
if repo == "" || ref == "" || who == "" {
return nil
}
_, err := d.w.Exec(`INSERT OR IGNORE INTO mention (repo, ref, who, by, at)
VALUES (?, ?, ?, ?, ?)`, repo, ref, who, by, now())
return err
}
// Mentions returns who was mentioned at ref, for tests and for any
// future reader that needs the raw rows rather than a notify trigger.
func (d *DB) Mentions(repo, ref string) ([]string, error) {
rows, err := d.db.Query(`SELECT who FROM mention WHERE repo = ? AND ref = ? ORDER BY who`, repo, ref)
if err != nil {
return nil, err
}
defer rows.Close()
var out []string
for rows.Next() {
var who string
if err := rows.Scan(&who); err != nil {
return nil, err
}
out = append(out, who)
}
return out, rows.Err()
}