Help us shape the future of Go! Take the 2026 Go Developer Survey

orm

package
v0.0.0-...-946afca Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package orm is a small SQL-first ORM built on database/sql and sqlx.

It keeps SQL visible, supports MySQL and PostgreSQL, enforces tenant scope for models that declare a tenant field, and wires gcosid for primary keys.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound       = gableerrors.New("orm_not_found", "record not found").WithStatus(404)
	ErrTenantMismatch = gableerrors.New("orm_tenant_mismatch", "record tenant does not match the context").WithStatus(400)
	ErrIDRequired     = gableerrors.New("orm_id_required", "primary key is required").WithStatus(400)
	ErrInvalidModel   = gableerrors.New("orm_invalid_model", "invalid model").WithStatus(500)
)

Framework errors.

Functions

func SchemaSQL

func SchemaSQL(model *Model, dialect Dialect) (string, error)

SchemaSQL renders a CREATE TABLE IF NOT EXISTS statement for the model. It creates missing tables only; changes to an existing table are left to explicit migrations.

Types

type Cond

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

Cond is one WHERE condition. Raw bypasses column validation and is the caller's responsibility.

func Eq

func Eq(column string, value any) Cond

Eq matches column = value. A nil value renders IS NULL.

func Gt

func Gt(column string, value any) Cond

Gt matches column > value.

func In

func In(column string, values ...any) Cond

In matches column IN (values...). An empty list matches nothing.

func IsNull

func IsNull(column string) Cond

IsNull matches column IS NULL.

func Like

func Like(column, pattern string) Cond

Like matches column LIKE pattern. The caller supplies the wildcards.

func Lt

func Lt(column string, value any) Cond

Lt matches column < value.

func Ne

func Ne(column string, value any) Cond

Ne matches column <> value.

func Raw

func Raw(where string, args ...any) Cond

Raw injects a raw SQL fragment. Placeholders must match the dialect of the database the query runs on.

type DB

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

DB wraps sqlx with a dialect.

func New

func New(db *sqlx.DB, dialect Dialect) (*DB, error)

New wraps an existing sqlx.DB.

func Open

func Open(driver, dsn string) (*DB, error)

Open connects to a database and selects its dialect.

The driver may name a dialect rather than a registered database/sql driver: PostgreSQL is reached through pgx, whose stdlib adapter registers itself as "pgx", so "postgres", "postgresql" and "pg" all open the same driver.

func (*DB) AutoMigrate

func (db *DB) AutoMigrate(models ...*Model) error

AutoMigrate creates missing tables. It never alters or drops an existing table; schema evolution belongs to explicit migrations.

func (*DB) Configure

func (db *DB) Configure(maxOpen, maxIdle int)

Configure sets the connection pool bounds.

func (*DB) Dialect

func (db *DB) Dialect() Dialect

Dialect returns the configured dialect.

type Dialect

type Dialect interface {
	Name() string
	Placeholder(index int) string
	Quote(identifier string) string
	Pagination(limit, offset int) string
	Returning() bool
}

Dialect renders SQL for one database engine.

func DialectFor

func DialectFor(driver string) (Dialect, error)

DialectFor returns the dialect for a database/sql driver name.

func MySQL

func MySQL() Dialect

MySQL returns the MySQL dialect.

func Postgres

func Postgres() Dialect

Postgres returns the PostgreSQL dialect.

type Field

type Field struct {
	GoName     string
	Column     string
	Type       reflect.Type
	PK         bool
	Auto       bool
	Tenant     bool
	AutoCreate bool
	AutoUpdate bool
	Unique     bool
	SoftDelete bool
	Retain     bool
	Text       bool
	Size       int
}

Field is one mapped struct field.

type Model

type Model struct {
	Table      string
	Type       reflect.Type
	Fields     []Field
	Relations  []Relation
	PK         *Field
	Tenant     *Field
	AutoCreate *Field
	AutoUpdate *Field
	// SoftDelete and Retain are the two supported ways of hiding a deleted row:
	// a timestamp column that stops being NULL, or a flag that flips. Both are
	// excluded by default and reachable only through Unscoped.
	SoftDelete *Field
	Retain     *Field
}

Model is the table metadata inferred from a Go struct.

func ModelOf

func ModelOf[T any](table string) (*Model, error)

ModelOf returns the metadata for T mapped to table.

type Query

type Query[T any] struct {
	// contains filtered or unexported fields
}

Query is a typed query builder. Conditions are ANDed.

func (*Query[T]) All

func (q *Query[T]) All(ctx context.Context) ([]T, error)

All returns every matching row.

func (*Query[T]) Count

func (q *Query[T]) Count(ctx context.Context) (int64, error)

Count returns the number of matching rows.

func (*Query[T]) First

func (q *Query[T]) First(ctx context.Context) (*T, error)

First returns the first matching row or ErrNotFound.

func (*Query[T]) Limit

func (q *Query[T]) Limit(limit int) *Query[T]

Limit bounds the row count. Zero means no limit.

func (*Query[T]) Offset

func (q *Query[T]) Offset(offset int) *Query[T]

Offset skips rows.

func (*Query[T]) OrderBy

func (q *Query[T]) OrderBy(column string, desc bool) *Query[T]

OrderBy adds an order clause.

func (*Query[T]) Page

func (q *Query[T]) Page(number, size int) *Query[T]

Page applies 1-based pagination. A negative size means "no limit".

func (*Query[T]) Preload

func (q *Query[T]) Preload(name string, related relationLoader) *Query[T]

Preload loads a relation for the rows this query returns, in one extra query per relation rather than one per row.

The name is the struct field; the repository passed in is the related model's. A relation declared on the entity but not asked for here stays empty, and a name that does not exist is a query error instead of a silently skipped load. The related rows obey the same tenant and soft-delete guards as this query, including Unscoped.

func (*Query[T]) Unscoped

func (q *Query[T]) Unscoped() *Query[T]

Unscoped disables the tenant, soft-delete and retain predicates. Use it only for privileged reads and for writing a deleted row back.

func (*Query[T]) Where

func (q *Query[T]) Where(conds ...Cond) *Query[T]

Where adds AND conditions.

type Relation

type Relation struct {
	// Name is the struct field.
	Name string
	Kind RelationKind
	// Table is the related table.
	Table string
	// ForeignKey is the column that carries the key, wherever it lives: on the related
	// table for HasMany, on this table for BelongsTo.
	ForeignKey string
	// Many reports whether the field holds a slice.
	Many bool
}

Relation is a struct field that Preload fills.

A relation is metadata, not a column: the field is never part of a SELECT list, and loading it is always an explicit Preload on the query that asked for it — there is no lazy loading, so a handler cannot accidentally issue a query per row.

type RelationKind

type RelationKind string

RelationKind is the direction of a relation.

const (
	// HasMany links one row of this model to many rows of another: the other table
	// carries the foreign key.
	HasMany RelationKind = "has_many"
	// BelongsTo links this row to one row of another: this table carries the foreign key.
	BelongsTo RelationKind = "belongs_to"
)

type Repo

type Repo[T any] struct {
	// contains filtered or unexported fields
}

Repo binds a model to a database and optional id generator.

func NewRepo

func NewRepo[T any](db *DB, table string, opts ...RepoOption) (*Repo[T], error)

NewRepo returns a repository for T mapped to table.

func (*Repo[T]) Create

func (r *Repo[T]) Create(ctx context.Context, entity *T) error

Create inserts the row. A zero primary key is filled by the id generator (or by the database when the field is tagged auto). A tenant field is filled from the context and a conflicting value is rejected.

func (*Repo[T]) Delete

func (r *Repo[T]) Delete(ctx context.Context, id int64) error

Delete removes the row, or hides it when the model declares soft_delete or retain.

Both spellings mean the same thing to callers: the row stops appearing in scoped queries. Use Purge to remove it for good.

func (*Repo[T]) Get

func (r *Repo[T]) Get(ctx context.Context, id int64) (*T, error)

Get loads one row by primary key.

func (*Repo[T]) Model

func (r *Repo[T]) Model() *Model

Model returns the table metadata.

func (*Repo[T]) Purge

func (r *Repo[T]) Purge(ctx context.Context, id int64) error

Purge removes the row for good, ignoring soft_delete and retain. It is the explicit escape hatch for retention jobs and for tests; ordinary code should call Delete.

func (*Repo[T]) Query

func (r *Repo[T]) Query() *Query[T]

Query starts a tenant-scoped query.

func (*Repo[T]) Restore

func (r *Repo[T]) Restore(ctx context.Context, id int64) error

Restore clears soft_delete and retain on one row.

func (*Repo[T]) Unscoped

func (r *Repo[T]) Unscoped() *Query[T]

Unscoped starts a query without the tenant predicate.

func (*Repo[T]) Update

func (r *Repo[T]) Update(ctx context.Context, entity *T) error

Update writes every mutable column by primary key.

soft_delete and retain columns are owned by Delete and Restore: writing an entity that carries a stale timestamp must not resurrect or re-delete a row.

type RepoOption

type RepoOption func(*repoOptions)

RepoOption configures a Repo.

func WithIDGenerator

func WithIDGenerator(ids gcosid.Generator) RepoOption

WithIDGenerator sets the primary key generator used when the key is zero.

Jump to

Keyboard shortcuts

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