postgres

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 12 Imported by: 0

README

postgres

Doc Go Release Test License

PostgreSQL driver module for rio, built on pgx. Two channels: database/sql (plain or over pgxpool) and native pgx execution.

Getting started

go get github.com/go-rio/postgres
db, err := postgres.Open("postgres://user:pass@localhost:5432/app")
if err != nil {
	log.Fatal(err)
}
defer db.Close()

users, err := rio.From[User]().Where("age > ?", 18).All(ctx, db)

Open validates the DSN — pgx URL or keyword/value form — but does not connect; ping with db.Unwrap().PingContext(ctx). New wraps an existing *sql.DB and leaves session configuration to the caller.

Error translation
SQLSTATE rio sentinel
23505 rio.ErrDuplicateKey
23503 rio.ErrForeignKeyViolated

The original *pgconn.PgError stays available through errors.As, including its constraint, table, and detail fields.

Execution paths

rio queries use the same API on all three paths.

Path Constructors Use for
database/sql Open, New *sql.DB pooling, sqlmock, database/sql instrumentation.
database/sql over pgxpool OpenPool, NewFromPool pgxpool configuration and PoolOf with the database/sql query path.
native pgx OpenNative, NewNativeFromPool Lower scan allocations, pgx execution modes and transaction semantics.

standard_conforming_strings

rio's placeholder lexer requires standard_conforming_strings=on, the PostgreSQL default since 9.1. Open, OpenPool, and OpenNative reject an explicit false — set directly, through the options startup parameter, or through PGOPTIONS — and do not inject the setting when omitted. If the server disables it globally, enable it in the DSN:

postgres://user:pass@localhost:5432/app?options=-c%20standard_conforming_strings%3Don

New, NewFromPool, and NewNativeFromPool cannot inspect an existing pool; their callers must enforce this requirement.

pgxpool with database/sql

db, err := postgres.OpenPool(ctx, "postgres://user:pass@localhost:5432/app?pool_max_conns=10")
if err != nil {
	log.Fatal(err)
}
defer db.Close()
  • OpenPool accepts pgxpool pool_* DSN parameters; NewFromPool takes a caller-built pool. PoolOf exposes the pool for Ping, Stat, AcquireFunc, CopyFrom, and LISTEN/NOTIFY.
  • Both constructors transfer pool ownership to the rio DB. Closing the DB closes the pool and may block until acquired connections return.
  • Configure connection counts on pgxpool, not on db.Unwrap(); the database/sql view keeps no idle connections.

Native pgx execution

OpenNative executes rio queries directly through pgx, preserving rio's SQL rendering, scanning rules, sentinel errors, hooks, and savepoint behavior. Preloads flush each relation layer in one pgx batch round trip. InsertAll streams explicit-key batches — no generated keys to backfill — over COPY.

db, err := postgres.OpenNative(ctx, "postgres://user:pass@localhost:5432/app")

NewNativeFromPool takes ownership of a caller-built pool; closing the DB closes the pool and may block until acquired connections return. db.Unwrap() stays available as a database/sql view for helpers such as migrations and pings, never for pool configuration.

Difference from database/sql Detail
tx.Unwrap() returns nil Use postgres.TxOf(tx) for the pgx.Tx (e.g. for SET LOCAL). Nested savepoints expose the root pgx transaction.
rio.WithStmtCache panics Native statement caching belongs to pgx's query execution mode.
Error text may carry pgx prefixes Check errors with errors.Is and errors.As.

rio's PostgreSQL benchmarks compare both channels.

Query exec mode and PgBouncer

The native path uses pgx's default cache_statement mode. Change it with the default_query_exec_mode DSN parameter or on a caller-built pool config.

Setup Action
Direct connection Keep the pgx default.
PgBouncer ≥ 1.21 with max_prepared_statements > 0 None.
Older PgBouncer in transaction/statement pooling Add default_query_exec_mode=exec (or simple_protocol) to the DSN. Symptom otherwise: prepared statement "stmtcache_..." does not exist.

On the database/sql paths, rio.WithStmtCache (off by default) adds DB- and transaction-local caches on top of pgx's per-connection cache. Do not use it behind transaction- or statement-mode poolers.

Arrays and JSONB

Tag JSON fields with rio:",json" to store them as jsonb (for example Prefs map[string]any on an Account model, column prefs); a set-based rio.Set{"prefs": v} uses the same mapping.

For PostgreSQL arrays, wrap pgtype in a driver.Valuer and sql.Scanner pair. The wrapper works on all three paths:

var pgMap = pgtype.NewMap()

type Tags []string

func (t Tags) Value() (driver.Value, error) {
	b, err := pgMap.Encode(pgtype.TextArrayOID, pgtype.TextFormatCode, pgtype.FlatArray[string](t), nil)
	if err != nil || b == nil {
		return nil, err
	}
	return string(b), nil
}

func (t *Tags) Scan(src any) error {
	return pgMap.SQLScanner((*pgtype.FlatArray[string])(t)).Scan(src)
}

For UpdateAll, pass the wrapper type or a rio.Expr, not a bare slice.

JSONB existence operators collide with rio's ? placeholder. Escape each literal question mark as ?? (?| → ??|, ?& → ??&):

rio.From[Account]().Where("prefs ?? ?", "beta").All(ctx, db) // prefs ? $1

Contributing

Use Go 1.27 or newer, then run go test ./..., go test -race ./..., and go vet ./... before opening a pull request.

License

MIT

Documentation

Overview

Package postgres connects rio to PostgreSQL through pgx's database/sql adapter or rio's native execution path.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(db *sql.DB, opts ...rio.Option) *rio.DB

New wraps an existing *sql.DB with the Postgres dialect and error translator. The caller must ensure standard_conforming_strings is on. Options apply last; a supplied translator replaces the default.

func NewFromPool added in v0.3.0

func NewFromPool(pool *pgxpool.Pool, opts ...rio.Option) *rio.DB

NewFromPool wraps a caller-built pgxpool.Pool and takes ownership: closing the rio DB closes the pool. The caller must ensure standard_conforming_strings is on.

func NewNativeFromPool added in v0.3.0

func NewNativeFromPool(pool *pgxpool.Pool, opts ...rio.Option) *rio.DB

NewNativeFromPool wraps a caller-built pgxpool.Pool for native execution and takes ownership: closing the rio DB closes the pool. The caller must ensure standard_conforming_strings is on.

func Open

func Open(dsn string, opts ...rio.Option) (*rio.DB, error)

Open validates a pgx DSN and returns a rio DB using pgx's database/sql adapter. It does not connect; ping and configure the pool via db.Unwrap(). URL and keyword/value DSNs are accepted; standard_conforming_strings=off is rejected, including via PGOPTIONS.

func OpenNative added in v0.3.0

func OpenNative(ctx context.Context, dsn string, opts ...rio.Option) (*rio.DB, error)

OpenNative validates a pgxpool DSN and returns a rio DB that executes queries directly through pgx. It connects lazily (ping via PoolOf) and, like Open, rejects standard_conforming_strings=off.

rio.WithStmtCache is unsupported; use pgx's default_query_exec_mode. Tx.Unwrap returns nil — use TxOf. db.Unwrap returns a database/sql view; configure pooling through pgxpool, never the view.

func OpenPool added in v0.3.0

func OpenPool(ctx context.Context, dsn string, opts ...rio.Option) (*rio.DB, error)

OpenPool validates a pgxpool DSN and returns a rio DB using pgx's database/sql adapter over that pool. It connects lazily (ping via PoolOf) and, like Open, rejects standard_conforming_strings=off. Configure pooling through the DSN, never db.Unwrap(). Closing the rio DB closes the pool and may block until acquired connections return.

func PoolOf added in v0.3.0

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

PoolOf returns the pool managed by a pool-backed or native rio DB. It returns nil for other constructions and for a nil DB.

func TxOf added in v0.3.0

func TxOf(tx *rio.Tx) pgx.Tx

TxOf returns the pgx transaction behind a native rio transaction. It returns nil for other constructions and for a nil transaction. Nested savepoints share the root pgx transaction.

Types

This section is empty.

Jump to

Keyboard shortcuts

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