postgres

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package postgres provides PostgreSQL access built on pgx/v5 and pgxpool. It exposes a DB type that wraps *pgxpool.Pool and implements the Querier interface. The legacy database/sql API is intentionally not exposed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InjectTx

func InjectTx(ctx context.Context, tx pgx.Tx) context.Context

InjectTx returns a new context carrying tx as the ambient transaction. Use this in tests to inject a pre-opened transaction that will be rolled back.

func IsConstraintViolation

func IsConstraintViolation(err error) bool

IsConstraintViolation reports whether err is a PostgreSQL SQLSTATE 23xxx (integrity constraint violation family).

func IsUniqueViolation

func IsUniqueViolation(err error) bool

IsUniqueViolation reports whether err is SQLSTATE 23505 (unique_violation).

func PgError

func PgError(err error) *pgconn.PgError

PgError unwraps err to a *pgconn.PgError if possible. Returns nil otherwise. Use this to inspect SQLSTATE codes without hiding the original error.

Types

type DB

type DB struct {
	// contains filtered or unexported fields
}

DB wraps a *pgxpool.Pool and provides the devengine PostgreSQL surface.

func Open

func Open(ctx context.Context, connString string, opts Options) (*DB, error)

Open parses connString, applies opts and returns a connected DB. connString may be a DSN (key=value) or a postgres:// URL.

func OpenConfig

func OpenConfig(ctx context.Context, cfg *pgxpool.Config, opts Options) (*DB, error)

OpenConfig applies opts to cfg and returns a connected DB. cfg must have been created by pgxpool.ParseConfig. The provided config is not mutated.

func (*DB) Close

func (db *DB) Close()

Close shuts down the connection pool gracefully.

func (*DB) Exec

func (db *DB) Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)

Exec implements Querier against the pool.

func (*DB) Ping

func (db *DB) Ping(ctx context.Context) error

Ping verifies the pool can reach the database.

func (*DB) Pool

func (db *DB) Pool() *pgxpool.Pool

Pool returns the underlying *pgxpool.Pool for callers that need raw pgx features (COPY, LISTEN/NOTIFY, batch, etc.). Prefer Querier for normal use.

func (*DB) Querier

func (db *DB) Querier(ctx context.Context) Querier

Querier returns the ambient pgx.Tx stored in ctx, or the pool itself if no transaction is active. Use this in repositories to transparently participate in an outer transaction.

func (*DB) Query

func (db *DB) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)

Query implements Querier against the pool.

func (*DB) QueryRow

func (db *DB) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row

QueryRow implements Querier against the pool.

func (*DB) ReadyCheck

func (db *DB) ReadyCheck() func(context.Context) error

ReadyCheck returns a func suitable for health.Registry.Register.

func (*DB) WithTransaction

func (db *DB) WithTransaction(ctx context.Context, fn func(context.Context, pgx.Tx) error, cfgs ...RetryConfig) error

WithTransaction executes fn inside a pgx transaction using default pgx transaction options. If ctx already carries an ambient transaction (via WithTransaction or InjectTx), fn is called within a savepoint instead of a new top-level transaction, implementing nested transaction semantics.

The provided fn receives a txCtx which MUST be passed to subsequent db.Querier(txCtx) calls. This ensures cross-repository operations participate in the same transaction.

Example:

db.WithTransaction(ctx, func(txCtx context.Context, tx pgx.Tx) error {
    if err := userRepo.Create(txCtx, user); err != nil { return err }
    if err := logRepo.Record(txCtx, "user_created"); err != nil { return err }
    return nil
})

Transient errors whose SQLSTATE matches cfg.RetryOn cause automatic retry up to cfg.MaxAttempts times with exponential jitter backoff. Consequently fn is retryable code: it must not perform external side effects. Store an outbox message or a jobs.Job in tx and let the Outbox/Jobs worker run that side effect after the transaction commits.

Non-transient errors, context cancellation, and business errors are never retried. If fn returns nil, the transaction (or savepoint) is committed.

func (*DB) WithTransactionOptions

func (db *DB) WithTransactionOptions(ctx context.Context, txOptions pgx.TxOptions, fn func(context.Context, pgx.Tx) error, cfgs ...RetryConfig) error

WithTransactionOptions executes fn using txOptions for a new top-level pgx transaction. Its retry and side-effect contract is identical to WithTransaction.

PostgreSQL transaction options are fixed when the outer transaction begins. Therefore, when ctx has an ambient transaction, zero txOptions uses a savepoint and non-zero txOptions returns an error rather than silently ignoring the requested isolation/access/deferrable modes.

type Options

type Options struct {
	// MaxConns is the maximum number of connections in the pool.
	// Defaults to pgxpool default (4 × NumCPU).
	MaxConns int32
	// MinConns keeps at least this many connections alive.
	MinConns int32
	// MaxConnLifetime is the maximum age of a connection before recycling.
	MaxConnLifetime time.Duration
	// MaxConnIdleTime is how long an idle connection is kept before closing.
	MaxConnIdleTime time.Duration
	// HealthCheckPeriod is how often the pool pings idle connections.
	HealthCheckPeriod time.Duration
}

Options configures the connection pool.

type Querier

type Querier interface {
	Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
	Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}

Querier abstracts the common row-level operations available on both a pool and a transaction. Repositories should depend on Querier, not on *DB or *pgxpool.Pool directly.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the total number of attempts (including the first).
	// Zero or negative means no retry (single attempt).
	MaxAttempts int
	// RetryOn is the set of SQLSTATE codes that qualify for a retry.
	// If empty, defaults to {"40P01"} (deadlock_detected).
	RetryOn []string
	// InitialBackoff is the base wait before the first retry.
	// Defaults to 20ms.
	InitialBackoff time.Duration
	// MaxBackoff caps the exponential backoff.
	// Defaults to 500ms.
	MaxBackoff time.Duration
}

RetryConfig controls how WithTransaction retries on transient SQLSTATE errors.

A callback passed to WithTransaction can run more than once. It must not perform external side effects such as sending email, publishing a webhook, or acknowledging a message broker. Persist an outbox message or a delayed job in the transaction instead, and let its handler perform the external action after commit.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL