diff options
Diffstat (limited to 'internal/store/store.go')
| -rw-r--r-- | internal/store/store.go | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/internal/store/store.go b/internal/store/store.go index 472bb2a..c8e97ea 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -1,9 +1,15 @@ // Package store holds the data models and all SQL queries. +// +// Store talks to the control database (users, blog registry) and hands out a +// BlogStore per blog, which is bound to that blog's own database — nothing a +// BlogStore does can reach another blog's rows. package store import ( + "context" "errors" + "github.com/gramanas/blogspace/internal/db" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" @@ -13,10 +19,36 @@ var ErrNotFound = errors.New("not found") var ErrConflict = errors.New("already exists") type Store struct { + db *pgxpool.Pool // control database + cluster *db.Cluster +} + +func New(cluster *db.Cluster) *Store { return &Store{db: cluster.Control(), cluster: cluster} } + +// BlogStore runs the content queries of one blog against its database. +type BlogStore struct { db *pgxpool.Pool } -func New(db *pgxpool.Pool) *Store { return &Store{db: db} } +// Open returns the blog's store and fills in the settings (title, tagline, +// theme) kept in the blog database. +func (s *Store) Open(ctx context.Context, b *Blog) (*BlogStore, error) { + pool, err := s.cluster.Blog(ctx, b.DBName) + if err != nil { + return nil, err + } + bs := &BlogStore{db: pool} + if err := bs.loadSettings(ctx, b); err != nil { + // A database restored underneath a running app (dropdb --force, + // createdb, psql < dump) leaves the pool holding dead connections; + // drop them and try once more before giving up. + pool.Reset() + if err := bs.loadSettings(ctx, b); err != nil { + return nil, err + } + } + return bs, nil +} // wrap maps driver errors onto the store's sentinel errors. func wrap(err error) error { @@ -32,3 +64,8 @@ func wrap(err error) error { } return err } + +// querier is what the helpers shared with transactions need: the pool or a pgx.Tx. +type querier interface { + Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) +} |
