// poll.go stores poll ballots. The poll DEFINITION lives in the forum
// post's body (a ```poll fenced block — a fossil peer renders readable
// markdown, which is the whole compatibility story); only the ballots
// are forge state, with the same idempotent shape as forum votes: the
// (repo, account, post) key IS the one-ballot rule.
package oltp
import (
"strings"
"time"
)
const pollSchema = `
CREATE TABLE IF NOT EXISTS poll_vote (
repo TEXT NOT NULL,
account TEXT NOT NULL,
post TEXT NOT NULL, -- the forum post carrying the poll block
choice INTEGER NOT NULL, -- option index in the block, 0-based
at TEXT NOT NULL,
PRIMARY KEY (repo, account, post)
);
CREATE INDEX IF NOT EXISTS poll_vote_by_post ON poll_vote (repo, post);`
// SetPollVote records (replacing) one account's ballot; changeable
// until the poll closes, which the API enforces from the parsed block.
func (d *DB) SetPollVote(repo, account, post string, choice int) error {
_, err := d.w.Exec(`INSERT INTO poll_vote (repo, account, post, choice, at)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (repo, account, post) DO UPDATE SET choice = excluded.choice, at = excluded.at`,
repo, account, post, choice, time.Now().UTC().Format(time.RFC3339))
return err
}
// PollBallot is one account's ballot on one version of a poll post.
type PollBallot struct {
Account string
Post string
Choice int
At string
}
// PollBallots returns every ballot cast on any of posts: the versions of
// one post, which the API tallies as one poll.
func (d *DB) PollBallots(repo string, posts []string) ([]PollBallot, error) {
if len(posts) == 0 {
return nil, nil
}
args := []any{repo}
for _, p := range posts {
args = append(args, p)
}
rows, err := d.db.Query(`SELECT account, post, choice, at FROM poll_vote
WHERE repo = ? AND post IN (?`+strings.Repeat(", ?", len(posts)-1)+`)`, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var out []PollBallot
for rows.Next() {
var b PollBallot
if err := rows.Scan(&b.Account, &b.Post, &b.Choice, &b.At); err != nil {
return nil, err
}
out = append(out, b)
}
return out, rows.Err()
}