Skip to content
quarry Artifact [cd9d907324]
Login

Artifact [cd9d907324]

Artifact cd9d9073244b1693468474f981cb2b931081ea0deb8b33ad86ecdfcba8f1690d:


// companion.go is RFC 0015's binding: which separate store holds a
// repository's forum. Forge-local wiring, like a grant — wiping it
// loses the binding, never a fact the DAG holds, because the parent and
// the companion are each complete on their own.
package oltp

import (
	"database/sql"
	"time"
)

const companionSchema = `
CREATE TABLE IF NOT EXISTS repo_companion (
	repo    TEXT NOT NULL,  -- the parent, as in repo.name
	feature TEXT NOT NULL,  -- 'forum' is the only value RFC 0015 defines
	store   TEXT NOT NULL,  -- the companion, as in repo.name
	since   TEXT NOT NULL,
	PRIMARY KEY (repo, feature),
	UNIQUE (store)          -- a store is one repository's companion, or nobody's
);`

// Companion is one binding row.
type Companion struct {
	Repo, Feature, Store, Since string
}

// BindCompanion records that repo's feature lives in store. The two
// uniqueness rules are the table's: one companion per (repo, feature),
// and a store bound at most once. Refusing a store that is itself a
// parent is the op's job, since it needs the whole table to know.
func (d *DB) BindCompanion(repo, feature, store string) error {
	_, err := d.w.Exec(`INSERT INTO repo_companion (repo, feature, store, since) VALUES (?, ?, ?, ?)`,
		repo, feature, store, time.Now().UTC().Format(time.RFC3339))
	return err
}

// UnbindCompanion removes the binding; the store stays.
func (d *DB) UnbindCompanion(repo, feature string) error {
	_, err := d.w.Exec(`DELETE FROM repo_companion WHERE repo = ? AND feature = ?`, repo, feature)
	return err
}

// Companion answers which store holds repo's feature, if any.
func (d *DB) Companion(repo, feature string) (store string, ok bool, err error) {
	err = d.db.QueryRow(`SELECT store FROM repo_companion WHERE repo = ? AND feature = ?`, repo, feature).Scan(&store)
	if err == sql.ErrNoRows {
		return "", false, nil
	}
	if err != nil {
		return "", false, err
	}
	return store, true, nil
}

// CompanionParent answers whose companion a store is, if anyone's.
func (d *DB) CompanionParent(store string) (repo, feature string, ok bool, err error) {
	err = d.db.QueryRow(`SELECT repo, feature FROM repo_companion WHERE store = ?`, store).Scan(&repo, &feature)
	if err == sql.ErrNoRows {
		return "", "", false, nil
	}
	if err != nil {
		return "", "", false, err
	}
	return repo, feature, true, nil
}

// Companions lists every binding.
func (d *DB) Companions() ([]Companion, error) {
	rows, err := d.db.Query(`SELECT repo, feature, store, since FROM repo_companion ORDER BY repo, feature`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []Companion
	for rows.Next() {
		var c Companion
		if err := rows.Scan(&c.Repo, &c.Feature, &c.Store, &c.Since); err != nil {
			return nil, err
		}
		out = append(out, c)
	}
	return out, rows.Err()
}