Documentation
¶
Overview ¶
Package pgexec is the transport-neutral execution layer shared by pdbq and pdbr: pool setup, per-request transactions with RLS role switching and claims via set_config (SET LOCAL only, so pooled connections never leak identity), serialization-failure retries, identifier quoting, and PostgreSQL error classification with the dev/prod/strict message policy.
Index ¶
- Constants
- func ApplyIdentity(ctx context.Context, tx Querier, id Identity) error
- func Connect(ctx context.Context, cfg PoolConfig) (*pgxpool.Pool, error)
- func IsSerializationFailure(err error) bool
- func ParseIsolation(s string) pgx.TxIsoLevel
- func QuoteIdent(s string) string
- func RunTx(ctx context.Context, db TxBeginner, opts TxOptions, id Identity, ...) error
- func SanitizeMessage(pgErr *pgconn.PgError, strict bool) string
- func ValidClaimKey(s string) bool
- func ValidRole(s string) bool
- type Class
- type Classified
- type Detail
- type Identity
- type PoolConfig
- type Querier
- type TxBeginner
- type TxOptions
Constants ¶
const DefaultClaimsPrefix = "pdbq.claims"
DefaultClaimsPrefix is the set_config namespace used when Identity.ClaimsPrefix is empty (kept as pdbq's so existing RLS policies work with either product).
Variables ¶
This section is empty.
Functions ¶
func ApplyIdentity ¶
ApplyIdentity switches role and exposes claims inside an open transaction. It uses SET LOCAL exclusively (never SET) so pooled connections cannot leak request identity across requests once the connection is reused.
func Connect ¶
Connect opens a pool. StatementTimeout > 0 is applied as the connection's statement_timeout runtime parameter (milliseconds).
func IsSerializationFailure ¶
IsSerializationFailure reports whether err carries SQLSTATE 40001 (serialization_failure) or 40P01 (deadlock_detected), anywhere in its wrap chain.
func ParseIsolation ¶
func ParseIsolation(s string) pgx.TxIsoLevel
ParseIsolation maps the config spelling ("read_committed", "repeatable_read", "serializable") to pgx's level; anything else is ReadCommitted.
func QuoteIdent ¶
QuoteIdent double-quotes an SQL identifier, doubling embedded quotes.
func RunTx ¶
func RunTx(ctx context.Context, db TxBeginner, opts TxOptions, id Identity, fn func(q Querier) error) error
RunTx begins a transaction with opts.Isolation, applies the identity (RLS true), calls fn, and commits unless fn returned an error (rollback). The error from fn is returned as-is (unwrapped) so callers can inspect their own error types; begin/RLS/commit errors are returned raw too. On a serialization failure or deadlock — anywhere in the attempt — the whole thing re-runs up to opts.MaxRetries times, so fn may run more than once and must not have side effects outside the transaction.
func SanitizeMessage ¶
SanitizeMessage keeps actionable constraint-class errors and hides the rest behind a generic message. strict hides those too: PG constraint messages embed constraint/table/column names, which strict mode treats as sensitive.
func ValidClaimKey ¶
ValidClaimKey reports whether s is a safe claim key for set_config (same alphabet as ValidRole).
Types ¶
type Class ¶
type Class int
Class is the transport-neutral error category. Products map it to an HTTP status (pdbr) or a GraphQL error shape (pdbq); the mapping table itself is product code.
const ( // OK: err was nil. OK Class = iota // UserInput: 22* data exceptions, 23502/23514, and 42* other than 42501. UserInput // Permission: 42501 insufficient privilege. Permission // Conflict: 23505/23503/23P01 and 40001/40P01 (retries exhausted). Conflict // Resource: 53* insufficient resources, 08* connection exceptions, // 57P01-57P03 operator intervention, connect failures. Resource // Timeout: 57014 statement timeout or a context deadline. Timeout // Internal: everything else. Internal )
type Classified ¶
type Classified struct {
Class Class
// SQLState is the five-character code, "" for non-PG errors.
SQLState string
// Message is the client-facing text after the Detail policy.
Message string
// PG is the underlying error for dev-mode extensions (detail, hint,
// table, constraint); nil for non-PG errors.
PG *pgconn.PgError
}
Classified is the result of Classify.
func Classify ¶
func Classify(err error, detail Detail) Classified
Classify categorises err and renders its client-facing message.
type Detail ¶
type Detail int
Detail is the errors.detail policy.
func ParseDetail ¶
ParseDetail maps the config strings "dev", "prod", "strict"; anything else is Prod.
type Identity ¶
type Identity struct {
// Role is the database role to SET LOCAL ROLE to ("" = no switch).
Role string
// Claims are exposed as set_config(ClaimsPrefix.key, value, true).
// String values are set verbatim; anything else is JSON-encoded. Keys
// failing ValidClaimKey are skipped.
Claims map[string]any
// ClaimsPrefix defaults to DefaultClaimsPrefix.
ClaimsPrefix string
}
Identity is the request identity applied inside a transaction.
type PoolConfig ¶
type PoolConfig struct {
URL string
MaxConns int32
ConnectTimeout time.Duration
StatementTimeout time.Duration
}
PoolConfig is the subset of database settings the pool needs.
type Querier ¶
type Querier interface {
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
}
Querier is the statement-execution surface handed to callbacks; satisfied by *pgxpool.Pool, *pgx.Conn and pgx.Tx, so callers can run the same code inside and outside a transaction.
type TxBeginner ¶
TxBeginner starts transactions; satisfied by *pgxpool.Pool and *pgx.Conn.
type TxOptions ¶
type TxOptions struct {
Isolation pgx.TxIsoLevel
// MaxRetries re-runs the whole transaction after a serialization
// failure or deadlock (SQLSTATE 40001/40P01), no backoff.
MaxRetries int
// RLS enables the SET LOCAL ROLE + set_config claims step.
RLS bool
// Log receives the retry warning; nil uses slog.Default().
Log *slog.Logger
}
TxOptions controls RunTx.