The package-doc examples showed a fictional launcher.Register / health.Register API (and launcher.Append as a package func). Corrected to the real wiring: lc.Append + web/health.NewHandler(...).ServeHTTP. Documentation-only. No code, API, or dependency changes; version stays v1.1.0 (the v1.1.0 tag is moved to include this fix). Verified by compiling the corrected pattern against the real API.
74 lines
2.9 KiB
Go
74 lines
2.9 KiB
Go
// Package sqlite provides a pure-Go SQLite client with lifecycle management,
|
|
// health checks, and unit-of-work transaction support for Einherjar applications.
|
|
//
|
|
// # Overview
|
|
//
|
|
// [New] returns a [Component] that manages a [database/sql] connection pool,
|
|
// satisfies the [lifecycle.Component] lifecycle hooks (OnInit, OnStart, OnStop),
|
|
// and implements [observability.Checkable] with critical priority.
|
|
//
|
|
// [NewUnitOfWork] wraps multiple repository operations in a single serialized
|
|
// transaction via context injection — no transaction object is passed between
|
|
// functions. Write transactions are serialized through an internal mutex to
|
|
// prevent SQLITE_BUSY errors under concurrent goroutines.
|
|
//
|
|
// # Lifecycle Registration
|
|
//
|
|
// db := sqlite.New(logger, cfg)
|
|
// lc.Append(db) // lifecycle: OnInit → OnStart → OnStop
|
|
//
|
|
// db satisfies [observability.Checkable], so it can back a health endpoint via
|
|
// web/health.NewHandler:
|
|
//
|
|
// srv.Get("/health", health.NewHandler(logger, db).ServeHTTP)
|
|
//
|
|
// # Querying
|
|
//
|
|
// Repository code receives a [Provider] or [Executor] and calls [Provider.GetExecutor]
|
|
// to obtain the active transaction (if inside a [UnitOfWork]) or the connection pool:
|
|
//
|
|
// exec := db.GetExecutor(ctx)
|
|
// row := exec.QueryRowContext(ctx, "SELECT id FROM users WHERE email = ?", email)
|
|
// if err := row.Scan(&id); err != nil {
|
|
// return db.HandleError(err) // maps sql.ErrNoRows → ErrNotFound, etc.
|
|
// }
|
|
//
|
|
// # Unit of Work
|
|
//
|
|
// [NewUnitOfWork] wraps operations in a single serialized write transaction.
|
|
// The transaction is injected into the context; [Provider.GetExecutor] returns
|
|
// it automatically.
|
|
//
|
|
// uow := sqlite.NewUnitOfWork(logger, db)
|
|
// err := uow.Do(ctx, func(ctx context.Context) error {
|
|
// exec := db.GetExecutor(ctx) // returns active Tx
|
|
// _, err := exec.ExecContext(ctx, "INSERT INTO orders ...")
|
|
// return err
|
|
// })
|
|
//
|
|
// # Error Handling
|
|
//
|
|
// [HandleError] translates database/sql and SQLite error codes into typed
|
|
// [xerrors] values. Call it at every point where a database error is first observed:
|
|
//
|
|
// if err := row.Scan(&out); err != nil {
|
|
// return db.HandleError(err)
|
|
// }
|
|
//
|
|
// Mapped codes:
|
|
// - sql.ErrNoRows → ErrNotFound
|
|
// - SQLITE_CONSTRAINT_UNIQUE (2067) → ErrAlreadyExists
|
|
// - SQLITE_CONSTRAINT_PRIMARYKEY (1555) → ErrAlreadyExists
|
|
// - SQLITE_CONSTRAINT_FOREIGNKEY (787) → ErrInvalidInput
|
|
// - all others → ErrInternal
|
|
//
|
|
// # Configuration
|
|
//
|
|
// All fields are read from environment variables with the EINHERJAR_SQLITE_* prefix:
|
|
//
|
|
// - EINHERJAR_SQLITE_PATH (required) — file path or ":memory:"
|
|
// - EINHERJAR_SQLITE_MAX_OPEN_CONNS — default: 1
|
|
// - EINHERJAR_SQLITE_MAX_IDLE_CONNS — default: 1
|
|
// - EINHERJAR_SQLITE_PRAGMAS — default: "?_journal=WAL&_timeout=5000&_fk=true"
|
|
package sqlite
|