Documentation
¶
Overview ¶
Package sqldb provides Postgres helpers on top of jmoiron/sqlx and the pgx driver: connection setup, named query/exec wrappers with query logging, transactions, and bulk insert/upsert.
Open builds a *sqlx.DB from a Config; StatusCheck waits for it to become reachable. The Named* helpers run named queries/execs, log the expanded SQL at debug level, and translate well-known Postgres errors into the package sentinels (ErrDBNotFound, ErrDBDuplicatedEntry, ErrUndefinedTable) so callers match them with errors.Is instead of inspecting driver codes. WithinTran runs a function inside a transaction, committing on success and rolling back on error or panic. Bulk* batch large multi-row writes within Postgres's parameter limit.
Usage ¶
cfg := sqldb.Config{
User: "postgres", Password: "postgres",
Host: "localhost:5432", Name: "app",
Schema: "public", MaxIdleConns: 2, MaxOpenConns: 5,
DisableTLS: true,
}
db, err := sqldb.Open(cfg)
if err != nil {
return err
}
if err := sqldb.StatusCheck(ctx, db); err != nil {
return err
}
// Named query into a slice.
var widgets []Widget
const q = `SELECT id, name FROM widgets WHERE name = :name`
arg := struct {
Name string `db:"name"`
}{Name: "gadget"}
if err := sqldb.NamedQuerySlice(ctx, log, db, q, arg, &widgets); err != nil {
return err
}
// Write inside a transaction.
err = sqldb.WithinTran(ctx, log, db, func(tx *sqlx.Tx) error {
const ins = `INSERT INTO widgets (id, name) VALUES (:id, :name)`
return sqldb.NamedExecContext(ctx, log, tx, ins, w)
})
if errors.Is(err, sqldb.ErrDBDuplicatedEntry) {
// unique violation
}
Querying ¶
- ExecContext / NamedExecContext run statements; the RowsAffected variant returns the affected-row count.
- QueryStruct / NamedQueryStruct scan exactly one row (ErrDBNotFound when none).
- QuerySlice / NamedQuerySlice scan every row into a *[]T.
The plain (non-Named) variants take no parameters; the Named variants bind from a struct via its `db` tags.
Bulk writes ¶
BulkInsert writes len(values)/len(columns) rows, splitting into batches that stay under Postgres's bind-parameter cap, with values laid out row-major. An optional conflictAction (e.g. "ON CONFLICT DO NOTHING") is appended verbatim. BulkUpsert builds the ON CONFLICT … DO UPDATE clause for you from the conflict columns.
Transactions ¶
WithinTran is the common path. For stores that must run against either a pool or an outer transaction, depend on the Beginner / CommitRollbacker seam: NewBeginner adapts a *sqlx.DB, and ExtContext extracts the query surface from a started transaction.
Config ¶
Config fields: User, Password, Host, Name, Schema (sets search_path), MaxIdleConns, MaxOpenConns, DisableTLS (DisableTLS=true selects sslmode=disable, otherwise sslmode=require). Open always sets timezone=utc.
Index ¶
- Variables
- func BulkInsert(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, table string, ...) error
- func BulkUpsert(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, table string, ...) error
- func ExecContext(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string) error
- func ExtContext(tx CommitRollbacker) (sqlx.ExtContext, error)
- func NamedExecContext(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) error
- func NamedExecContextRowsAffected(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) (int64, error)
- func NamedQuerySlice[T any](ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) error
- func NamedQueryStruct(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) error
- func Open(cfg Config) (*sqlx.DB, error)
- func QuerySlice[T any](ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) error
- func QueryStruct(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, ...) error
- func StatusCheck(ctx context.Context, db *sqlx.DB) error
- func WithinTran(ctx context.Context, log *logger.Logger, db *sqlx.DB, ...) error
- type Beginner
- type CommitRollbacker
- type Config
- type DBBeginner
Constants ¶
This section is empty.
Variables ¶
var ( ErrDBNotFound = sql.ErrNoRows ErrDBDuplicatedEntry = errors.New("duplicated entry") ErrUndefinedTable = errors.New("undefined table") )
Sentinel errors returned by this package; match them with errors.Is.
Functions ¶
func BulkInsert ¶
func BulkInsert(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, table string, columns []string, values []any, conflictAction string) error
BulkInsert inserts len(values)/len(columns) rows into table in batches, each batch a single multi-row INSERT. conflictAction, if non-empty, is appended verbatim (e.g. "ON CONFLICT DO NOTHING"). values are laid out row-major: [r0c0, r0c1, r1c0, r1c1, ...].
For upserts prefer BulkUpsert, which builds the conflict clause for you.
func BulkUpsert ¶
func BulkUpsert(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, table string, columns []string, values []any, conflictColumns []string) error
BulkUpsert inserts rows and, on conflict over conflictColumns, updates every non-conflict column from the proposed row.
func ExecContext ¶
ExecContext runs a parameterless statement.
func ExtContext ¶
func ExtContext(tx CommitRollbacker) (sqlx.ExtContext, error)
ExtContext extracts the sqlx.ExtContext (the query surface) from a transaction returned by Begin.
func NamedExecContext ¶
func NamedExecContext(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any) error
NamedExecContext runs an INSERT/UPDATE/DELETE with named parameters bound from data. It logs the expanded query and translates well-known Postgres errors.
func NamedExecContextRowsAffected ¶
func NamedExecContextRowsAffected(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any) (int64, error)
NamedExecContextRowsAffected is like NamedExecContext but returns the number of affected rows.
func NamedQuerySlice ¶
func NamedQuerySlice[T any](ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any, dest *[]T) error
NamedQuerySlice runs a named query and scans all rows into *[]T.
func NamedQueryStruct ¶
func NamedQueryStruct(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data, dest any) error
NamedQueryStruct runs a named query expected to return exactly one row.
func Open ¶
Open opens a sqlx.DB using the pgx driver. It does not verify connectivity; call StatusCheck for that.
func QuerySlice ¶
func QuerySlice[T any](ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, dest *[]T) error
QuerySlice runs a parameterless query and scans all rows into *[]T.
func QueryStruct ¶
func QueryStruct(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, dest any) error
QueryStruct runs a parameterless query expected to return exactly one row and scans it into dest (a pointer to a struct).
func StatusCheck ¶
StatusCheck pings the database, retrying until it is reachable or ctx is done.
On timeout it returns errors.Join(ctx.Err(), lastPingErr) rather than a bare "context deadline exceeded", so the real cause — connection refused, TLS failure, password authentication failed — is visible at the call site instead of being masked by the deadline.
Types ¶
type Beginner ¶
type Beginner interface {
Begin() (CommitRollbacker, error)
}
Beginner starts a transaction. It is the seam stores depend on so they can be driven either by a pool or by an outer transaction (see middleware that opens a transaction per request).
type CommitRollbacker ¶
CommitRollbacker is a transaction that can be committed or rolled back.
type Config ¶
type Config struct {
User string
Password string
Host string
Name string
Schema string
MaxIdleConns int
MaxOpenConns int
DisableTLS bool
}
Config holds connection settings.
type DBBeginner ¶
type DBBeginner struct {
// contains filtered or unexported fields
}
DBBeginner adapts a *sqlx.DB to the Beginner interface.
func NewBeginner ¶
func NewBeginner(db *sqlx.DB) *DBBeginner
NewBeginner returns a Beginner backed by db.
func (*DBBeginner) Begin ¶
func (b *DBBeginner) Begin() (CommitRollbacker, error)
Begin starts a new transaction.