// chatref.go records that a chat message referenced an artifact
// (RFC 0011 §14). Chat is a window, not the DAG: these rows live and
// die with the messages they describe, which is why an artifact page
// shows their count as a window-bounded "chat" row beside the durable
// MentionRefs the index derives — the two look different because they
// ARE different, and a reader who cannot tell them apart will read a
// vanished count as data loss.
package oltp
const chatRefSchema = `
CREATE TABLE IF NOT EXISTS chat_ref (
repo TEXT NOT NULL,
target TEXT NOT NULL, -- artifact hash or thread root
seq INTEGER NOT NULL,
at TEXT NOT NULL,
PRIMARY KEY (repo, target, seq)
);
CREATE INDEX IF NOT EXISTS chat_ref_seq ON chat_ref (repo, seq);`
// RecordChatRef notes one reference, idempotently on (repo, target,
// seq): the same message replayed says the same thing, and a replay
// must never double a count.
func (d *DB) RecordChatRef(repo, target string, seq uint64, at string) error {
if repo == "" || target == "" || seq == 0 {
return nil
}
_, err := d.db.Exec(`INSERT OR IGNORE INTO chat_ref (repo, target, seq, at)
VALUES (?, ?, ?, ?)`, repo, target, seq, at)
return err
}
// PruneChatRefs drops rows for messages the channel window no longer
// holds. It runs on the post path rather than on a timer: the window
// only ever moves when something is posted, so a sweep with no post to
// trigger it would have nothing to do.
func (d *DB) PruneChatRefs(repo string, firstSeq uint64) error {
if repo == "" || firstSeq == 0 {
return nil
}
_, err := d.db.Exec(`DELETE FROM chat_ref WHERE repo = ? AND seq < ?`, repo, firstSeq)
return err
}
// ChatRefs answers how often a target has been named in chat and when
// last — count, the latest message's seq (the permalink to land on),
// and its time. A target nobody has mentioned answers zero, not an
// error: "no chat about this" is an answer.
func (d *DB) ChatRefs(repo, target string) (count int, lastSeq uint64, lastAt string, err error) {
row := d.db.QueryRow(`SELECT count(*), COALESCE(max(seq), 0),
COALESCE((SELECT at FROM chat_ref i WHERE i.repo = o.repo AND i.target = o.target
ORDER BY i.seq DESC LIMIT 1), '')
FROM chat_ref o WHERE repo = ? AND target = ? GROUP BY repo, target`, repo, target)
err = row.Scan(&count, &lastSeq, &lastAt)
if err != nil && err.Error() == "sql: no rows in result set" {
return 0, 0, "", nil
}
return count, lastSeq, lastAt, err
}