database

package
v0.1.12 Latest Latest
Warning

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

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

Documentation

Overview

Package database opens supported GORM SQL drivers and exposes driver metadata.

MapLoadError and MapPersistError classify GORM errors into D10 categories (not_found, conflict, internal) so generated handlers, admin, and auth share one unique-violation detector instead of three copies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Aggregate added in v0.1.12

func Aggregate(ctx context.Context, q *gorm.DB, specs []AggregateSpec) (map[string]types.Decimal, error)

Aggregate computes the requested aggregates over q — which already carries the list's filters and search — and returns a map keyed by each spec's Key ("<func>:<field>"). All aggregates are evaluated in a single SELECT with no GROUP BY, so the query returns exactly one row and runs before pagination.

Values are the database's own aggregate, encoded as a types.Decimal (a canonical JSON string). Precision therefore follows the driver: Postgres and MySQL compute SUM/AVG/MIN/MAX over numeric/decimal in fixed point, so a decimal SUM (and integer aggregates on every driver) is exact; SQLite has no native fixed-point aggregate and computes AVG — and SUM of a fractional decimal column — in IEEE double, so those may carry float rounding and can differ from Postgres/MySQL for the same data. AVG is fractional by nature (rounded to each driver's default scale) and is an approximation everywhere. See docs/contract.md "List query: numeric aggregates" for the per-driver contract. A NULL result — an aggregate over an empty matching set (SUM/AVG/ MIN/MAX all yield NULL) — becomes decimal zero, so a card renders 0 not null.

An empty specs list is a no-op (nil map, no query). Column names in specs are generator-controlled; nothing here is derived from user input.

func FilterEq added in v0.1.11

func FilterEq(ctx context.Context, q *gorm.DB, column string, kind FilterKind, raw string) (*gorm.DB, error)

FilterEq adds an exact-match `column = <raw coerced to kind>` predicate. An empty (or whitespace-only) raw value is a no-op — the filter was not supplied — so callers can chain one FilterEq per declared filterable field. A raw value that does not parse as kind returns a D10 validation error (422) keyed on column. The column name is generator-controlled (a declared field's DB column); only raw is user input.

func IsForeignKeyViolation added in v0.1.5

func IsForeignKeyViolation(err error) bool

IsForeignKeyViolation reports foreign-key constraint violations, such as a belongs_to reference to a row that does not exist. Same primary/fallback shape as IsUniqueViolation: all three supported dialectors translate their foreign-key error code to gorm.ErrForeignKeyViolated, with the driver error string as a fallback.

func IsNotNullViolation added in v0.1.5

func IsNotNullViolation(err error) bool

IsNotNullViolation reports NOT NULL constraint violations. Unlike unique and foreign-key violations, none of the SQLite/Postgres/MySQL GORM dialectors translate this case to a gorm sentinel error (it is a gap in gorm itself, not something Open configures around), so detection is driver-message-only. The three drivers phrase it differently: SQLite ("NOT NULL constraint failed"), Postgres ("violates not-null constraint"), MySQL ("cannot be null").

func IsUniqueViolation added in v0.1.3

func IsUniqueViolation(err error) bool

IsUniqueViolation reports duplicate-key errors. Open enables gorm.Config.TranslateError, so ErrDuplicatedKey is the primary signal on SQLite, Postgres, and MySQL (all three dialectors translate their unique- violation error code to it). The driver error string stays as a fallback for any dialect that does not implement translation.

func MapDeleteError added in v0.1.5

func MapDeleteError(ctx context.Context, err error, conflict, internal string) error

MapDeleteError maps a GORM delete error to a D10 category error. A foreign-key violation here means another row still references the one being deleted, which is a state conflict caused by other data, not an invalid value in the request (a delete has no body to validate) — the opposite meaning a foreign-key violation has on create/update, so this is deliberately not MapPersistError. Unique and NOT NULL violations cannot occur on delete, so there is no equivalent branch for them here.

func MapLoadError added in v0.1.3

func MapLoadError(ctx context.Context, err error, notFound, internal string) error

MapLoadError maps a GORM read/load error to a D10 category error: record-not-found becomes not_found; any other driver failure becomes internal. Unique/duplicate is not treated as conflict on load.

func MapPersistError added in v0.1.3

func MapPersistError(ctx context.Context, err error, conflict, internal string) error

MapPersistError maps a GORM write error to a D10 category error: unique/duplicate becomes conflict; a foreign-key or NOT NULL violation becomes validation, since both mean the client submitted a value that references or omits something invalid, not that the server failed; any other failure becomes internal.

func Ordering added in v0.1.11

func Ordering(ctx context.Context, q *gorm.DB, ordering string, allowed []string, fallback string) (*gorm.DB, error)

Ordering applies `ORDER BY` for a Django-style `?ordering=` value (optional leading '-' for DESC), validated against the allowed columns:

  • empty ordering: order by fallback (the stable default, e.g. "id") when fallback is non-empty; otherwise no ORDER BY is added.
  • ordering field not in allowed: a 422 validation error on "ordering".

allowed and fallback are generator-controlled (a resource's declared sortable columns); ordering is user input, matched by exact string equality so only a declared column can reach the query.

func ParseOrdering added in v0.1.11

func ParseOrdering(ordering string) (field string, desc bool)

ParseOrdering splits a Django-style `?ordering=` token into its field name and direction: a leading '-' means descending, e.g. "-created_at" → ("created_at", true). The admin data plane and generated list handlers share this spelling so the two contracts cannot drift.

func Search(q *gorm.DB, columns []string, term string) *gorm.DB

Search adds a case-insensitive OR of `LOWER(col) LIKE LOWER(%term%)` across the given columns. An empty (or whitespace-only) term, or an empty column list, is a no-op. The term is treated as a literal: LIKE wildcards in it are escaped, so a user searching for "50%" matches the literal text, not a prefix. Columns are generator-controlled; only the term is user input.

Types

type AggregateColumn added in v0.1.12

type AggregateColumn struct {
	Column string
}

AggregateColumn declares one field a resource exposes to aggregates: the DB column the SQL function is applied to. It is generator-controlled — built from a field declared `aggregatable` — so only a declared numeric column can reach the query.

type AggregateFunc added in v0.1.12

type AggregateFunc string

AggregateFunc is a supported SQL aggregate function.

const (
	AggSum AggregateFunc = "sum"
	AggAvg AggregateFunc = "avg"
	AggMin AggregateFunc = "min"
	AggMax AggregateFunc = "max"
)

type AggregateSpec added in v0.1.12

type AggregateSpec struct {
	Func   AggregateFunc
	Column string
	Key    string
}

AggregateSpec is one resolved aggregate request: Func over Column, returned in the response keyed as "<func>:<field>" (Key).

func ParseAggregates added in v0.1.12

func ParseAggregates(ctx context.Context, raw string, allowed map[string]AggregateColumn) ([]AggregateSpec, error)

ParseAggregates turns the raw `aggregate` query param — a comma-separated list of "<func>:<field>" pairs, e.g. "sum:total,avg:total" — into resolved specs.

  • An empty (or whitespace-only) raw value returns nil (no aggregates asked for), so a plain list request is unchanged.
  • func is matched case-insensitively against sum / avg / min / max.
  • field must be a key in allowed (a declared aggregatable field); the value supplies the DB column. allowed is generator-controlled; only raw is user input, so an undeclared field cannot reach the query.
  • A malformed pair, unknown func, or undeclared field returns a D10 validation error (422) keyed on "aggregate".
  • Duplicate pairs collapse to one spec (same key), so the response map has one entry per requested aggregate.

type Capabilities

type Capabilities struct {
	Transactions          bool
	Savepoints            bool
	ForeignKeyConstraints bool
	Returning             bool
	Upsert                bool
	AdvisoryLocks         bool
	ConcurrentIndexBuilds bool
}

Capabilities names behavior that differs across supported database drivers.

func CapabilitiesFor

func CapabilitiesFor(driver Driver) Capabilities

CapabilitiesFor returns the capability model for driver.

type DB

type DB struct {
	*gorm.DB
	// contains filtered or unexported fields
}

DB is an opened GORM database with Gombit driver metadata.

func Open

func Open(cfg config.DatabaseConfig) (*DB, error)

Open opens a GORM database for a supported config.DatabaseConfig.

func (*DB) Capabilities

func (db *DB) Capabilities() Capabilities

Capabilities returns the configured driver's capability flags.

func (*DB) Close

func (db *DB) Close() error

Close closes the underlying database/sql handle.

func (*DB) Driver

func (db *DB) Driver() Driver

Driver returns the configured driver.

func (*DB) SQLDB

func (db *DB) SQLDB() (*sql.DB, error)

SQLDB returns the underlying database/sql handle.

type Driver

type Driver string

Driver names a supported database driver.

const (
	// DriverSQLite identifies SQLite.
	DriverSQLite Driver = Driver(config.DatabaseDriverSQLite)
	// DriverPostgres identifies PostgreSQL.
	DriverPostgres Driver = Driver(config.DatabaseDriverPostgres)
	// DriverMySQL identifies MySQL.
	DriverMySQL Driver = Driver(config.DatabaseDriverMySQL)
)

type FilterKind added in v0.1.11

type FilterKind int

FilterKind is the column type an exact-match filter coerces its raw query string to before comparing. Filters arrive as strings (Huma does not support optional/pointer query params, so an empty string is the "absent" signal — the same convention the admin data plane uses), and the kind decides how the string is parsed and bound.

const (
	FilterString FilterKind = iota
	FilterInt
	FilterInt64
	FilterUint
	FilterBool
)

type ValidationError added in v0.1.7

type ValidationError struct {
	Message string
	Fields  map[string][]string
}

ValidationError is a domain validation failure raised by a model's Validate hook. MapPersistError maps it to a D10 422 validation_error, preserving the per-field messages.

func NewValidationError added in v0.1.7

func NewValidationError(message string, fields map[string][]string) *ValidationError

NewValidationError builds a domain ValidationError. Pass nil fields for a message-only error.

func (*ValidationError) Error added in v0.1.7

func (e *ValidationError) Error() string

Error satisfies the error interface.

type Validator added in v0.1.7

type Validator interface {
	Validate(ctx context.Context, tx *gorm.DB) error
}

Validator is implemented by models that enforce domain invariants — rules that cross fields, rows, or models, such as "ownership windows must not overlap" or "confirm a rental only if every child resource is available".

The framework runs Validate inside the write transaction on BOTH the API (generated handler Create/Save) and the admin data-plane write paths, via a GORM callback registered in Open. An invariant therefore has exactly one home and neither write surface can bypass it. tx is the transaction the write runs in, so Validate can query committed-but-in-flight state consistently.

Return a *ValidationError (see NewValidationError) to surface field-level D10 422 responses; any other error aborts the write and maps to 500.

Directories

Path Synopsis
Package conformance hosts the multi-DB conformance suite.
Package conformance hosts the multi-DB conformance suite.
models
Package models holds GORM fixtures for the multi-DB conformance suite.
Package models holds GORM fixtures for the multi-DB conformance suite.

Jump to

Keyboard shortcuts

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