Documentation
¶
Index ¶
- Variables
- func Add[E any](s *Session, entities ...*E)
- func ClonePtr[V any](p *V) *V
- func CloneTimePtr(p *time.Time) *time.Time
- func IsUniqueViolation(err error) bool
- func JSONScan[T any](dst *T) sql.Scanner
- func JSONSnapshot(v any) []byte
- func JSONValue(v any) driver.Valuer
- func PtrEq[V comparable](a, b *V) bool
- func QueryNameFromContext(ctx context.Context) string
- func Register[E any](m Meta[E])
- func RegisterTable(def TableDef)
- func Remove[E any](s *Session, entities ...*E)
- func RunInTx(ctx context.Context, db DB, fn func(tx Tx) error) error
- func SQLTypeFor(dialect string, c ColumnDef) string
- func ScalarSnapshot(v driver.Valuer) any
- func TimePtrEq(a, b *time.Time) bool
- func UnregisterTable(name string)
- func WithQueryName(ctx context.Context, name string) context.Context
- type AfterLoader
- type AggExpr
- func Avg[E any](c AnyCol) AggExpr[E, float64]
- func Count[E any](c AnyCol) AggExpr[E, int64]
- func CountAll[E any]() AggExpr[E, int64]
- func CountDistinct[E any](c AnyCol) AggExpr[E, int64]
- func Max[E any, V comparable](c ColV[V]) AggExpr[E, V]
- func Min[E any, V comparable](c ColV[V]) AggExpr[E, V]
- func NewAgg[E any, V comparable](parts ...AggPart) AggExpr[E, V]
- func Sum[E any, V comparable](c ColV[V]) AggExpr[E, V]
- type AggPart
- type AnyCol
- type ArrayCol
- func (c ArrayCol[E, V]) ColName() string
- func (c ArrayCol[E, V]) Contains(vs ...V) Pred[E]
- func (c ArrayCol[E, V]) Has(v V) Pred[E]
- func (c ArrayCol[E, V]) IsNotNull() Pred[E]
- func (c ArrayCol[E, V]) IsNull() Pred[E]
- func (c ArrayCol[E, V]) Overlaps(vs ...V) Pred[E]
- func (c ArrayCol[E, V]) Set(vs []V) Assign[E]
- func (c ArrayCol[E, V]) SetNull() Assign[E]
- type Assign
- type BatchItem
- type BeforeSaver
- type BelongsTo
- type BytesCol
- type ChildOpt
- type Col
- func (c Col[E, V]) Asc() Order[E]
- func (c Col[E, V]) ColName() string
- func (c Col[E, V]) Desc() Order[E]
- func (c Col[E, V]) Eq(v V) Pred[E]
- func (c Col[E, V]) In(vs ...V) Pred[E]
- func (c Col[E, V]) IsNotNull() Pred[E]
- func (c Col[E, V]) IsNull() Pred[E]
- func (c Col[E, V]) Neq(v V) Pred[E]
- func (c Col[E, V]) NotIn(vs ...V) Pred[E]
- func (c Col[E, V]) Set(v V) Assign[E]
- func (c Col[E, V]) SetNull() Assign[E]
- type ColOf
- type ColOfV
- type ColV
- type ColumnDef
- type ConflictError
- type ConstraintError
- type ConstraintKind
- type DB
- type DeleteBuilder
- func (q DeleteBuilder[E]) AllRows() DeleteBuilder[E]
- func (q DeleteBuilder[E]) Exec(ctx context.Context) (int64, error)
- func (q DeleteBuilder[E]) Hard() DeleteBuilder[E]
- func (q DeleteBuilder[E]) Named(name string) DeleteBuilder[E]
- func (q DeleteBuilder[E]) ToSQL() (string, []any, error)
- func (q DeleteBuilder[E]) Where(ps ...Pred[E]) DeleteBuilder[E]
- type FromBuilder
- func (q FromBuilder[E]) GroupBy(cols ...ColOf[E]) FromBuilder[E]
- func (q FromBuilder[E]) Having(ps ...Pred[E]) FromBuilder[E]
- func (q FromBuilder[E]) Join(specs ...JoinSpec[E]) FromBuilder[E]
- func (q FromBuilder[E]) Limit(n int) FromBuilder[E]
- func (q FromBuilder[E]) Named(name string) FromBuilder[E]
- func (q FromBuilder[E]) Offset(n int) FromBuilder[E]
- func (q FromBuilder[E]) OrderBy(os ...Order[E]) FromBuilder[E]
- func (q FromBuilder[E]) Where(ps ...Pred[E]) FromBuilder[E]
- func (q FromBuilder[E]) WithDeleted() FromBuilder[E]
- type HasMany
- type HasOne
- type IncludeSpec
- type IndexDef
- type IndexPart
- type InstrumentFunc
- type JSONArr
- type JSONBool
- type JSONCol
- func (c JSONCol[E]) ColName() string
- func (c JSONCol[E]) Contains(v any) Pred[E]
- func (c JSONCol[E]) HasKey(key string) Pred[E]
- func (c JSONCol[E]) IsNotNull() Pred[E]
- func (c JSONCol[E]) IsNull() Pred[E]
- func (c JSONCol[E]) Path(path string) JSONPath[E]
- func (c JSONCol[E]) Set(v any) Assign[E]
- func (c JSONCol[E]) SetNull() Assign[E]
- type JSONNum
- func (j JSONNum[E, V]) Eq(v V) Pred[E]
- func (j JSONNum[E, V]) Gt(v V) Pred[E]
- func (j JSONNum[E, V]) Gte(v V) Pred[E]
- func (j JSONNum[E, V]) IsNotNull() Pred[E]
- func (j JSONNum[E, V]) IsNull() Pred[E]
- func (j JSONNum[E, V]) Lt(v V) Pred[E]
- func (j JSONNum[E, V]) Lte(v V) Pred[E]
- func (j JSONNum[E, V]) Neq(v V) Pred[E]
- type JSONPath
- type JSONStr
- type JoinOn
- type JoinSpec
- type ManyToMany
- func (r ManyToMany[E, C]) Any(preds ...Pred[C]) Pred[E]
- func (r ManyToMany[E, C]) Include(opts ...ChildOpt[C]) IncludeSpec[E]
- func (r ManyToMany[E, C]) Link(ctx context.Context, db DB, parent *E, children ...*C) error
- func (r ManyToMany[E, C]) Unlink(ctx context.Context, db DB, parent *E, children ...*C) error
- type Meta
- type Op
- type OrdCol
- type Order
- type Pred
- func And[E any](ps ...Pred[E]) Pred[E]
- func EqQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func GtQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func GteQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func InQuery[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func LtQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func LteQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func NeqQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func Not[E any](p Pred[E]) Pred[E]
- func NotInQuery[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
- func Or[E any](ps ...Pred[E]) Pred[E]
- type ProjQuery
- type QueryBuilder
- func (q QueryBuilder[E]) All(ctx context.Context) ([]*E, error)
- func (q QueryBuilder[E]) Count(ctx context.Context) (int64, error)
- func (q QueryBuilder[E]) ForUpdate() QueryBuilder[E]
- func (q QueryBuilder[E]) ForUpdateSkipLocked() QueryBuilder[E]
- func (q QueryBuilder[E]) Iter(ctx context.Context) iter.Seq2[*E, error]
- func (q QueryBuilder[E]) Limit(n int) QueryBuilder[E]
- func (q QueryBuilder[E]) Named(name string) QueryBuilder[E]
- func (q QueryBuilder[E]) Offset(n int) QueryBuilder[E]
- func (q QueryBuilder[E]) One(ctx context.Context) (*E, error)
- func (q QueryBuilder[E]) OnlyDeleted() QueryBuilder[E]
- func (q QueryBuilder[E]) OrderBy(os ...Order[E]) QueryBuilder[E]
- func (q QueryBuilder[E]) ToSQL() (string, []any)
- func (q QueryBuilder[E]) Where(ps ...Pred[E]) QueryBuilder[E]
- func (q QueryBuilder[E]) With(specs ...IncludeSpec[E]) QueryBuilder[E]
- func (q QueryBuilder[E]) WithDeleted() QueryBuilder[E]
- type RawQuery
- type Ref
- type Rows
- type SaveOp
- type ScalarCol
- func (c ScalarCol[E, V]) Asc() Order[E]
- func (c ScalarCol[E, V]) ColName() string
- func (c ScalarCol[E, V]) Desc() Order[E]
- func (c ScalarCol[E, V]) Eq(v V) Pred[E]
- func (c ScalarCol[E, V]) Gt(v V) Pred[E]
- func (c ScalarCol[E, V]) Gte(v V) Pred[E]
- func (c ScalarCol[E, V]) In(vs ...V) Pred[E]
- func (c ScalarCol[E, V]) IsNotNull() Pred[E]
- func (c ScalarCol[E, V]) IsNull() Pred[E]
- func (c ScalarCol[E, V]) Lt(v V) Pred[E]
- func (c ScalarCol[E, V]) Lte(v V) Pred[E]
- func (c ScalarCol[E, V]) Neq(v V) Pred[E]
- func (c ScalarCol[E, V]) NotIn(vs ...V) Pred[E]
- func (c ScalarCol[E, V]) Set(v V) Assign[E]
- func (c ScalarCol[E, V]) SetNull() Assign[E]
- type ScanError
- type SelectExpr
- type Session
- type Set
- func (s Set[E]) Add(entities ...*E)
- func (s Set[E]) All(ctx context.Context) ([]*E, error)
- func (s Set[E]) Count(ctx context.Context) (int64, error)
- func (s Set[E]) Find(ctx context.Context, pk any) (*E, error)
- func (s Set[E]) Iter(ctx context.Context) iter.Seq2[*E, error]
- func (s Set[E]) Limit(n int) QueryBuilder[E]
- func (s Set[E]) Named(name string) QueryBuilder[E]
- func (s Set[E]) NoTracking() QueryBuilder[E]
- func (s Set[E]) OrderBy(os ...Order[E]) QueryBuilder[E]
- func (s Set[E]) Query() QueryBuilder[E]
- func (s Set[E]) Remove(entities ...*E)
- func (s Set[E]) Where(ps ...Pred[E]) QueryBuilder[E]
- func (s Set[E]) With(specs ...IncludeSpec[E]) QueryBuilder[E]
- type StrCol
- type SubQ
- type TableDef
- type Tx
- type UpdateBuilder
- func (q UpdateBuilder[E]) AllRows() UpdateBuilder[E]
- func (q UpdateBuilder[E]) Exec(ctx context.Context) (int64, error)
- func (q UpdateBuilder[E]) Named(name string) UpdateBuilder[E]
- func (q UpdateBuilder[E]) Set(as ...Assign[E]) UpdateBuilder[E]
- func (q UpdateBuilder[E]) ToSQL() (string, []any, error)
- func (q UpdateBuilder[E]) Where(ps ...Pred[E]) UpdateBuilder[E]
- func (q UpdateBuilder[E]) WithDeleted() UpdateBuilder[E]
- type UpsertBuilder
- func (q UpsertBuilder[E]) DoNothing() UpsertBuilder[E]
- func (q UpsertBuilder[E]) DoUpdate(cols ...ColOf[E]) UpsertBuilder[E]
- func (q UpsertBuilder[E]) Exec(ctx context.Context) (int64, error)
- func (q UpsertBuilder[E]) Named(name string) UpsertBuilder[E]
- func (q UpsertBuilder[E]) OnConflict(cols ...ColOf[E]) UpsertBuilder[E]
- func (q UpsertBuilder[E]) Rows(es ...*E) UpsertBuilder[E]
- func (q UpsertBuilder[E]) ToSQL() (string, []any, error)
Constants ¶
This section is empty.
Variables ¶
var ErrCyclicGraph = errors.New("sorm: cyclic dependency between new entities")
ErrCyclicGraph — a cycle between new entities during SaveChanges (e.g. mutual references between two Added objects). Not supported in the MVP.
var ErrNotFound = errors.New("sorm: not found")
ErrNotFound — the single "not found" semantics across the entire API (One, future Find by PK).
Functions ¶
func Add ¶
Add registers new entities for INSERT. FKs of a new graph are set via navigation (p.Author = u) — the runtime fills in the FK column value after the parent is inserted.
func CloneTimePtr ¶
CloneTimePtr copies a *time.Time, dropping the monotonic component.
func IsUniqueViolation ¶
IsUniqueViolation — a shorthand check for a uniqueness violation.
func JSONScan ¶
JSONScan wraps a destination for reading a JSON column: it unmarshals []byte/string; SQL NULL leaves the destination at its zero value.
func JSONSnapshot ¶
JSONSnapshot marshals a value for snapshot diffing (json.Marshal is deterministic: map keys are sorted). nil-ish values snapshot as nil.
func JSONValue ¶
JSONValue wraps a value for writing into a JSON column: it marshals on Value(). A nil pointer/map/slice becomes SQL NULL.
func PtrEq ¶
func PtrEq[V comparable](a, b *V) bool
func QueryNameFromContext ¶
QueryNameFromContext returns the query name attached by WithQueryName or a builder's Named, or "" if none.
func RegisterTable ¶
func RegisterTable(def TableDef)
RegisterTable is called from the generated package's init().
func Remove ¶
Remove marks entities for DELETE. An entity must be tracked or have a populated PK (checked in SaveChanges).
func RunInTx ¶
RunInTx runs fn in a transaction: commit on nil, rollback on error. If the adapter classifies the error as transient (deadlock, serialization failure), the transaction is retried with exponential backoff — up to 3 retries. fn may be called multiple times: side effects outside the DB must be idempotent.
Sessions work naturally inside: NewSession(tx).SaveChanges flushes within this transaction without opening a nested one.
func SQLTypeFor ¶
SQLTypeFor — the column's SQL type for a dialect ("postgres","mysql","sqlite"). Single mapping point for the DDL generator and migrations.
func ScalarSnapshot ¶
ScalarSnapshot normalizes a custom scalar for snapshot diffing: the driver.Value is comparable after []byte → string (driver.Value is one of nil, bool, int64, float64, string, time.Time, []byte). Used by generated snapshot/diff code — the Go type itself does not have to be comparable (decimal.Decimal is not).
func UnregisterTable ¶
func UnregisterTable(name string)
UnregisterTable removes a table definition (mainly for migration tests).
Types ¶
type AfterLoader ¶ added in v0.4.0
AfterLoader is implemented by entities that post-process themselves after materialization (computed fields, decryption, ...).
type AggExpr ¶
type AggExpr[E any, V comparable] struct { // contains filtered or unexported fields }
AggExpr is an aggregate expression with value type V: comparisons yield Pred[E] for Having, As(...) yields a result column.
func CountDistinct ¶
CountDistinct — count(DISTINCT col); portable across all dialects.
func NewAgg ¶
func NewAgg[E any, V comparable](parts ...AggPart) AggExpr[E, V]
NewAgg assembles a custom aggregate expression from parts. E is the root entity of the projection; V is the result type used by Having comparisons.
// string_agg(name, ', ') — how pgagg.StringAgg is built:
sorm.NewAgg[E, string](
sorm.AggDialect("postgres"),
sorm.AggRaw("string_agg("), sorm.AggCol(col), sorm.AggRaw(", "),
sorm.AggArg(", "), sorm.AggRaw(")"),
)
type AggPart ¶
type AggPart struct {
// contains filtered or unexported fields
}
AggPart is one fragment of a custom aggregate expression.
func AggDialect ¶
AggDialect guards the expression: rendering on any other dialect is a build error returned when the query executes.
type AnyCol ¶
type AnyCol interface {
ColName() string
// contains filtered or unexported methods
}
AnyCol is a column of any entity. Used in projections after a JOIN ("relaxed mode"): table membership in the FROM/JOIN set is validated when the query is built.
type ArrayCol ¶
type ArrayCol[E any, V comparable] struct { // contains filtered or unexported fields }
ArrayCol is the descriptor of a native PostgreSQL array column (`sorm:"array"` on []string, []int64, ...). Array predicates render PG operators and are guarded: building them on another dialect is a build error returned when the query executes. A nil slice maps to NULL.
func NewArrayCol ¶
func NewArrayCol[E any, V comparable](table, name string) ArrayCol[E, V]
func (ArrayCol[E, V]) Contains ¶
Contains — col @> ARRAY[vs]: the column contains every listed element.
type Assign ¶
type Assign[E any] struct { // contains filtered or unexported fields }
Assign is a typed column assignment (Update.Set, future INSERT DSL).
type BatchItem ¶
type BatchItem struct {
SQL string
Args []any
// IDCount > 0: a multi-row INSERT with auto-PK for IDCount rows. The
// adapter obtains the ids via RETURNING (if the dialect supports it) or
// LastInsertId arithmetic (MySQL — first id of the batch, SQLite — last)
// and calls OnIDs with a slice of length IDCount in VALUES row order.
IDCount int
OnIDs func(ids []int64)
// Check is called with the number of affected rows (optimistic concurrency).
Check func(rowsAffected int64) error
}
BatchItem — a single statement of a write batch.
type BeforeSaver ¶ added in v0.4.0
BeforeSaver is implemented by entities that want a say before being inserted, updated or deleted by a session flush.
type BelongsTo ¶
type BelongsTo[C, P any] struct { // contains filtered or unexported fields }
BelongsTo — many-to-one relation descriptor (child → parent). Accessor functions are generated by `sorm gen`.
func NewBelongsTo ¶
func (BelongsTo[C, P]) Include ¶
func (r BelongsTo[C, P]) Include(opts ...ChildOpt[P]) IncludeSpec[C]
Include — eager loading of the parent: one query WHERE pk IN (children's fks) and distribution to children. Pred[P] options filter the parents; a child whose parent is filtered out keeps a nil navigation. IncludeSpec[P] options — nested loading of the parent's relations.
type BytesCol ¶
type BytesCol[E any] struct { // contains filtered or unexported fields }
BytesCol: []byte is not comparable, hence a separate descriptor without In (SQL `= $1` on bytea is valid; IN is semantically unnecessary).
func NewBytesCol ¶
type ChildOpt ¶
type ChildOpt[C any] interface { // contains filtered or unexported methods }
ChildOpt — an Include option: a child predicate (Pred[C]), child ordering (Order[C]), or a nested spec (IncludeSpec[C] — analogous to ThenInclude):
q.With(u.Posts.Include(
p.Title.HasPrefix("Go"), // child filter
p.CreatedAt.Desc(), // child order
p.Comments.Include(), // nested loading
))
type Col ¶
type Col[E any, V comparable] struct { // contains filtered or unexported fields }
Col is a column descriptor with equality comparisons. The descriptor type (Col/OrdCol/StrCol/BytesCol) is chosen by the generator based on the field's Go type. For nullable fields (*string etc.) the descriptor is generated for the base type plus IsNull/IsNotNull — predicates do not require pointers.
Eq(false) and Eq(0) are full-fledged conditions: predicates have no notion of a zero value.
type ColOf ¶
ColOf is a column descriptor of exactly entity E (for Field/GroupBy on the root table — with type inference).
type ColOfV ¶
type ColOfV[E any, V comparable] interface { ColOf[E] // contains filtered or unexported methods }
ColOfV is a column descriptor of E with a value type (for type-safe ColEq).
type ColV ¶
type ColV[V comparable] interface { AnyCol // contains filtered or unexported methods }
ColV is a column of any entity with a known value type (aggregates and ColEq stay typed over V).
type ColumnDef ¶
type ColumnDef struct {
Name string
GoKind string // "bool","string","int*","uint*","float32/64","time","bytes"
Nullable bool
Unique bool
PK bool
Auto bool
SQLType string // override from the type: tag
RefTable string // FK: target table
RefCol string // FK: target column
}
ColumnDef — a column description.
type ConflictError ¶
ConflictError — optimistic concurrency: an UPDATE/DELETE affected 0 rows, meaning the row was changed or deleted concurrently after loading.
func (*ConflictError) Error ¶
func (e *ConflictError) Error() string
type ConstraintError ¶
type ConstraintError struct {
Kind ConstraintKind
Constraint string // constraint/column name, if the driver reported it
Err error
}
ConstraintError — a DB constraint violation translated by the driver adapter into a typed error: a handler can tell "email taken" (409) from a failure (500) without parsing driver codes.
var ce *sorm.ConstraintError
if errors.As(err, &ce) && ce.Kind == sorm.ConstraintUnique { ... }
func (*ConstraintError) Error ¶
func (e *ConstraintError) Error() string
func (*ConstraintError) Unwrap ¶
func (e *ConstraintError) Unwrap() error
type ConstraintKind ¶
type ConstraintKind int
ConstraintKind — the kind of violated DB constraint.
const ( ConstraintUnique ConstraintKind = iota + 1 ConstraintForeignKey ConstraintNotNull ConstraintCheck )
func (ConstraintKind) String ¶
func (k ConstraintKind) String() string
type DB ¶
type DB interface {
Dialect() dialect.Dialect
Query(ctx context.Context, sql string, args ...any) (Rows, error)
// Exec returns the number of affected rows.
Exec(ctx context.Context, sql string, args ...any) (int64, error)
// ExecBatch executes statements in order: pgx — in one roundtrip
// (pgx.Batch), database/sql — sequentially on the current connection.
ExecBatch(ctx context.Context, items []BatchItem) error
Begin(ctx context.Context) (Tx, error)
}
DB — sorm's driver abstraction. The runtime does not depend on a specific driver: the adapters are sorm/driver/pgxd (pgx, PostgreSQL) and sorm/driver/sqld (database/sql: MySQL, SQLite).
func InSchema ¶
InSchema binds a connection to a database schema: every table rendered by sorm becomes schema-qualified ("billing"."orders"). Models stay schema-agnostic — the same entities can live in different schemas via different wrappers over one pool (per-schema multi-tenancy):
db := sorm.InSchema(pgxd.Wrap(pool), "billing") c := sormgen.NewContext(db) // the context inherits the schema
On MySQL a "schema" is a database name (`billing`.`orders`); on SQLite it is an attached database name. Raw/RawAs SQL is the caller's text and is not rewritten. For migrations see migrate.WithSchema.
func Instrument ¶
func Instrument(db DB, fn InstrumentFunc) DB
Instrument wraps a DB with a middleware function. Works with any adapter; transactions are instrumented with the same fn.
db = sorm.Instrument(db, func(ctx context.Context, op sorm.Op, next func(context.Context) error) error {
start := time.Now()
err := next(ctx)
slog.Info("sql", "kind", op.Kind, "sql", op.SQL, "dur", time.Since(start), "err", err)
return err
})
func Primary ¶ added in v0.5.0
Primary returns the write connection behind db: unwraps WithReplicas through any wrapper stack; plain connections come back unchanged.
func Replica ¶ added in v0.5.0
Replica returns a read replica behind db (round-robin); plain connections come back unchanged.
func WithReplicas ¶ added in v0.5.0
WithReplicas splits reads and writes: untracked SELECTs go to the replicas round-robin, everything else stays on the primary.
db := sorm.WithReplicas(pgxd.Wrap(primary),
pgxd.Wrap(replica1), pgxd.Wrap(replica2))
Routing rules:
- Query (untracked reads) → next replica
- Exec / ExecBatch / Begin / RunInTx → primary
- Sessions and generated Contexts → primary entirely (read-your- writes: a tracked snapshot from a lagging replica would produce stale diffs and false version conflicts)
- ForUpdate / ForUpdateSkipLocked → primary (locks on a replica are meaningless)
Explicit overrides: sorm.Primary(db) pins the primary for a single query, sorm.Replica(db) pins a replica (both are no-ops for plain connections). Health checking and failover are the pool's job — the resolver only routes.
Composes with other wrappers; recommended order is instrumentation outside, InSchema in the middle, the resolver inside.
type DeleteBuilder ¶
type DeleteBuilder[E any] struct { // contains filtered or unexported fields }
func Delete ¶
func Delete[E any](db DB) DeleteBuilder[E]
Delete is a set-based DELETE. Same rules: without Where, AllRows() is required.
func (DeleteBuilder[E]) AllRows ¶
func (q DeleteBuilder[E]) AllRows() DeleteBuilder[E]
func (DeleteBuilder[E]) Hard ¶ added in v0.4.0
func (q DeleteBuilder[E]) Hard() DeleteBuilder[E]
Hard forces a real DELETE for soft-delete entities: the rows are gone, including already soft-deleted ones matching the predicates (purge).
func (DeleteBuilder[E]) Named ¶
func (q DeleteBuilder[E]) Named(name string) DeleteBuilder[E]
Named labels the statement for instrumentation (sorm.query.name).
func (DeleteBuilder[E]) Where ¶
func (q DeleteBuilder[E]) Where(ps ...Pred[E]) DeleteBuilder[E]
type FromBuilder ¶
type FromBuilder[E any] struct { // contains filtered or unexported fields }
func From ¶
func From[E any](db DB) FromBuilder[E]
From starts a projection query from the table of entity E.
func (FromBuilder[E]) GroupBy ¶
func (q FromBuilder[E]) GroupBy(cols ...ColOf[E]) FromBuilder[E]
func (FromBuilder[E]) Having ¶
func (q FromBuilder[E]) Having(ps ...Pred[E]) FromBuilder[E]
func (FromBuilder[E]) Join ¶
func (q FromBuilder[E]) Join(specs ...JoinSpec[E]) FromBuilder[E]
Join adds JOIN specifications (created by relation methods or by the free functions LeftJoinOn/InnerJoinOn/CrossJoin).
func (FromBuilder[E]) Limit ¶
func (q FromBuilder[E]) Limit(n int) FromBuilder[E]
func (FromBuilder[E]) Named ¶
func (q FromBuilder[E]) Named(name string) FromBuilder[E]
Named labels the projection for instrumentation (sorm.query.name).
func (FromBuilder[E]) Offset ¶
func (q FromBuilder[E]) Offset(n int) FromBuilder[E]
func (FromBuilder[E]) OrderBy ¶
func (q FromBuilder[E]) OrderBy(os ...Order[E]) FromBuilder[E]
func (FromBuilder[E]) Where ¶
func (q FromBuilder[E]) Where(ps ...Pred[E]) FromBuilder[E]
func (FromBuilder[E]) WithDeleted ¶ added in v0.4.0
func (q FromBuilder[E]) WithDeleted() FromBuilder[E]
WithDeleted disables the ROOT table soft-delete filter. Joined tables are not filtered implicitly — add explicit ON/WHERE predicates there.
type HasMany ¶
type HasMany[E, C any] struct { // contains filtered or unexported fields }
HasMany — one-to-many relation descriptor. It knows both types, so every "two-type" operation (Include, Any) is a method on it: builder methods cannot introduce new type parameters.
Accessor functions are generated by `sorm gen` — the runtime uses no reflection.
func NewHasMany ¶
func (HasMany[E, C]) Any ¶
Any filters the PARENT by its children: EXISTS (SELECT 1 FROM children WHERE fk = parent.pk AND preds). Returns a Pred[E]; feed it into a regular Where.
func (HasMany[E, C]) Include ¶
func (r HasMany[E, C]) Include(opts ...ChildOpt[C]) IncludeSpec[E]
Include — eager loading of children (split strategy: a separate query WHERE fk IN (pks) and in-memory distribution to parents). Options: Pred[C] (child filter), Order[C] (their order), IncludeSpec[C] (nested loading — analogous to ThenInclude).
func (HasMany[E, C]) InnerJoin ¶
InnerJoin is an INNER JOIN of the child table over the relation's FK.
type HasOne ¶
type HasOne[E, C any] struct { // contains filtered or unexported fields }
HasOne — one-to-one relation (FK on the child side C, *C navigation on E). A missing child after Include is indistinguishable from "not loaded" (the pointer stays nil) — unlike hasMany, where an empty slice ≠ nil.
func (HasOne[E, C]) Include ¶
func (r HasOne[E, C]) Include(opts ...ChildOpt[C]) IncludeSpec[E]
Include — eager loading of the single child (WHERE fk IN (pks)).
type IncludeSpec ¶
type IncludeSpec[E any] struct { // contains filtered or unexported fields }
IncludeSpec — an eager loading specification closed over the parent entity; created by relation descriptor methods, executed by the builder.
type IndexDef ¶
type IndexDef struct {
Name string
Columns []string // simple ASC columns (equivalent to Parts without Desc/Expr)
Parts []IndexPart // extended form: sort order and expressions
Unique bool
Type string // index type: "gin"/"brin" (PG, USING), "fulltext" (MySQL)
Where string // partial index (PG, SQLite); raw SQL condition
}
IndexDef — a table index. Simple (including composite) indexes are declared with `index:`/`uniqueIndex:` tags; custom ones via an optional model method:
func (Post) Indexes() []sorm.IndexDef {
return []sorm.IndexDef{
{Name: "idx_posts_fts", Type: "gin",
Parts: []sorm.IndexPart{{Expr: "to_tsvector('russian', title)"}}},
{Name: "idx_posts_recent",
Parts: []sorm.IndexPart{{Column: "created_at", Desc: true}},
Where: "views > 0"},
}
}
`sorm gen` merges the tags and the method into the TableDef.
func (IndexDef) IndexParts ¶
IndexParts — the normalized list of index elements (for generators).
type IndexPart ¶
type IndexPart struct {
Column string // column name (mutually exclusive with Expr)
Expr string // raw expression: to_tsvector(...), lower(email), ...
Desc bool
}
IndexPart — an index element: a column or an expression.
type InstrumentFunc ¶
InstrumentFunc wraps every DB operation: logging, metrics, tracing (an OpenTelemetry span around next). It must call next exactly once and return its error (possibly wrapped).
type JSONArr ¶
type JSONArr[E any] struct { // contains filtered or unexported fields }
JSONArr is a typed accessor for an array JSON field.
func NewJSONArr ¶
type JSONBool ¶
type JSONBool[E any] struct { // contains filtered or unexported fields }
JSONBool is a typed accessor for a boolean JSON field.
func NewJSONBool ¶
type JSONCol ¶
type JSONCol[E any] struct { // contains filtered or unexported fields }
JSONCol is the descriptor of a JSON column with content predicates. Dialect support: Path works everywhere; Contains — PostgreSQL (@>) and MySQL (JSON_CONTAINS), a build error on SQLite; HasKey works everywhere.
func NewJSONCol ¶
func (JSONCol[E]) Contains ¶
Contains reports whether the column's JSON document contains v (PostgreSQL @>, MySQL JSON_CONTAINS). Not available on SQLite.
func (JSONCol[E]) Path ¶
Path addresses a nested value with dot notation ("a.b.c"). Extraction is textual: comparisons behave like string comparisons of the unquoted value. Segments must match [A-Za-z0-9_]+ — anything else is a build error surfaced when the query executes.
type JSONNum ¶
JSONNum is a typed accessor for a numeric JSON field. Comparisons are numeric on every dialect: PG casts the text extraction to numeric, MySQL and SQLite compare the extracted value natively.
type JSONPath ¶
type JSONPath[E any] struct { // contains filtered or unexported fields }
JSONPath is a dot-notation path into a JSON column.
type JSONStr ¶
type JSONStr[E any] struct { // contains filtered or unexported fields }
JSONStr is a typed accessor for a string JSON field.
func NewJSONStr ¶
type JoinOn ¶
type JoinOn[C, E any] struct { // contains filtered or unexported fields }
JoinOn is the join condition between C (being joined) and E (already in the query). The column value types must match — enforced by the compiler.
type JoinSpec ¶
type JoinSpec[E any] struct { // contains filtered or unexported fields }
type ManyToMany ¶
type ManyToMany[E, C any] struct { // contains filtered or unexported fields }
ManyToMany — many-to-many relation through an implicit join table (`many2many:join_table` tag). Reads via Include/Any; writes via explicit Link/Unlink: sorm does not guess collection diffs, linking is an operation.
func NewManyToMany ¶
func NewManyToMany[E, C any]( joinTable, parentCol, childCol string, initSlice func(*E), appendChild func(*E, *C), ) ManyToMany[E, C]
func (ManyToMany[E, C]) Any ¶
func (r ManyToMany[E, C]) Any(preds ...Pred[C]) Pred[E]
Any filters E by related C: EXISTS over the join table; preds narrow C with a correlated subquery.
func (ManyToMany[E, C]) Include ¶
func (r ManyToMany[E, C]) Include(opts ...ChildOpt[C]) IncludeSpec[E]
Include — eager loading of related C: pairs from the join table + loading children with a single IN query (chunked), then distribution to parents. Order[C] options set the order of children within each parent.
type Meta ¶
type Meta[E any] struct { Table string PK string // PK column name Auto bool // PK is DB-generated (identity) → INSERT ... RETURNING VersionCol string // "" — no optimistic concurrency SelectCols []string // order matches Scan InsertCols []string // without auto-PK; order matches InsertValues // Scan returns pointers to fields in SelectCols order. Scan func(*E) []any // InsertValues returns values in InsertCols order. InsertValues func(*E) []any // ValuesFor returns column values by SelectCols indexes (bridge diff → UPDATE). ValuesFor func(*E, []int) []any // Snapshot/Diff — snapshot tracking (PR #4). The snapshot is a typed // generated struct, boxed at the meta boundary. Snapshot func(*E) any Diff func(any, *E) []int // SetPK sets the auto-PK after RETURNING. SetPK func(*E, int64) // PKValue — the PK value (identity map key). PKValue func(*E) any // GetVersion/SetVersion — only when VersionCol != "". GetVersion func(*E) int64 SetVersion func(*E, int64) // SoftDeleteCol — the column of `sorm:"softDelete"` (a *time.Time); // "" disables soft deletion. When set: queries filter the column // IS NULL, session Remove and set-based Delete become UPDATEs. SoftDeleteCol string // SetDeleted stamps the soft-delete field (only when SoftDeleteCol != ""). SetDeleted func(*E, time.Time) // TouchCreate/TouchUpdate — auto-timestamps (sorm:"autoCreate"/"autoUpdate"). // Nil when the entity has no such fields. TouchCreate stamps zero-valued // autoCreate fields on INSERT (a manually set value wins); TouchUpdate // stamps autoUpdate fields on INSERT and on every effective UPDATE. TouchCreate func(*E, time.Time) TouchUpdate func(*E, time.Time) // Refs — belongsTo navigations of this entity: edges for per-instance // toposort and FK fixup of new graphs. Refs []Ref[E] // RefTables — tables referenced by FKs (deletion order). RefTables []string }
Meta describes an entity to the runtime. In production code it is generated by `sorm gen`; handwritten meta is fine for tests and transition periods. All functions are per-field, no reflection — the only runtime reflection is a single type lookup in the registry when building a query.
type Op ¶
type Op struct {
Kind string // "query" | "exec" | "batch" | "begin" | "commit" | "rollback"
SQL string // query text ("" for begin/commit/rollback; first statement for batch)
Args []any
// Statements — all statements of the batch (Kind == "batch").
Statements []string
}
Op — an operation description for instrumentation.
type OrdCol ¶
type OrdCol[E any, V comparable] struct{ Col[E, V] }
OrdCol adds ordered comparisons. The constraint is comparable, not cmp.Ordered: time.Time is ordered in SQL but not Ordered in Go; the generator guarantees correct selection (OrdCol only for numbers, strings, and time.Time).
type Order ¶
type Order[E any] struct { // contains filtered or unexported fields }
Order is a sort element, closed over the entity.
type Pred ¶
type Pred[E any] struct { // contains filtered or unexported fields }
Pred is an immutable condition parameterized by the entity: a condition on one entity cannot be passed into a query on another (compile-time error). agg=true means the condition contains an aggregate and is only valid in Having.
func EqQ ¶ added in v0.4.0
func EqQ[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
Scalar comparisons: col <op> (SELECT ...). The subquery must yield a single row (usually an aggregate) — more rows is a database error.
func InQuery ¶ added in v0.4.0
func InQuery[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
InQuery — col IN (SELECT ...). Value types must match.
func NotInQuery ¶ added in v0.4.0
func NotInQuery[E any, V comparable](c ColOfV[E, V], sub SubQ[V]) Pred[E]
NotInQuery — col NOT IN (SELECT ...). Beware NULLs in the subquery: SQL's NOT IN over a set containing NULL matches nothing.
type ProjQuery ¶
type ProjQuery[R any] struct { // contains filtered or unexported fields }
func Project ¶
func Project[R any, E any](q FromBuilder[E], exprs ...SelectExpr[E]) ProjQuery[R]
Project performs a projection into struct R. The match between expression aliases and R's fields (`sorm:` tag or snake_case) is checked strictly before the query.
type QueryBuilder ¶
type QueryBuilder[E any] struct { // contains filtered or unexported fields }
func Query ¶
func Query[E any](db DB) QueryBuilder[E]
Query starts a typed query over entity E. The builder is immutable: every method returns a copy, so reusing a base builder is safe (no state leaks between queries).
func Track ¶
func Track[E any](s *Session) QueryBuilder[E]
Track is the same query builder, but materialized entities go into the tracker. Reloading the same row returns the already-tracked pointer; database data does not overwrite local changes (EF semantics).
func (QueryBuilder[E]) All ¶
func (q QueryBuilder[E]) All(ctx context.Context) ([]*E, error)
All runs the query without tracking. An empty result is an empty slice, nil error.
func (QueryBuilder[E]) ForUpdate ¶ added in v0.4.0
func (q QueryBuilder[E]) ForUpdate() QueryBuilder[E]
ForUpdate locks the selected rows until the transaction ends (SELECT ... FOR UPDATE). Meaningful inside RunInTx / an open Tx; PostgreSQL and MySQL only — building on SQLite is an error.
func (QueryBuilder[E]) ForUpdateSkipLocked ¶ added in v0.4.0
func (q QueryBuilder[E]) ForUpdateSkipLocked() QueryBuilder[E]
ForUpdateSkipLocked locks the selected rows, silently skipping rows already locked by other transactions — the standard queue-worker pattern (PostgreSQL / MySQL 8+).
func (QueryBuilder[E]) Iter ¶
Iter streams the result: rows are yielded as they are read, without loading the whole result set into memory. Incompatible with With (eager loading requires the full set of parents) — in that case the iterator yields a single error.
for u, err := range sorm.Query[models.User](db).Iter(ctx) {
if err != nil { return err }
...
}
func (QueryBuilder[E]) Limit ¶
func (q QueryBuilder[E]) Limit(n int) QueryBuilder[E]
func (QueryBuilder[E]) Named ¶
func (q QueryBuilder[E]) Named(name string) QueryBuilder[E]
Named labels the query for instrumentation: spans and metrics carry it as sorm.query.name. Equivalent to wrapping the context in WithQueryName (an explicit WithQueryName on the context wins over Named).
func (QueryBuilder[E]) Offset ¶
func (q QueryBuilder[E]) Offset(n int) QueryBuilder[E]
func (QueryBuilder[E]) One ¶
func (q QueryBuilder[E]) One(ctx context.Context) (*E, error)
One returns the first row or ErrNotFound.
func (QueryBuilder[E]) OnlyDeleted ¶ added in v0.4.0
func (q QueryBuilder[E]) OnlyDeleted() QueryBuilder[E]
OnlyDeleted returns exclusively soft-deleted rows (a trash-bin view).
func (QueryBuilder[E]) OrderBy ¶
func (q QueryBuilder[E]) OrderBy(os ...Order[E]) QueryBuilder[E]
func (QueryBuilder[E]) ToSQL ¶
func (q QueryBuilder[E]) ToSQL() (string, []any)
ToSQL returns the final SQL and arguments — inspection instead of magic.
func (QueryBuilder[E]) Where ¶
func (q QueryBuilder[E]) Where(ps ...Pred[E]) QueryBuilder[E]
Where adds conditions; multiple Where calls and multiple arguments are ANDed.
func (QueryBuilder[E]) With ¶
func (q QueryBuilder[E]) With(specs ...IncludeSpec[E]) QueryBuilder[E]
With adds eager loading of relations (specs are created by the Include method on relation descriptors: gen.User.Posts.Include(...)).
func (QueryBuilder[E]) WithDeleted ¶ added in v0.4.0
func (q QueryBuilder[E]) WithDeleted() QueryBuilder[E]
WithDeleted disables the soft-delete filter: the query sees every row, deleted or not. No-op for entities without a softDelete field.
type RawQuery ¶
type RawQuery[T any] struct { // contains filtered or unexported fields }
func Raw ¶
Raw is the escape hatch into raw SQL with scanning into entities via meta. Matching is strict by column name: any mismatch yields a *ScanError with the lists, not silently skipped fields.
func RawAs ¶
RawAs is raw SQL with scanning into an arbitrary struct R (aggregates, CTEs, window functions — anything that is not the shape of an entity). Mapping: the `sorm:"col"` tag or snake_case of the field name. The scan plan is built once per type and cached.
type Ref ¶
type Ref[E any] struct { FKCol string NotNull bool Nav func(*E) any NavPK func(*E) any // SetFK sets the FK from the parent's PK (fixup after the parent is inserted). SetFK func(*E, any) // FKIsZero — the FK column was not set manually. FKIsZero func(*E) bool }
Ref — a many-to-one navigation reference generated for an FK column.
type SaveOp ¶ added in v0.4.0
type SaveOp int
SaveOp tells a BeforeSave hook which operation is being planned.
type ScalarCol ¶
ScalarCol is the descriptor of a custom-scalar column: a named Go type implementing driver.Valuer + sql.Scanner (decimal.Decimal, money types, encrypted strings). V is intentionally unconstrained — the type does not have to be Go-comparable; every comparison happens in SQL, where the column's real type (numeric, ...) is ordered.
type ScanError ¶
type ScanError struct {
Missing []string // result columns that have no destination
Extra []string // expected but absent from the result
}
ScanError — a mismatch between result columns and destination fields (Raw/RawAs/Project).
type SelectExpr ¶
type SelectExpr[E any] struct { // contains filtered or unexported fields }
SelectExpr is a column of the projection result.
func As ¶
func As[E any, V comparable](a AggExpr[E, V], alias string) SelectExpr[E]
As is an aggregate with an alias.
func Field ¶
func Field[E any](c ColOf[E]) SelectExpr[E]
Field is a column of the root entity (E is inferred); the result name = the column name.
func FieldAs ¶
func FieldAs[E any](c ColOf[E], alias string) SelectExpr[E]
FieldAs is a column with an alias (e.g. on a name collision after a JOIN).
func FieldOf ¶
func FieldOf[E any](c AnyCol) SelectExpr[E]
FieldOf is a column of a joined entity ("relaxed mode", E is explicit; table membership is validated at build time).
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session — Unit of Work: identity map + snapshot change tracking. Entities loaded via Track are mutated with plain Go code; SaveChanges computes the minimal diff and applies it in batches within a single transaction.
Not thread-safe (like DbContext in EF); lives for one unit of work.
func NewSession ¶
func (*Session) SaveChanges ¶
SaveChanges opens a transaction, applies the diff, and commits. Order: DELETE (children before parents) → UPDATE (changed columns only) → INSERT by dependency level (RETURNING → FK fixup of children). DELETE+UPDATE go in one pgx.Batch (single roundtrip), each insert level in another.
type Set ¶
type Set[E any] struct { // contains filtered or unexported fields }
Set is typed access to one entity within a unit of work — the EF Core DbSet analog. Queries started from a set are tracked: materialized entities land in the session's identity map, plain Go mutations are picked up by SaveChanges. The generated Context wires a set per entity.
c := sormgen.NewContext(db)
users, err := c.Users.Where(u.Age.Gt(18)).All(ctx)
users[0].Name = "new" // tracked — no manual Track
c.Orders.Add(&models.Order{...})
err = c.SaveChanges(ctx)
func NewSet ¶
NewSet binds a set to a session. Normally called by the generated NewContext, not by hand.
func (Set[E]) Add ¶
func (s Set[E]) Add(entities ...*E)
Add registers new entities for INSERT on the session.
func (Set[E]) Find ¶
Find returns the entity by primary key: first from the identity map (no SQL if already tracked — EF Find semantics), otherwise a tracked SELECT by PK. ErrNotFound if the row does not exist.
func (Set[E]) Limit ¶
func (s Set[E]) Limit(n int) QueryBuilder[E]
func (Set[E]) Named ¶
func (s Set[E]) Named(name string) QueryBuilder[E]
func (Set[E]) NoTracking ¶
func (s Set[E]) NoTracking() QueryBuilder[E]
NoTracking starts an untracked read-only query: no snapshots, no identity map — cheaper for large result sets you won't mutate.
func (Set[E]) OrderBy ¶
func (s Set[E]) OrderBy(os ...Order[E]) QueryBuilder[E]
func (Set[E]) Query ¶
func (s Set[E]) Query() QueryBuilder[E]
Query starts a tracked query (identity map, EF semantics: a reloaded row returns the already-tracked pointer, local changes win).
func (Set[E]) Remove ¶
func (s Set[E]) Remove(entities ...*E)
Remove marks entities for DELETE on the session.
func (Set[E]) Where ¶
func (s Set[E]) Where(ps ...Pred[E]) QueryBuilder[E]
Shortcuts to Query() so a set reads like a query root.
func (Set[E]) With ¶
func (s Set[E]) With(specs ...IncludeSpec[E]) QueryBuilder[E]
type StrCol ¶
StrCol adds string predicates. HasPrefix/HasSuffix/Contains arguments are literals, LIKE special characters are escaped; Like/ILike take a ready-made pattern.
type SubQ ¶ added in v0.4.0
type SubQ[V comparable] struct { // contains filtered or unexported fields }
Typed subqueries. SubQ[V] is a subquery producing values of type V — the compiler will not let an int64 subquery meet a string column. Arguments of the outer query and the subquery share one placeholder sequence: everything renders into a single writer.
// users who authored a flagged post: id IN (SELECT author_id FROM posts WHERE ...) flagged := sorm.Pick(p.AuthorID, sorm.Query[models.Post](nil).Where(p.Flagged.Eq(true))) users, err := sorm.Query[models.User](db).Where(sorm.InQuery(u.ID, flagged)).All(ctx) // balance above the average: balance > (SELECT avg(balance) FROM users) avg := sorm.PickScalar(sorm.Avg[models.User](u.Balance), sorm.Query[models.User](nil)) rich, err := sorm.Query[models.User](db).Where(sorm.GtQ(u.Balance, avg)).All(ctx)
The inner builder is usually created with a nil db — rendering is dialect-driven by the OUTER query, so the same subquery value works on any connection. Correlated subqueries (referencing the outer row) are covered by relation predicates (Any/None/Is); Pick/PickScalar are for самостоятельные подзапросы.
func Pick ¶ added in v0.4.0
func Pick[E any, V comparable](c ColOfV[E, V], q QueryBuilder[E]) SubQ[V]
Pick selects a single typed column from a query — the IN-subquery shape. ORDER BY/LIMIT/OFFSET of the builder are preserved if set.
func PickScalar ¶ added in v0.4.0
func PickScalar[E any, V comparable](a AggExpr[E, V], q QueryBuilder[E]) SubQ[V]
PickScalar selects a single aggregate value — the scalar-subquery shape for comparisons: avg, max, count(*), ...
type TableDef ¶
TableDef — a dialect-neutral description of an entity's table. Generated by `sorm gen` and registered alongside the meta; used by migrations (sorm/migrate, `sorm schema`) as the source of the desired schema.
type UpdateBuilder ¶
type UpdateBuilder[E any] struct { // contains filtered or unexported fields }
func Update ¶
func Update[E any](db DB) UpdateBuilder[E]
Update is a set-based UPDATE without a session (analogous to ExecuteUpdate in EF Core). Type safety comes from descriptors: Set takes an Assign[E] from Col.Set(v). Set(false) and Set(0) are full-fledged assignments.
UPDATE without Where is an error unless explicitly allowed via AllRows() (anti-footgun). For versioned entities the version column is incremented automatically — open sessions will properly catch the conflict.
func (UpdateBuilder[E]) AllRows ¶
func (q UpdateBuilder[E]) AllRows() UpdateBuilder[E]
AllRows explicitly allows an UPDATE over the whole table.
func (UpdateBuilder[E]) Named ¶
func (q UpdateBuilder[E]) Named(name string) UpdateBuilder[E]
Named labels the statement for instrumentation (sorm.query.name).
func (UpdateBuilder[E]) Set ¶
func (q UpdateBuilder[E]) Set(as ...Assign[E]) UpdateBuilder[E]
func (UpdateBuilder[E]) Where ¶
func (q UpdateBuilder[E]) Where(ps ...Pred[E]) UpdateBuilder[E]
func (UpdateBuilder[E]) WithDeleted ¶ added in v0.4.0
func (q UpdateBuilder[E]) WithDeleted() UpdateBuilder[E]
WithDeleted lets the UPDATE touch soft-deleted rows too.
type UpsertBuilder ¶ added in v0.4.0
type UpsertBuilder[E any] struct { // contains filtered or unexported fields }
func Upsert ¶ added in v0.4.0
func Upsert[E any](db DB) UpsertBuilder[E]
Upsert is a set-based INSERT ... ON CONFLICT (PostgreSQL/SQLite) / ON DUPLICATE KEY (MySQL):
n, err := sorm.Upsert[models.User](db).
Rows(u1, u2).
OnConflict(gen.User.Email). // conflict target (PG/SQLite)
DoUpdate(gen.User.Name, gen.User.Age). // set these from the new values
Exec(ctx)
Semantics and caveats:
- OnConflict is required on PostgreSQL/SQLite and ignored on MySQL (MySQL fires on ANY unique key — that is the engine's rule).
- DoUpdate columns are updated from the values being inserted (excluded.col / VALUES(col)); the version column of optimistic concurrency is bumped automatically on the update path.
- DoNothing() skips conflicting rows instead of updating them.
- autoCreate/autoUpdate timestamps are stamped on the inserted values.
- Auto-generated PKs are NOT written back into the entities (unlike Session inserts) — this is a set-based statement; reload if you need the keys. Exec returns the driver-reported affected count, whose meaning differs per engine (MySQL counts an update as 2).
func (UpsertBuilder[E]) DoNothing ¶ added in v0.4.0
func (q UpsertBuilder[E]) DoNothing() UpsertBuilder[E]
DoNothing skips conflicting rows entirely.
func (UpsertBuilder[E]) DoUpdate ¶ added in v0.4.0
func (q UpsertBuilder[E]) DoUpdate(cols ...ColOf[E]) UpsertBuilder[E]
DoUpdate lists the columns to overwrite with the incoming values when the row already exists.
func (UpsertBuilder[E]) Exec ¶ added in v0.4.0
func (q UpsertBuilder[E]) Exec(ctx context.Context) (int64, error)
func (UpsertBuilder[E]) Named ¶ added in v0.4.0
func (q UpsertBuilder[E]) Named(name string) UpsertBuilder[E]
Named labels the statement for instrumentation (sorm.query.name).
func (UpsertBuilder[E]) OnConflict ¶ added in v0.4.0
func (q UpsertBuilder[E]) OnConflict(cols ...ColOf[E]) UpsertBuilder[E]
OnConflict sets the conflict target (a unique column or unique-index column set). Required on PostgreSQL/SQLite; ignored on MySQL.
func (UpsertBuilder[E]) Rows ¶ added in v0.4.0
func (q UpsertBuilder[E]) Rows(es ...*E) UpsertBuilder[E]
Rows adds entities to insert.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
sorm
command
Command sorm is the code generator and migration CLI.
|
Command sorm is the code generator and migration CLI. |
|
Package dialect defines the minimal surface of differences between databases.
|
Package dialect defines the minimal surface of differences between databases. |
|
lite
Package lite implements the SQLite dialect.
|
Package lite implements the SQLite dialect. |
|
my
Package my implements the MySQL/MariaDB dialect.
|
Package my implements the MySQL/MariaDB dialect. |
|
pg
Package pg implements the PostgreSQL dialect.
|
Package pg implements the PostgreSQL dialect. |
|
driver
|
|
|
pgxd
Package pgxd is the pgx v5 adapter (PostgreSQL): one roundtrip per batch via pgx.Batch.
|
Package pgxd is the pgx v5 adapter (PostgreSQL): one roundtrip per batch via pgx.Batch. |
|
sqld
Package sqld is the database/sql adapter (MySQL, SQLite).
|
Package sqld is the database/sql adapter (MySQL, SQLite). |
|
examples
|
|
|
chat/cmd/chat
command
Chat — the sorm showcase application: Echo + clean layering (transport → service → repository → sorm), everything in a dedicated "chat" database schema.
|
Chat — the sorm showcase application: Echo + clean layering (transport → service → repository → sorm), everything in a dedicated "chat" database schema. |
|
chat/internal/config
Package config — typed application settings via sconf: YAML file + environment variables (CHAT_ prefix, __ as the section separator) + command-line flags, later sources override earlier ones.
|
Package config — typed application settings via sconf: YAML file + environment variables (CHAT_ prefix, __ as the section separator) + command-line flags, later sources override earlier ones. |
|
chat/internal/models
Package models is the persistence model of the chat example.
|
Package models is the persistence model of the chat example. |
|
chat/internal/repository
Package repository is the data-access layer.
|
Package repository is the data-access layer. |
|
chat/internal/service
Package service is the business layer: it composes repositories, makes multi-write flows atomic with sorm.RunInTx (the audit entry commits or rolls back together with the action) and logs via srog.
|
Package service is the business layer: it composes repositories, makes multi-write flows atomic with sorm.RunInTx (the audit entry commits or rolls back together with the action) and logs via srog. |
|
chat/internal/transport/httpapi
Package httpapi is the transport layer: Echo handlers over the chat service.
|
Package httpapi is the transport layer: Echo handlers over the chat service. |
|
internal
|
|
|
codegen
Package codegen generates the sormgen package from the parsed schema: typed column descriptors and meta with per-field Scan/Snapshot/Diff (zero reflection at runtime) — compact and diff-friendly.
|
Package codegen generates the sormgen package from the parsed schema: typed column descriptors and meta with per-field Scan/Snapshot/Diff (zero reflection at runtime) — compact and diff-friendly. |
|
ddl
Package ddl generates the canonical SQL schema (CREATE TABLE) from the parsed models schema — input for a file-based Atlas workflow (`atlas migrate diff --to file://schema.sql`) and schema documentation.
|
Package ddl generates the canonical SQL schema (CREATE TABLE) from the parsed models schema — input for a file-based Atlas workflow (`atlas migrate diff --to file://schema.sql`) and schema documentation. |
|
parse
Package parse parses the models package: it finds entity structs (marked by exactly one field with a `sorm:"pk..."` tag), classifies fields by Go type, and validates the schema before generation.
|
Package parse parses the models package: it finds entity structs (marked by exactly one field with a `sorm:"pk..."` tag), classifies fields by Go type, and validates the schema before generation. |
|
pgmodels
Package pgmodels is a PostgreSQL-only test schema: native array columns are rejected by the DDL generator on other dialects, so they live apart from the portable testmodels.
|
Package pgmodels is a PostgreSQL-only test schema: native array columns are rejected by the DDL generator on other dialects, so they live apart from the portable testmodels. |
|
testmodels
Package testmodels is a test schema covering every field kind the generator supports: basic types, nullable pointers, time.Time, []byte, a version field, and navigations.
|
Package testmodels is a test schema covering every field kind the generator supports: basic types, nullable pointers, time.Time, []byte, a version field, and navigations. |
|
Package migrate provides declarative migrations from application code, powered by the Atlas engine (ariga.io/atlas as a Go dependency, no external CLI):
|
Package migrate provides declarative migrations from application code, powered by the Atlas engine (ariga.io/atlas as a Go dependency, no external CLI): |
|
Package myagg provides MySQL-specific aggregate functions for sorm projections.
|
Package myagg provides MySQL-specific aggregate functions for sorm projections. |
|
Package otelsorm provides OpenTelemetry tracing AND metrics for sorm on top of sorm.Instrument:
|
Package otelsorm provides OpenTelemetry tracing AND metrics for sorm on top of sorm.Instrument: |
|
Package pgagg provides PostgreSQL-specific aggregate functions for sorm projections.
|
Package pgagg provides PostgreSQL-specific aggregate functions for sorm projections. |
|
Package sormtest is the testing toolkit for projects built on sorm.
|
Package sormtest is the testing toolkit for projects built on sorm. |