relational

package
v1.36.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 15 Imported by: 7

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LockRow added in v1.32.0

func LockRow(ctx context.Context, tx bun.Tx, model any) error

LockRow locks the row model's primary key names until tx ends and loads its current values into model. Other transactions that lock or write the row wait; plain reads do not.

It takes a bun.Tx rather than a bun.IDB because a row lock taken outside a transaction is released when the statement ends and protects nothing.

On PostgreSQL the lock is FOR NO KEY UPDATE rather than FOR UPDATE, so inserts of rows that reference this one by foreign key are not blocked. SQLite has no row locks, so there the row is only loaded, and on a file database a second transaction that loaded the row meanwhile fails its write with a "database is locked" error instead of waiting. A missing row is ENOTFOUND.

func Mutate added in v1.32.0

func Mutate(ctx context.Context, db bun.IDB, model DBModel, fn func(ctx context.Context, tx bun.Tx) error) error

Mutate locks the row model's primary key names, loads it into model, runs fn, then calls model.Update, all in one transaction. Set fields inside fn: whatever was set on model before the call is overwritten by the load.

An error from the load, fn or Update rolls the transaction back and is returned with its code. When db is already a bun.Tx, Mutate runs inside a savepoint of it, and the caller's commit or rollback decides the outcome.

fn should write through tx, so its writes roll back with the rest. Side effects that cannot be rolled back, such as sending an email or calling another service, belong after Mutate returns, not inside fn: fn may have run for a change that was never saved.

When fn locks other rows too, with LockRow, every code path must lock them in the same order, counting model's row, which is locked before fn runs, or two requests can deadlock and PostgreSQL fails one of them.

func SetColumn added in v1.32.0

func SetColumn[T any](p *Patch, column string, dst *T, value types.Optional[T])

SetColumn copies value into dst and records column when value was sent. An absent key does nothing. A null is an EINVALID error that Update returns, because dst cannot hold null; the error's data names the column.

func SetNullableColumn added in v1.32.0

func SetNullableColumn[T any](p *Patch, column string, dst **T, value types.Optional[T])

SetNullableColumn is SetColumn for a nullable column held as *T: a null sets *dst to nil, a value sets it to a pointer to a copy of the value. An absent key does nothing.

Types

type Baseline added in v1.27.0

type Baseline struct {
	// SQL is the output of pg_dump --schema-only of a database that has run
	// every migration up to and including Floor. Empty when the app has no baseline yet.
	SQL string
	// Floor is the highest name among the deleted migration files, as bun stores
	// it in the name column (the digits before the first underscore, not the
	// file name); SQL holds it and everything below it. Every migration named
	// at or below it must have its file deleted.
	Floor string
}

Baseline - A schema snapshot that stands in for every migration up to its floor.

type DB

type DB struct {
	*bun.DB
	// MigrationDB is a second pool for migrations and the schema baseline load,
	// with no statement or read timeout and its own lock timeout. It is nil
	// when the driver does not provide one, then migrations run on DB.
	MigrationDB *bun.DB
}

func (*DB) Close added in v1.27.0

func (db *DB) Close() error

Close - Closes the migration pool, when there is one, and then the main pool

func (*DB) CreateExtensions

func (db *DB) CreateExtensions(extensions []string) error

CreateExtensions - Creates a database extension if it doesn't already exist. Extensions are a Postgres concept, so requesting any on another dialect is an error rather than a silent no-op.

func (*DB) CreateTables

func (db *DB) CreateTables(models []interface{}) error

CreateTables - Creates the database schema if it doesn't already exist

func (*DB) RegisterModels

func (db *DB) RegisterModels(models []interface{}) error

RegisterModels - Registers many-to-many relationship

func (*DB) ResetTables

func (db *DB) ResetTables(models []interface{}) error

ResetTables - Drops and recreates the database schema

func (*DB) RollbackLastMigration

func (db *DB) RollbackLastMigration(migrations *migrate.Migrations) error

RollbackLastMigration - Rolls back the last applied group of migrations. Each migration is unmarked only after its Down succeeds, so a failed Down stays applied. It returns nil when the set is empty or no registered migration is applied. On PostgreSQL it fails with ECONFLICT while another instance runs or rolls back migrations on the same database: after a wait the last group would be a different one than the caller meant to undo.

func (*DB) RunMigrations

func (db *DB) RunMigrations(migrations *migrate.Migrations) error

RunMigrations - Executes all pending migrations. Each one is marked as applied only after its Up succeeds, so a failed migration stays pending and the next start retries it: every migration must be safe to run twice. Rolling back is an explicit call to RollbackLastMigration. On PostgreSQL it first waits for any other instance running migrations on the same database to finish.

func (*DB) RunMigrationsFromBaseline added in v1.27.0

func (db *DB) RunMigrationsFromBaseline(baseline Baseline, migrations *migrate.Migrations) error

RunMigrationsFromBaseline - Loads the baseline into a fresh database, or checks an existing one is not behind it, then runs the pending migrations. It holds the migration lock from the check to the last migration, like RunMigrations.

type DBModel

type DBModel interface {
	// Validate checks the model for logical and business-rule correctness.
	Validate() error

	// Insert validates and inserts the model into the DB.
	Insert(ctx context.Context, db bun.IDB) error

	// Update validates and updates the model in the DB.
	Update(ctx context.Context, db bun.IDB) error

	// Delete removes the model from the DB.
	Delete(ctx context.Context, db bun.IDB) error
}

DBModel defines the minimal interface for a model that can be persisted.

type Option

type Option func(db *DB) error

func WithExtensions

func WithExtensions(extensions []string) Option

func WithRegistrableModels

func WithRegistrableModels(models []interface{}) Option

type PaginableModel

type PaginableModel interface {
	// GetCursor returns a stable, opaque value representing the record’s
	// position in the sorted result set.
	GetCursor() string

	// GetSortField returns the DB column used for ordering in pagination queries.
	GetSortField() string

	// GetSortValue returns the value of the sort field for this record.
	GetSortValue() interface{}

	// GetUniqueField returns the DB column that uniquely identifies this record.
	GetUniqueField() string

	// GetUniqueValue returns the value of the unique field for this record.
	GetUniqueValue() interface{}
}

PaginableModel defines the minimal interface for cursor-based pagination.

type Patch added in v1.32.0

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

Patch collects the columns a partial update writes, so a PATCH request changes only the fields the client sent. Writing the whole row instead puts back the stale value of every field another request changed since the row was loaded.

var patch relational.Patch
relational.SetColumn(&patch, "name", &org.Name, body.Name)
relational.SetNullableColumn(&patch, "phone", &org.Phone, body.Phone)
org.UpdatedAt = time.Now().UTC()
patch.Add("updated_at")
err := patch.Update(ctx, db, org)

Patch takes no lock, so it is right for fields that are independent of each other. A write whose new value or validity depends on the row's current state (a status transition, a counter, a rule across two fields that two requests could each change) needs the row locked and re-read in the same transaction instead: use Mutate or LockRow.

The zero value is an empty patch.

func (*Patch) Add added in v1.32.0

func (p *Patch) Add(column string)

Add records a column the caller set on the model itself (updated_at, a derived column, or a field taken from a pointer request field). A column added twice is recorded once. A caller who wants an empty body to write nothing adds its own columns only when len(patch.Columns()) > 0, and still calls Update, which returns the error of a null sent for a column that cannot hold one.

func (*Patch) Columns added in v1.32.0

func (p *Patch) Columns() []string

Columns returns the recorded columns in the order they were added.

func (*Patch) Update added in v1.32.0

func (p *Patch) Update(ctx context.Context, db bun.IDB, model interface{ Validate() error }) error

Update validates model and writes only the recorded columns of the row model's primary key names. It returns the patch's error first, if a null was sent for a column that cannot hold one. With no column recorded it returns nil without validating or querying. A row that no longer exists is ENOTFOUND.

Directories

Path Synopsis
internal
jsonfmt
Package jsonfmt holds the JSON formatter the compose dialects use in place of bun v1.3.0's, which mis-escapes the text \u0000 (https://github.com/uptrace/bun/issues/1472).
Package jsonfmt holds the JSON formatter the compose dialects use in place of bun v1.3.0's, which mis-escapes the text \u0000 (https://github.com/uptrace/bun/issues/1472).
pgtest
Package pgtest is test support for apps that test against a real PostgreSQL: it creates a uniquely named database per test run, empties it between tests, drops it at the end, and sweeps the databases left behind by runs that died.
Package pgtest is test support for apps that test against a real PostgreSQL: it creates a uniquely named database per test run, empties it between tests, drops it at the end, and sweeps the databases left behind by runs that died.

Jump to

Keyboard shortcuts

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