// landingnumber.go is the forge's landing numbering (d60c0c76): one
// sequence per repository, apart from the tickets', written !N. A
// landing is a forum thread any clone can open, so the forge numbers
// it when it first sees it, as it does a pushed ticket. The thread
// root's hash stays the identity; the number is what people say.
package oltp
import (
"database/sql"
"errors"
)
const landingNumberSchema = `
CREATE TABLE IF NOT EXISTS landing_number (
repo TEXT NOT NULL,
number INTEGER NOT NULL,
landing TEXT NOT NULL,
PRIMARY KEY (repo, number),
UNIQUE (repo, landing)
);`
// LandingNumbersAssign numbers, in the order given, every landing in ids
// that has no number yet, continuing the repository's sequence. Already
// numbered landings keep theirs, so calling it again is harmless, and
// the write transaction is immediate, so two callers at once cannot
// both read the same highest number.
func (d *DB) LandingNumbersAssign(repo string, ids []string) error {
if len(ids) == 0 {
return nil
}
tx, err := d.w.Begin()
if err != nil {
return err
}
defer tx.Rollback()
var max int
if err := tx.QueryRow(`SELECT COALESCE(MAX(number), 0) FROM landing_number WHERE repo = ?`, repo).Scan(&max); err != nil {
return err
}
for _, id := range ids {
var have int
err := tx.QueryRow(`SELECT number FROM landing_number WHERE repo = ? AND landing = ?`, repo, id).Scan(&have)
if err == nil {
continue
}
if !errors.Is(err, sql.ErrNoRows) {
return err
}
max++
if _, err := tx.Exec(`INSERT INTO landing_number (repo, number, landing) VALUES (?, ?, ?)`, repo, max, id); err != nil {
return err
}
}
return tx.Commit()
}
// LandingNumbers is every numbered landing of a repository, id → number.
func (d *DB) LandingNumbers(repo string) (map[string]int, error) {
rows, err := d.db.Query(`SELECT landing, number FROM landing_number WHERE repo = ?`, repo)
if err != nil {
return nil, err
}
defer rows.Close()
out := map[string]int{}
for rows.Next() {
var id string
var n int
if err := rows.Scan(&id, &n); err != nil {
return nil, err
}
out[id] = n
}
return out, rows.Err()
}
// LandingNumbered is the landing a repository's number names.
func (d *DB) LandingNumbered(repo string, n int) (string, bool, error) {
var id string
err := d.db.QueryRow(`SELECT landing FROM landing_number WHERE repo = ? AND number = ?`, repo, n).Scan(&id)
if errors.Is(err, sql.ErrNoRows) {
return "", false, nil
}
return id, err == nil, err
}