Skip to content
quarry Artifact [fac1cd2c63]
Login

Artifact [fac1cd2c63]

Artifact fac1cd2c63a4af07855e59ce8d78844259a1a64d9fed8c87b77aed35f4ded466:


// 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
}