Documentation
¶
Index ¶
- func LockRow(ctx context.Context, tx bun.Tx, model any) error
- func Mutate(ctx context.Context, db bun.IDB, model DBModel, ...) error
- func SetColumn[T any](p *Patch, column string, dst *T, value types.Optional[T])
- func SetNullableColumn[T any](p *Patch, column string, dst **T, value types.Optional[T])
- type Baseline
- type DB
- func (db *DB) Close() error
- func (db *DB) CreateExtensions(extensions []string) error
- func (db *DB) CreateTables(models []interface{}) error
- func (db *DB) RegisterModels(models []interface{}) error
- func (db *DB) ResetTables(models []interface{}) error
- func (db *DB) RollbackLastMigration(migrations *migrate.Migrations) error
- func (db *DB) RunMigrations(migrations *migrate.Migrations) error
- func (db *DB) RunMigrationsFromBaseline(baseline Baseline, migrations *migrate.Migrations) error
- type DBModel
- type Option
- type PaginableModel
- type Patch
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LockRow ¶ added in v1.32.0
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
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
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
Close - Closes the migration pool, when there is one, and then the main pool
func (*DB) CreateExtensions ¶
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 ¶
CreateTables - Creates the database schema if it doesn't already exist
func (*DB) RegisterModels ¶
RegisterModels - Registers many-to-many relationship
func (*DB) ResetTables ¶
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 ¶
func WithExtensions ¶
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
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
Columns returns the recorded columns in the order they were added.
func (*Patch) Update ¶ added in v1.32.0
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.
Source Files
¶
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. |