Documentation
¶
Overview ¶
Package database provides the SQL data layer's dialect-neutral core: a lifecycle-integrated wrapper over a database/sql connection pool and the dialect seam its providers implement. The package is stdlib-plus-config only; every driver lives in a provider sub-module (database/postgres) that constructs the pool and supplies the dialect, so a consumer imports its provider once, at the composition root.
Wrapper ¶
New wraps the provider-constructed *sql.DB with the provider's Dialect and a finalized Config, applying the config's pool settings — the dialect-independent half of construction every provider shares. It performs no I/O: an unfinalized Config, nil conn, or nil dialect panics with the fix named, and everything else waits for Start. DB.Conn exposes the pool; DB.Dialect the seam.
Lifecycle wiring ¶
The package registers no lifecycle hooks of its own. DB.Start and DB.Shutdown carry the lifecycle package's hook signature, so a composition root wires the database as bare method values:
lc.OnStartup(db.Start) lc.OnShutdown(db.Shutdown)
Start establishes connectivity with a ping bounded by the configured conn_timeout, so a dead database fails the coordinator's startup rather than serving traffic unready. Shutdown closes the pool and returns the close error; after a failed startup the drain passes through it cleanly.
Readiness ¶
DB.Ready satisfies lifecycle.ReadinessChecker structurally and reports live connectivity: false before Start or after Shutdown, and otherwise a ping bounded by conn_timeout. A readiness probe aggregating the database therefore reflects it now — 503 during an outage, healed when the database returns — at the cost of one bounded round trip per probe. DB.Ping is the same verification under the caller's context and bound.
Dialect ¶
Dialect is the whole provider surface the base needs: a name, the bind placeholder renderer, and the driver error mapper. At the connectivity slice MapError is the identity; query surfaces in later slices route SQL rendering and error classification through the seam, which is what keeps them dialect-neutral. Providers are selected by typed construction — a Provider constant and a constructor per sub-module, no registry.
Configuration ¶
Config holds the connection identity, pool sizing, and timeouts, and implements the config package's Merge and Finalize contract, so it loads as part of an application's configuration. The numeric and duration fields are pointers: nil is unset and takes the default, while an explicit zero survives the load and means what it says. Port defaults in the provider, User and Password stay optional (requiredness varies by provider and auth mode), and Options passes dialect-specific connection keys through to the provider. Finalize composes the standard override names from the prefix it receives (via NewEnv, recorded on Env for introspection); an empty prefix disables the overrides.
Errors ¶
ErrNotReady and ErrConnectionFailed are the package's two sentinels, wrapped in the dual form fmt.Errorf("%w: %w", sentinel, err) so errors.Is classifies while the driver's error stays recoverable. sql.ErrNoRows is never mapped; it flows to the boundary unchanged.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotReady reports a call against a [DB] before a successful Start // or after Shutdown. ErrNotReady = errors.New("database not ready") // ErrConnectionFailed classifies a connectivity failure. It is wrapped // alongside the driver's error in the dual form // fmt.Errorf("%w: %w", ErrConnectionFailed, err), so errors.Is matches // the class while the cause stays recoverable. ErrConnectionFailed = errors.New("database connection failed") )
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Host string `json:"host"`
Name string `json:"name"`
User string `json:"user"`
Password string `json:"password"`
Port *int `json:"port"`
MaxOpenConns *int `json:"max_open_conns"`
MaxIdleConns *int `json:"max_idle_conns"`
ConnMaxLifetime *config.Duration `json:"conn_max_lifetime"`
ConnMaxIdleTime *config.Duration `json:"conn_max_idle_time"`
ConnTimeout *config.Duration `json:"conn_timeout"`
Options map[string]string `json:"options"`
Env Env `json:"-"`
}
Config holds the connection identity, pool sizing, and timeouts for a SQL database. The numeric and duration fields are tri-state pointers: nil is unset and takes the default, while an explicit zero survives the load and means what it says — an unlimited pool, or no idle connections. Port has no default here because the default port is a provider fact; each provider supplies its own. User and Password are optional because their requiredness varies by provider and auth mode; the password rides the secrets layer of config.Load rather than a committed file. Options carries dialect-specific connection keys (postgres: sslmode) passed through to the provider. Env records the environment-variable names Finalize composed and read; it is excluded from JSON.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB wraps a provider-constructed connection pool with lifecycle integration and the provider's dialect. It owns one pool for the process: construction performs no I/O, Start establishes connectivity, and Ready reports it live. Start and Shutdown carry the lifecycle hook signature, so the composition root registers the bare method values, and Ready satisfies lifecycle.ReadinessChecker structurally; the package registers no hooks of its own.
func New ¶
New wraps a provider-constructed pool with dialect and applies cfg's pool settings — the dialect-independent half of construction every provider shares. It panics if cfg was not finalized, or on a nil conn or dialect: each is a wiring defect at the composition root, not a runtime condition.
func (*DB) Ping ¶
Ping verifies connectivity under the caller's context: ErrNotReady before Start or after Shutdown, and otherwise a ping whose failure wraps ErrConnectionFailed. Unlike DB.Ready it applies no timeout of its own.
func (*DB) Ready ¶
Ready reports live connectivity: false before Start or after Shutdown, and otherwise the result of a ping bounded by the configured conn_timeout. The probe reflects the database now — readiness drops during an outage and recovers with it, at the cost of one bounded round trip per call.
func (*DB) Shutdown ¶
Shutdown clears readiness and closes the pool, returning the close error. Closing a pool that never connected is a clean no-op, so the drain-after-failed-startup path is safe. The context exists for the lifecycle hook signature; sql.DB.Close is synchronous.
func (*DB) Start ¶
Start establishes connectivity with a ping bounded by the configured conn_timeout and marks the database started. A failure wraps ErrConnectionFailed around the driver's error. There is no started guard: a repeat Start just pings again.
type Dialect ¶
type Dialect interface {
// Name identifies the dialect; it matches the provider's Provider
// constant.
Name() string
// Placeholder renders the 1-based nth bind parameter ("$1" for
// postgres, "@p1" for a future mssql).
Placeholder(n int) string
// MapError translates a driver error into the package's sentinels. At
// the connectivity slice it is the identity: nothing is classified and
// sql.ErrNoRows always flows through unchanged.
MapError(err error) error
}
Dialect is the seam a provider implements: the whole surface the base package needs from an engine. Query surfaces in later slices route SQL rendering and driver errors through it, so consumers import their provider only at the composition root.
type Env ¶
type Env struct {
Host string
Name string
User string
Password string
Port string
MaxOpenConns string
MaxIdleConns string
ConnMaxLifetime string
ConnMaxIdleTime string
ConnTimeout string
}
Env names the environment variables Config.Finalize reads, composed from the prefix it receives under the "database" segment: DATABASE_HOST, DATABASE_PORT, and the rest, prefixed by whatever config.EnvName produces. An empty name disables that one override; the zero value — an empty prefix — disables all of them. Populated by Finalize and exposed for introspection.
func NewEnv ¶
NewEnv composes the standard override names from a prefix under the "database" segment: DATABASE_HOST, DATABASE_PORT, and the rest, prefixed by whatever config.EnvName produces. An empty prefix returns the zero Env, disabling the overrides.