Files
CableGUI/internal/db/db.go

48 lines
1.4 KiB
Go

// Package db owns SQLite access for mCables: migrations runner + the
// query layer (store.go). The Store wraps a *sql.DB with helpers; tests
// and the HTTP layer take a *Store, never a raw *sql.DB.
package db
import (
"database/sql"
"fmt"
_ "modernc.org/sqlite"
)
// Open opens (or creates) the SQLite file at path and returns a Store
// with WAL + foreign keys + busy_timeout configured.
func Open(path string) (*Store, error) {
// `_pragma` query params are honoured by modernc.org/sqlite for
// connection-time PRAGMA setup. journal_mode WAL is persistent
// across opens; the others apply per-connection.
dsn := fmt.Sprintf(
"file:%s?_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)&_pragma=busy_timeout(5000)",
path,
)
d, err := sql.Open("sqlite", dsn)
if err != nil {
return nil, fmt.Errorf("open sqlite: %w", err)
}
if err := d.Ping(); err != nil {
_ = d.Close()
return nil, fmt.Errorf("ping sqlite: %w", err)
}
// Single writer keeps things deterministic for a local-LAN tool;
// reads scale fine in WAL.
d.SetMaxOpenConns(1)
return &Store{db: d}, nil
}
// Store is the application's handle on the SQLite database.
type Store struct {
db *sql.DB
}
// DB returns the underlying *sql.DB. Used by Migrate and (sparingly) by
// callers that need a raw query escape hatch.
func (s *Store) DB() *sql.DB { return s.db }
// Close releases the database.
func (s *Store) Close() error { return s.db.Close() }