coverage~bigbes/sr-ht-dolt3523280cdb/store.go

Coverage
33.3% 1/3 statements
Δ
+0.0
Blob
5082e24
Uncovered L58-L60L65-L67
1 // Package db is the PostgreSQL persistence layer for dolt.sr.ht. It maps the
2 // repository, access (ACL) and dolt_key tables to core value types with plain
3 // database/sql and $n placeholders (no ORM), and enforces the listing/effective
4 // -access rules that the pure core.Allowed matrix cannot express in SQL.
5 //
6 // Design: a Store wraps a Querier — an interface satisfied by *sql.DB, *sql.Tx
7 // and *sql.Conn alike. This gives us two things the task requires at once:
8 //
9 // - Context-first, middleware-compatible signatures. Production callers build
10 // a Store from the connection that core-go's database middleware injects
11 // into the request context (see FromContext, which reads the same *sql.DB
12 // that database.Middleware installed). Every method takes ctx first and
13 // threads it into the query for cancellation.
14 //
15 // - Trivial test injection. Tests call NewStore(db) with a plain *sql.DB, no
16 // HTTP context required.
17 //
18 // Because the wrapped value is an interface, a Store can be re-bound to an
19 // open transaction with WithTx(tx): the multi-statement create-repo flow (INSERT
20 // the row, then write the on-disk NBS store, rolling back both on failure) runs
21 // every query on the caller's *sql.Tx while sharing the exact same method set.
22 package db
23
24 import (
25 "context"
26 "database/sql"
27 "errors"
28
29 "sourcecraft.dev/bigbes/sr-ht-core/database"
30 )
31
32 // Querier is the common subset of *sql.DB, *sql.Tx and *sql.Conn used by this
33 // package. Binding a Store to any of them keeps every method identical whether
34 // it runs standalone (autocommit) or inside a caller-managed transaction.
35 type Querier interface {
36 ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
37 QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
38 QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
39 }
40
41 // Store is the entry point for all queries in this package.
42 type Store struct {
43 q Querier
44 }
45
46 // NewStore builds a Store over a database handle (or any Querier). Tests pass a
47 // plain *sql.DB; production wiring passes the shared pool.
48 15 func NewStore(q Querier) *Store {
49 15 return &Store{q: q}
50 15 }
51
52 // FromContext builds a Store over the *sql.DB that core-go's database.Middleware
53 // installed in ctx. It panics (via database.DBForContext) if no database is
54 // present in the context — a programming error, never a runtime condition to
55 // recover from. We wrap the pooled *sql.DB rather than checking out a *sql.Conn
56 // (database.ForContext) so the Store has no connection to leak; the pool manages
57 // connection lifetime and ctx still bounds each query.
58 0 func FromContext(ctx context.Context) *Store {
59 0 return &Store{q: database.DBForContext(ctx)}
60 0 }
61
62 // WithTx returns a Store that runs every query on tx instead of the pool. Used
63 // by the repository create flow, which must coordinate the SQL insert with the
64 // on-disk store creation under one transaction.
65 0 func (s *Store) WithTx(tx *sql.Tx) *Store {
66 0 return &Store{q: tx}
67 0 }
68
69 // Typed errors returned by this package. Callers match them with errors.Is.
70 var (
71 // ErrNotFound is returned when a lookup, update or delete matched no row.
72 ErrNotFound = errors.New("db: not found")
73 // ErrNameTaken is returned by CreateRepo when the owner already has a
74 // repository with the requested name (uq_repo_owner_id_name or the
75 // equivalent repository_path_key violation).
76 ErrNameTaken = errors.New("db: repository name already taken")
77 // ErrKeyExists is returned by InsertKey when the key id (kid) is already
78 // registered (dolt_key.kid UNIQUE violation).
79 ErrKeyExists = errors.New("db: dolt key already registered")
80 )
81
82 // rowScanner is satisfied by both *sql.Row and *sql.Rows.
83 type rowScanner interface {
84 Scan(dest ...any) error
85 }