Documentation
¶
Overview ¶
Package sqlate is the session layer of the sqlate library, which every authored statement runs through. It wraps a plain *sql.DB behind the stdlib method set (ExecContext, QueryContext, PrepareContext), maps every driver error through the engine's dialect at that boundary, and owns the transaction runner. The package depends on the standard library alone; each engine lives in a sub-module (postgres) that supplies the dialect, so a consumer imports its engine once, where it opens its pool. The packages above it, query and migrate, take a Session and never a driver.
Wrapper ¶
Wrap builds the DB session over a pool with the engine's Dialect. Lifecycle (opening, readiness, closing) belongs to whoever owns the pool; DB adds error mapping, prepare, options on Begin, and pinned connections. It performs no I/O: a nil pool or dialect is a defect in the caller and panics. DB.Conn pins one connection for a protocol that needs session scope, such as a session-level lock or a run of non-transactional DDL; DB.Dialect returns the dialect statements are compiled against.
Sessions and transactions ¶
Session is the method set the query and migrate packages take, implemented by both DB and Tx, so the same statement runs against the pool or inside a transaction. The consumer owns the transaction boundary: DB.Begin opens a Tx with TxOption values applied, and DB.Transact runs one unit of work with a result: commit on success, rollback on the unit's error or panic; Transact is the same runner over any Beginner. Tx.Commit routes its error through the dialect's MapError, the one place a violation deferred to COMMIT can be classified. ErrorMapper is the capability both sessions expose for errors that arise after a call returns, rows.Err and Scan, which the session cannot see.
Dialect ¶
Dialect is what the library needs from an engine: its name, how it renders the nth bind parameter, and how its driver's errors classify. An engine sub-module implements it. Capabilities beyond it are separate interfaces a protocol asserts: Locker, the named session-scoped lock a concurrent-starter protocol such as migration takes; TryLocker, the same lock tried without waiting, for a protocol that skips its work when another session holds it or, as migration does, polls for it; and query.Returner, which renders a command that returns its changed row as one statement. Beginner is the session capability a protocol asserts to open its own transaction.
Errors ¶
The package owns the error taxonomy consumers match on. ErrConnectionFailed classifies a connectivity failure, ErrInvalidValue a data exception, ErrDependentObjects a refusal to drop an object another still depends on, and ErrSerializationFailure a serialization failure under SERIALIZABLE isolation, each wrapped in the dual form fmt.Errorf("%w: %w", sentinel, err) so errors.Is classifies while the driver's error stays recoverable. The session classifies connectivity itself, before the dialect: a network error or driver.ErrBadConn from any call means the engine never saw the statement, so the dialect's vocabulary does not apply. A dialect's MapError returns the four constraint classes (ErrUniqueViolation, ErrForeignKeyViolation, ErrCheckViolation, ErrNotNullViolation) inside a ConstraintError, with the constraint name when the driver exposes it. sql.ErrNoRows is never mapped; MapError returns it unchanged.
Index ¶
- Variables
- func Transact[T any](ctx context.Context, b Beginner, fn func(*Tx) (T, error), opts ...TxOption) (T, error)
- type Beginner
- type ConstraintError
- type DB
- func (d *DB) Begin(ctx context.Context, opts ...TxOption) (*Tx, error)
- func (d *DB) Conn(ctx context.Context) (*sql.Conn, error)
- func (d *DB) Dialect() Dialect
- func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (d *DB) MapError(err error) error
- func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
- func (d *DB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (d *DB) Transact[T any](ctx context.Context, fn func(*Tx) (T, error), opts ...TxOption) (T, error)
- type Dialect
- type ErrorMapper
- type Locker
- type Session
- type TryLocker
- type Tx
- func (t *Tx) Commit() error
- func (t *Tx) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (t *Tx) MapError(err error) error
- func (t *Tx) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
- func (t *Tx) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (t *Tx) Rollback() error
- type TxOption
Constants ¶
This section is empty.
Variables ¶
var ( // ErrConnectionFailed classifies a failure to reach the engine: a // failure to obtain a connection or begin a transaction, and a network // error or driver.ErrBadConn raised by any session call. It is wrapped // as fmt.Errorf("%w: %w", ErrConnectionFailed, err) so errors.Is // matches it and the driver's error stays reachable. ErrConnectionFailed = errors.New("database connection failed") // ErrInvalidValue classifies a data exception: a bound value the engine // could not read as the type it was cast to (SQLSTATE class 22). It is // the engine-side half of request validation (a filter value that is // not a uuid, a date that is not a date), and a dialect maps its // engine's form to it, wrapped in the same dual form. ErrInvalidValue = errors.New("invalid value") // ErrUniqueViolation classifies a unique-constraint violation. A // dialect's MapError wraps it in a [ConstraintError]; errors.Is // matches the class. ErrUniqueViolation = errors.New("unique constraint violation") // ErrForeignKeyViolation classifies a foreign-key-constraint violation, // wrapped the same way as [ErrUniqueViolation]. ErrForeignKeyViolation = errors.New("foreign key constraint violation") // ErrCheckViolation classifies a check-constraint violation, wrapped // the same way as [ErrUniqueViolation]. ErrCheckViolation = errors.New("check constraint violation") // ErrNotNullViolation classifies a not-null-constraint violation, // wrapped the same way as [ErrUniqueViolation]. ErrNotNullViolation = errors.New("not-null constraint violation") // ErrDependentObjects classifies a refusal to drop an object another // object still depends on (SQLSTATE class 2BP01). A multi-set // migration's Down or Reset raises it when it reverts sets out of // order and a foreign key from a set above still references the set // being reverted. A dialect's MapError wraps it as // fmt.Errorf("%w: %w", ErrDependentObjects, err) so errors.Is matches // the class and the driver's error stays reachable. ErrDependentObjects = errors.New("dependent objects still exist") // ErrSerializationFailure classifies a serialization failure under // SERIALIZABLE isolation (SQLSTATE 40001). Two migrator starters // raise it when they race under the migration lock and the engine // offers no native advisory lock to serialize them. A dialect maps // its engine's form to it, wrapped in the same dual form as // [ErrDependentObjects]. ErrSerializationFailure = errors.New("serialization failure") )
Functions ¶
func Transact ¶ added in v0.3.0
func Transact[T any](ctx context.Context, b Beginner, fn func(*Tx) (T, error), opts ...TxOption) (T, error)
Transact runs fn as one unit of work with a result in a transaction b opens: begin with opts, fn, commit on success. On fn's error it rolls back and returns that error, with a rollback failure joined onto it. A panic in fn rolls back and re-panics, so no transaction leaks to the pool's reaper. b is *DB or any type that embeds it, so a protocol handed a session it asserts to Beginner runs its unit the way DB.Transact does.
Types ¶
type Beginner ¶ added in v0.3.0
Beginner is a session that can open a transaction: *DB, and any type that embeds it. A protocol that must run several statements as one unit, and is handed a session rather than a transaction, asserts it to open its own.
type ConstraintError ¶
ConstraintError is a classified constraint violation: the class sentinel, the driver's own error, and the violated constraint's name, table, and column when the driver exposes them. A driver exposes them unevenly: a not-null violation names a column and no constraint, so Column is the only handle that case offers. Unwrap yields both wrapped errors, so errors.Is matches the class and errors.As finds the driver error, and a consumer maps the constraint name, or the column name when there is no constraint name, to its field.
func (*ConstraintError) Error ¶
func (e *ConstraintError) Error() string
func (*ConstraintError) Unwrap ¶
func (e *ConstraintError) Unwrap() []error
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB is the pool session over a plain *sql.DB. Lifecycle (opening, readiness, closing) belongs to whoever owns the pool; DB adds mapping, prepare, options on Begin, and pinned connections.
func Wrap ¶
Wrap builds the session over pool with dialect, the engine's or a capability-adding wrapper of it. Nil arguments are a defect in the caller and panic.
func (*DB) Begin ¶
Begin opens a transaction with the options applied; a failure to begin wraps ErrConnectionFailed.
func (*DB) Conn ¶
Conn pins one connection for a protocol that needs session scope: a session-level lock, a run of non-transactional DDL. The caller closes it.
func (*DB) ExecContext ¶
func (*DB) MapError ¶
MapError classifies a connectivity failure as ErrConnectionFailed and routes every other error through the dialect; nil stays nil.
func (*DB) PrepareContext ¶
func (*DB) QueryContext ¶
type Dialect ¶
type Dialect interface {
// Name identifies the engine.
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 library's sentinels;
// it returns sql.ErrNoRows unchanged.
MapError(err error) error
}
Dialect is what the library needs from an engine: its name, how it renders the nth bind parameter, and how its driver's errors classify. An engine sub-module implements it; a provider library's dialect satisfies it structurally. Capabilities beyond it, Locker, TryLocker, and ErrorMapper, are separate interfaces a protocol asserts.
type ErrorMapper ¶
ErrorMapper is the capability both sessions expose for errors that arise after a call returns, from rows.Err and Scan, which the session's methods cannot see. A runner type-asserts it on its Session.
type Locker ¶
type Locker interface {
Lock(ctx context.Context, conn *sql.Conn, name string) error
Unlock(ctx context.Context, conn *sql.Conn, name string) error
}
Locker is the dialect capability a concurrent-starter protocol takes: a session-scoped exclusive lock on a dedicated connection, identified by name. Locks are named, never numbered (<owner>.<structure>, such as migrate.schema_version or organization.tree), and the dialect maps the name to its engine's lock space. A provider without the capability cannot serialize migrations across processes.
type Session ¶
type Session interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}
Session is the stdlib method set a runner needs, implemented by *DB and *Tx, so the same statement handle runs against the pool or inside a transaction. Every method classifies a connectivity failure and maps every other driver error through the dialect; the dialect itself is not on the interface, because runners never need it at request time.
type TryLocker ¶ added in v0.6.0
type TryLocker interface {
Locker
TryLock(ctx context.Context, conn *sql.Conn, name string) (bool, error)
}
TryLocker is the dialect capability a protocol takes when it must not wait for a lock another session holds, or must wait without holding a statement open, as migrate does by polling: a Locker that can also try the same named lock without blocking. TryLock reports whether it took the lock; a lock it takes is the same session-scoped lock Lock takes, in the same name space, and the Locker's Unlock on the same conn releases it. A name held by another session, through Lock or TryLock, reports false and no error.
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
Tx is one transaction, a Session whose errors are classified and mapped on every call and on commit.
func (*Tx) Commit ¶
Commit commits, mapping the error: the one place a violation deferred to COMMIT can be classified.
func (*Tx) ExecContext ¶
func (*Tx) MapError ¶
MapError classifies a connectivity failure as ErrConnectionFailed and routes every other error through the dialect; nil stays nil.
func (*Tx) PrepareContext ¶
func (*Tx) QueryContext ¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package header reads the declaration header of an authored SQL file: the "--|" declarations that precede the body the engine receives.
|
Package header reads the declaration header of an authored SQL file: the "--|" declarations that precede the body the engine receives. |
|
Package migrate is schema versioning over authored SQL: ordered sets of migrations, each migration SQL text plus a version, applied against a history table on one pinned connection under the dialect's lock capability.
|
Package migrate is schema versioning over authored SQL: ordered sets of migrations, each migration SQL text plus a version, applied against a history table on one pinned connection under the dialect's lock capability. |
|
postgres
module
|
|
|
Package query runs authored SQL files.
|
Package query runs authored SQL files. |
|
sqlint
module
|
|
|
Package sqltest is the scripted database/sql driver a consumer's hermetic tests run over: its connections consume scripted responses in call order and record every statement, prepare, and transaction call they receive.
|
Package sqltest is the scripted database/sql driver a consumer's hermetic tests run over: its connections consume scripted responses in call order and record every statement, prepare, and transaction call they receive. |