drel

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 22 Imported by: 0

README

Drel

Code-generation-based Go ORM for Postgres and SQLite/LibSQL (Turso). Type-safe queries, snapshot-based change tracking, zero runtime reflection, EF Core-level developer experience.

Installation

go install github.com/alternayte/drel/cmd/drel@latest

Quick Start

1. Define a model
package models

import "github.com/alternayte/drel"

type Task struct {
    drel.Model[int]
    Title    string `db:"title"`
    Done     bool   `db:"done"`
    Priority int    `db:"priority"`
}

func NewTask(title string, priority int) *Task {
    return &Task{Title: title, Priority: priority}
}

func (t *Task) MarkDone() { t.Done = true }
2. Generate code
drel generate

This produces type-safe query builders, scan functions, snapshot/diff helpers, and a DB struct that aggregates all discovered models.

3. Use it
// Open the generated DB
database, err := db.Open(dsn)

// Insert via a UnitOfWork (change-tracking work session)
uow := database.NewUnitOfWork()
uow.Tasks.Add(models.NewTask("Build ORM", 1))
err = uow.SaveChanges(ctx)

// Query with generated type-safe columns (read-only, untracked)
tasks, err := database.Tasks.
    Where(models.Tasks.Done.IsFalse()).
    OrderBy(models.Tasks.Priority.Asc()).
    All(ctx)

// Update with change tracking (only modified columns are UPDATEd)
uow = database.NewUnitOfWork()
task, err := uow.Tasks.Find(ctx, 1) // tracked
if err == nil {
    task.MarkDone()
    err = uow.SaveChanges(ctx)
}

// Or an explicit multi-statement transaction:
err = database.Transaction(ctx, func(tx *drel.Tx) error {
    repo := drel.NewTxRepository(tx, models.TaskMeta)
    repo.Add(models.NewTask("ship it", 2))
    return tx.SaveChanges(ctx)
})

Features

  • No reflection -- all scanning, diffing, and query building use generated code.
  • Snapshot-based change tracking -- only modified columns appear in UPDATE statements.
  • Type-safe query builder -- compile-time checked column predicates and ordering (Eq, In, Between, ILike, Raw).
  • Transactions -- explicit transaction API with configurable isolation levels and automatic flush on commit.
  • Soft delete, versioning, audit -- embed drel.SoftDelete, drel.Versioned, or drel.Audit for automatic column management.
  • Relationships -- generated RelationInfo and IncludeSpec for has-many, has-one, belongs-to, and many-to-many eager loading with cross-package support. Filter-aware includes respect soft-delete on related models, with Unscoped() opt-out.
  • Bulk operations -- BulkInsert, BulkUpdate, BulkDelete, BulkUpsert with batching and safety guards.
  • Domain events & outbox -- record events on entities, dispatch them after commit, and optionally persist them to a transactional outbox table via Engine.UseOutbox.
  • Pagination -- offset (PageOffset) and keyset/cursor (Page) paging with a deterministic primary-key tiebreaker.
  • Projections & aggregations -- Select, Aggregate, GroupBy into arbitrary DTOs.
  • Nested & filtered includes -- Include(Users.Posts.Then(Posts.Tags)), with Where/OrderBy/Limit per relationship; split-query loading avoids cartesian products.
  • Change-tracking depth -- tracked queries by default, AsNoTracking, Attach/Detach, and nested Savepoints.
  • Migrations -- dialect-aware schema generation and a structured snapshot diff (drel migrate new) that emits add/drop/alter for tables, columns, types, nullability, and indexes; up/down/status/lint for both dialects. Declare indexes/checks with db: tag options.
  • Read replicas -- WithReadReplica round-robins reads; writes and transactions use the primary; Primary() forces read-your-writes.
  • Query batching -- NewBatch + BatchAll/BatchFirst/BatchCount pipeline queries over pgx (sequential fallback elsewhere).
  • Observability -- structured slog query logging, slow-query and dev-mode diagnostics (N+1, unbounded queries, missing-index hints), and an OpenTelemetry-adaptable Tracer.
  • CLI -- drel init, generate, migrate, seed.
  • Raw SQL escape hatches -- Engine.Exec, Engine.Query, Engine.QueryRow, RawQuery[T], and Tx.Exec, Tx.QueryRow for anything the ORM does not cover.

Examples

See examples/ for working samples:

Dialects

  • Postgres — direct pgx, auto-detected from postgres:// DSNs.
  • SQLite — pure-Go modernc.org/sqlite, auto-detected from file:, sqlite://, :memory:, or *.db DSNs.
  • LibSQL/Turso — libsql:///https:///wss:// DSNs; build with -tags libsql (keeps the libsql client out of builds that don't use it). Verified end-to-end against a real libSQL server over HTTP. Prefer libsql:///https:// over ws:// for models with time.Time columns.

Limitations

  • Migration diffing does not auto-detect column renames — a rename appears as drop + add; edit the generated SQL if you intend a rename. SQLite cannot ALTER COLUMN TYPE/nullability in place, so those changes are emitted as loud -- WARNING comments to be applied by hand.
  • Bulk Set accepts any values — type safety is enforced on column predicates and Find but not on bulk mutation values.
  • True JOIN-based eager loading is intentionally not offered; relationships load via batched split queries (correct for every shape, no cartesian products).

License

MIT -- see LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBulkDeleteRequiresFilter = fmt.Errorf("drel: BulkDelete requires at least one Where predicate to prevent accidental full-table deletes")

BulkUpsert inserts or updates multiple entities based on conflict resolution. It bypasses change tracking and executes directly against the database. Returns the total number of rows affected. ErrBulkDeleteRequiresFilter is returned when BulkDelete is called without any WHERE predicates or filters.

View Source
var ErrConcurrencyConflict = errors.New("drel: concurrency conflict — entity was modified by another transaction")

ErrConcurrencyConflict is returned when an entity was modified by another transaction.

View Source
var ErrCursorPaginationNeedsOrderBy = errors.New("drel: cursor pagination requires OrderBy")

ErrCursorPaginationNeedsOrderBy is returned when Page is called without an OrderBy. Keyset pagination requires a deterministic ordering.

View Source
var ErrEntityNotTracked = errors.New("drel: entity is not tracked")

ErrEntityNotTracked is returned when an operation requires a tracked entity but none is found.

View Source
var ErrInvalidCursor = errors.New("drel: invalid cursor")

ErrInvalidCursor is returned when a cursor string cannot be decoded.

View Source
var ErrLibSQLNotBuilt = errors.New(
	"drel: libSQL/Turso support not built — rebuild with -tags libsql to enable it")

ErrLibSQLNotBuilt is returned when a libSQL/Turso DSN is used but the binary was not compiled with the "libsql" build tag.

View Source
var ErrNotFound = errors.New("drel: not found")

ErrNotFound is returned when a query expects a result but finds none.

View Source
var ErrPaginationNeedsLimit = errors.New("drel: pagination requires Take/Limit to set the page size")

ErrPaginationNeedsLimit is returned when a pagination method is called without Take/Limit.

View Source
var SoftDeleteFilter = NamedFilter{
	Name: "soft_delete",
	Clause: ast.WhereClause{
		Comparison: &ast.ComparisonNode{Column: "deleted_at", Op: ast.OpIsNull},
	},
}

Functions

func ActorFromContext

func ActorFromContext(ctx context.Context) string

ActorFromContext extracts the actor identifier from the context, returning an empty string if none is set.

func Aggregate added in v0.3.0

func Aggregate[R any, T any](ctx context.Context, q *QueryBuilder[T], agg AggExpr) (R, error)

Aggregate executes a single aggregate function and returns the scalar result.

func GroupBy added in v0.3.0

func GroupBy[R any, T any](ctx context.Context, q *QueryBuilder[T], groups []GroupSpec, aggs []AliasedAgg, opts ...GroupByOpt) ([]*R, error)

GroupBy executes a GROUP BY query with aggregates, returning results as []*R.

func OutboxSchema added in v0.3.0

func OutboxSchema(table, dialect string) string

OutboxSchema returns CREATE TABLE DDL for an outbox table for the given dialect ("postgres" or "sqlite"). The payload column is TEXT for portability (JSON is stored as a string); processed_at is left for a relay to stamp.

func RawQuery added in v0.3.0

func RawQuery[T any](ctx context.Context, e *Engine, sql string, args ...any) ([]*T, error)

RawQuery executes arbitrary SQL and scans results into []*T. Uses $1, $2 placeholder format — rewritten to ? for SQLite automatically.

func RawQueryRow added in v0.3.0

func RawQueryRow[T any](ctx context.Context, e *Engine, sql string, args ...any) (*T, error)

RawQueryRow executes arbitrary SQL expecting exactly one row, scanned into *T. Uses $1, $2 placeholder format — rewritten to ? for SQLite automatically.

func Select added in v0.3.0

func Select[R any, T any](ctx context.Context, q *QueryBuilder[T], cols ...ColumnRef) ([]*R, error)

Select executes a projection query, returning only specified columns into DTO type R. Because Go does not allow new type parameters on methods, this is a standalone function that takes a *QueryBuilder[T] and projects into a different type R.

func WithActor

func WithActor(ctx context.Context, actor string) context.Context

WithActor returns a new context carrying the given actor identifier.

Types

type AfterCommitHook

type AfterCommitHook func(ctx context.Context, events []any)

type AggExpr added in v0.3.0

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

AggExpr represents an aggregate function call.

func Avg added in v0.3.0

func Avg(col ColumnRef) AggExpr

Avg creates an AVG aggregate expression.

func CountCol added in v0.3.0

func CountCol(col ColumnRef) AggExpr

CountCol creates a COUNT aggregate expression for a specific column.

func Max added in v0.3.0

func Max(col ColumnRef) AggExpr

Max creates a MAX aggregate expression.

func Min added in v0.3.0

func Min(col ColumnRef) AggExpr

Min creates a MIN aggregate expression.

func Sum added in v0.3.0

func Sum(col ColumnRef) AggExpr

Sum creates a SUM aggregate expression.

type AliasedAgg added in v0.3.0

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

AliasedAgg pairs an aggregate with a result alias.

func As added in v0.3.0

func As(alias string, agg AggExpr) AliasedAgg

As pairs an aggregate expression with a column alias for the result.

type Audit

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

func (*Audit) AuditPtrs

func (a *Audit) AuditPtrs() (*string, *string)

func (Audit) CreatedBy

func (a Audit) CreatedBy() string

func (Audit) UpdatedBy

func (a Audit) UpdatedBy() string

type Batch added in v0.3.0

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

Batch groups multiple read queries so they execute in a single network round-trip when the driver supports pipelining (pgx), falling back to sequential execution otherwise. Queue queries with BatchAll/BatchFirst/ BatchCount, call Execute, then read each typed result via Result.

batch := db.NewBatch()
users := drel.BatchAll(batch, db.Users.Where(Users.Active.IsTrue()))
count := drel.BatchCount(batch, db.Users)
if err := batch.Execute(ctx); err != nil { ... }
us, _ := users.Result()
n, _ := count.Result()

func (*Batch) Execute added in v0.3.0

func (b *Batch) Execute(ctx context.Context) error

Execute runs all queued queries. On a pipelining driver they are sent in one round-trip and their results read in order; otherwise they run sequentially. Each query's typed result becomes available via its BatchResult afterwards.

type BatchResult added in v0.3.0

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

BatchResult holds the typed outcome of a batched query, available after the batch has been executed.

func BatchAll added in v0.3.0

func BatchAll[T any](b *Batch, q *QueryBuilder[T]) *BatchResult[[]*T]

BatchAll queues a query returning all matching rows.

func BatchCount added in v0.3.0

func BatchCount[T any](b *Batch, q *QueryBuilder[T]) *BatchResult[int]

BatchCount queues a COUNT query.

func BatchFirst added in v0.3.0

func BatchFirst[T any](b *Batch, q *QueryBuilder[T]) *BatchResult[*T]

BatchFirst queues a query returning the first matching row; Result returns ErrNotFound if there is none.

func (*BatchResult[T]) Result added in v0.3.0

func (r *BatchResult[T]) Result() (T, error)

Result returns the query's result and any error encountered while scanning it.

type BeforeCommitHook

type BeforeCommitHook func(ctx context.Context, tx *Tx, events []any) error

type BoolColumn

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

func NewBoolCol

func NewBoolCol(name string) BoolColumn

func (BoolColumn) Asc

func (c BoolColumn) Asc() OrderExpr

func (BoolColumn) ColRef added in v0.3.0

func (c BoolColumn) ColRef() ColumnRef

ColRef returns a ColumnRef for use with Select projections and aggregates.

func (BoolColumn) Desc

func (c BoolColumn) Desc() OrderExpr

func (BoolColumn) Eq

func (c BoolColumn) Eq(v bool) Predicate

func (BoolColumn) IsFalse

func (c BoolColumn) IsFalse() Predicate

func (BoolColumn) IsNotNull

func (c BoolColumn) IsNotNull() Predicate

func (BoolColumn) IsNull

func (c BoolColumn) IsNull() Predicate

func (BoolColumn) IsTrue

func (c BoolColumn) IsTrue() Predicate

func (BoolColumn) NEQ

func (c BoolColumn) NEQ(v bool) Predicate

func (BoolColumn) Name

func (c BoolColumn) Name() string

type Column

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

func NewCol

func NewCol[T any](name string) Column[T]

func (Column[T]) Asc

func (c Column[T]) Asc() OrderExpr

func (Column[T]) ColRef added in v0.3.0

func (c Column[T]) ColRef() ColumnRef

ColRef returns a ColumnRef for use with Select projections and aggregates.

func (Column[T]) Desc

func (c Column[T]) Desc() OrderExpr

func (Column[T]) Eq

func (c Column[T]) Eq(v T) Predicate

func (Column[T]) In

func (c Column[T]) In(vs ...T) Predicate

func (Column[T]) IsNotNull

func (c Column[T]) IsNotNull() Predicate

func (Column[T]) IsNull

func (c Column[T]) IsNull() Predicate

func (Column[T]) NEQ

func (c Column[T]) NEQ(v T) Predicate

func (Column[T]) Name

func (c Column[T]) Name() string

func (Column[T]) NotIn

func (c Column[T]) NotIn(vs ...T) Predicate

type ColumnRef added in v0.3.0

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

ColumnRef identifies a database column for use in Select projections.

func ColRef added in v0.3.0

func ColRef(name string) ColumnRef

ColRef creates a ColumnRef from a column name string.

func (ColumnRef) Name added in v0.3.0

func (c ColumnRef) Name() string

Name returns the column name.

type CursorPage added in v0.3.0

type CursorPage[T any] struct {
	Items      []*T
	NextCursor string // empty when there are no more results
	HasMore    bool
}

CursorPage holds a page of results from keyset (cursor) pagination.

type Engine

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

Engine holds a database driver and dialect for executing queries.

func NewEngine

func NewEngine(dsn string, opts ...Option) (*Engine, error)

NewEngine creates a new Engine connected to the given DSN. The dialect and driver are auto-detected from the DSN unless overridden with WithDriver or WithDialect.

func (*Engine) Close

func (e *Engine) Close()

Close shuts down the underlying database connection and any read replicas.

func (*Engine) Exec

func (e *Engine) Exec(ctx context.Context, sql string, args ...any) (int64, error)

Exec executes a raw SQL statement and returns the number of rows affected.

func (*Engine) NewBatch added in v0.3.0

func (e *Engine) NewBatch() *Batch

NewBatch creates an empty query batch bound to this engine.

func (*Engine) NewUnitOfWork added in v0.3.0

func (e *Engine) NewUnitOfWork() *UnitOfWork

NewUnitOfWork creates a change-tracking work session bound to this engine.

func (*Engine) OnAfterCommit

func (e *Engine) OnAfterCommit(hook AfterCommitHook)

func (*Engine) OnBeforeCommit

func (e *Engine) OnBeforeCommit(hook BeforeCommitHook)

func (*Engine) OnQuery

func (e *Engine) OnQuery(hook QueryHook)

OnQuery registers a hook that is called after every query execution.

func (*Engine) Query

func (e *Engine) Query(ctx context.Context, sql string, args ...any) (Rows, error)

Query executes a raw SQL query and returns the result rows. The caller must close the returned Rows when done.

func (*Engine) QueryRow

func (e *Engine) QueryRow(ctx context.Context, sql string, args ...any) Row

QueryRow executes a raw SQL query that is expected to return at most one row.

func (*Engine) Transaction

func (e *Engine) Transaction(ctx context.Context, fn func(tx *Tx) error, opts ...TxOption) (retErr error)

Transaction runs fn inside a database transaction. If fn returns nil the transaction is committed; otherwise it is rolled back. Pending changes in the Tx change tracker are flushed automatically before commit.

func (*Engine) UseOutbox added in v0.3.0

func (e *Engine) UseOutbox(table string, opts ...OutboxOption)

UseOutbox writes domain events to a transactional outbox table on every SaveChanges, within the same transaction as the changes. The table must exist (see OutboxSchema) with at least (type, payload) columns; payload is stored as JSON text. Events are dispatched to after-commit handlers as usual in addition to being persisted.

type EntityState added in v0.3.0

type EntityState int

EntityState describes a tracked entity's lifecycle state within a Tx.

const (
	// StateUnchanged marks an entity with no pending changes.
	StateUnchanged EntityState = iota
	// StateAdded marks an entity to be INSERTed on the next flush.
	StateAdded
	// StateModified marks an entity to be UPDATEd on the next flush.
	StateModified
	// StateDeleted marks an entity to be deleted on the next flush.
	StateDeleted
)

type EventRecorder

type EventRecorder interface {
	PendingEvents() []any
	ClearEvents()
}

type FieldChange

type FieldChange struct {
	Column string
	Value  any
}

FieldChange represents a single column value that has changed on an entity.

type GroupByOpt added in v0.3.0

type GroupByOpt func(*groupByConfig)

GroupByOpt is an option for GroupBy queries.

func Having added in v0.3.0

func Having(pred Predicate) GroupByOpt

Having adds a HAVING clause to a GroupBy query.

type GroupSpec added in v0.3.0

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

GroupSpec specifies a GROUP BY column.

func Group added in v0.3.0

func Group(col ColumnRef) GroupSpec

Group creates a GroupSpec from a ColumnRef.

type IncludableQuery

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

IncludableQuery constructs queries that eagerly load related entities. The root query may be refined with Where/OrderBy/Limit/Skip/Take exactly like a plain QueryBuilder; the configured relationships are loaded via split queries after the root result set is materialized.

func (*IncludableQuery[T]) All

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

All returns all matching records (honoring Where/OrderBy/Limit) and loads included relationships.

func (*IncludableQuery[T]) Find

func (q *IncludableQuery[T]) Find(ctx context.Context, id any) (*T, error)

Find looks up a single record by primary key and loads included relationships.

func (*IncludableQuery[T]) First added in v0.3.0

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

First returns the first matching record (honoring Where/OrderBy) and loads included relationships, or ErrNotFound if none match.

func (*IncludableQuery[T]) Include added in v0.3.0

func (q *IncludableQuery[T]) Include(rels ...IncludeSpec) *IncludableQuery[T]

Include adds more relationships to eagerly load.

func (*IncludableQuery[T]) Limit added in v0.3.0

func (q *IncludableQuery[T]) Limit(n int) *IncludableQuery[T]

Limit restricts the number of root records returned.

func (*IncludableQuery[T]) OrderBy added in v0.3.0

func (q *IncludableQuery[T]) OrderBy(exprs ...OrderExpr) *IncludableQuery[T]

OrderBy sets the ordering for the root query.

func (*IncludableQuery[T]) Skip added in v0.3.0

func (q *IncludableQuery[T]) Skip(n int) *IncludableQuery[T]

Skip sets the offset for the root query.

func (*IncludableQuery[T]) Take added in v0.3.0

func (q *IncludableQuery[T]) Take(n int) *IncludableQuery[T]

Take restricts the number of root records returned (alias for Limit).

func (*IncludableQuery[T]) Unscoped added in v0.3.0

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

Unscoped removes all global filters from the root query.

func (*IncludableQuery[T]) Where added in v0.3.0

func (q *IncludableQuery[T]) Where(pred Predicate) *IncludableQuery[T]

Where adds a filter predicate to the root query.

func (*IncludableQuery[T]) WithoutFilter added in v0.3.0

func (q *IncludableQuery[T]) WithoutFilter(name string) *IncludableQuery[T]

WithoutFilter removes the named global filter from the root query.

type IncludeSpec

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

IncludeSpec wraps a RelationInfo for use with Include queries.

func NewIncludeSpec

func NewIncludeSpec(rel *RelationInfo) IncludeSpec

NewIncludeSpec creates an IncludeSpec from a RelationInfo.

func (IncludeSpec) Limit added in v0.3.0

func (s IncludeSpec) Limit(n int) IncludeSpec

Limit returns a copy of the IncludeSpec that limits the number of related entities loaded per parent batch.

func (IncludeSpec) OrderBy added in v0.3.0

func (s IncludeSpec) OrderBy(exprs ...OrderExpr) IncludeSpec

OrderBy returns a copy of the IncludeSpec with the given ordering applied to the related entities query.

func (IncludeSpec) Then added in v0.3.0

func (s IncludeSpec) Then(children ...IncludeSpec) IncludeSpec

Then returns a copy of the IncludeSpec that also eagerly loads the given relationships on the related entities, enabling nested includes such as Users.Posts.Then(Posts.Tags). The child specs describe relationships on the related model (here Post), and may themselves be nested with Then. Nested loading uses split queries on each level, so it never produces a cartesian product regardless of depth.

func (IncludeSpec) Unscoped

func (s IncludeSpec) Unscoped() IncludeSpec

Unscoped returns a copy of the IncludeSpec that bypasses global filters (e.g. soft delete) when loading the related entities.

func (IncludeSpec) Where added in v0.3.0

func (s IncludeSpec) Where(pred Predicate) IncludeSpec

Where returns a copy of the IncludeSpec with an additional filter predicate applied to the related entities query.

func (IncludeSpec) WithoutFilter added in v0.3.0

func (s IncludeSpec) WithoutFilter(name string) IncludeSpec

WithoutFilter returns a copy of the IncludeSpec that drops the named global filter when loading the related entities (the others still apply).

type IsolationLevel

type IsolationLevel int

IsolationLevel represents the transaction isolation level.

const (
	// ReadCommitted is the default isolation level.
	ReadCommitted IsolationLevel = iota
	// RepeatableRead prevents non-repeatable reads.
	RepeatableRead
	// Serializable provides full serializability.
	Serializable
)

type Model

type Model[K comparable] struct {
	// contains filtered or unexported fields
}

func (*Model[K]) ClearEvents

func (m *Model[K]) ClearEvents()

func (Model[K]) CreatedAt

func (m Model[K]) CreatedAt() time.Time

func (Model[K]) ID

func (m Model[K]) ID() K

func (*Model[K]) PendingEvents

func (m *Model[K]) PendingEvents() []any

func (*Model[K]) RecordEvent

func (m *Model[K]) RecordEvent(event any)

func (*Model[K]) ScanPtrs

func (m *Model[K]) ScanPtrs() (*K, *time.Time, *time.Time)

ScanPtrs returns pointers to the base model fields for use in row scanning. Generated code calls this to obtain scan destinations for id, createdAt, and updatedAt without directly accessing unexported fields.

func (Model[K]) UpdatedAt

func (m Model[K]) UpdatedAt() time.Time

type ModelMeta

type ModelMeta[T any] struct {
	Table          string
	Columns        []string
	PKColumn       string
	Scan           func(Row) (*T, error)
	Snapshot       func(*T) any
	Diff           func(*T, any) []FieldChange
	PKValue        func(*T) any
	InsertColumns  func(*T) ([]string, []any)
	ScanReturning  func(*T, Row) error
	ColumnValue    func(*T, int) any
	Filters        []NamedFilter
	HasSoftDelete  bool
	HasVersioned   bool
	HasAudit       bool
	VersionValue   func(*T) int
	SetVersion     func(*T, int)
	AuditSetCreate func(*T, string)
	AuditSetUpdate func(*T, string)
}

ModelMeta describes the database mapping for a model type T.

type ModelMetaBase

type ModelMetaBase struct {
	Table          string
	Columns        []string
	PKColumn       string
	Snapshot       func(entity any) any
	Diff           func(entity any, snapshot any) []FieldChange
	PKValue        func(entity any) any
	InsertColumns  func(entity any) ([]string, []any)
	ScanRow        func(Row) (any, error)
	ScanReturning  func(entity any, row Row) error
	ColumnValue    func(entity any, colIdx int) any
	HasSoftDelete  bool
	HasVersioned   bool
	HasAudit       bool
	VersionValue   func(entity any) int
	SetVersion     func(entity any, v int)
	AuditSetCreate func(entity any, actor string)
	AuditSetUpdate func(entity any, actor string)
	Filters        []NamedFilter
}

ModelMetaBase is the type-erased version of ModelMeta[T], used internally by the change tracker and relationship loading infrastructure.

func ToMetaBase

func ToMetaBase[T any](meta *ModelMeta[T]) *ModelMetaBase

ToMetaBase converts a typed ModelMeta[T] to a type-erased ModelMetaBase.

type MultiColumnMapper

type MultiColumnMapper interface {
	DrelColumns() []string
	DrelValues() ([]any, error)
	DrelScanMulti(vals []any) error
}

MultiColumnMapper is implemented by value objects that map to multiple database columns (e.g., Money → amount + currency).

type NamedFilter

type NamedFilter struct {
	Name   string
	Clause ast.WhereClause
}

type OffsetPage added in v0.3.0

type OffsetPage[T any] struct {
	Items      []*T
	Total      int
	Page       int // 1-based page number
	PageSize   int
	TotalPages int
	HasMore    bool
}

OffsetPage holds a page of results from offset-based pagination.

type Option

type Option func(*engineConfig)

Option configures Engine creation.

func WithAuthToken added in v0.3.0

func WithAuthToken(token string) Option

WithAuthToken sets the authentication token for a libSQL/Turso connection. It is appended to the DSN as the authToken query parameter.

func WithContext

func WithContext(ctx context.Context) Option

WithContext sets the context used during engine creation.

func WithDevMode added in v0.3.0

func WithDevMode() Option

WithDevMode enables development-time diagnostics: warnings for unbounded queries (SELECT without LIMIT), likely N+1 access patterns (the same query shape executed repeatedly in a short window), slow queries, and tracked entities that were loaded but never modified. Do not enable in production.

func WithDialect added in v0.3.0

func WithDialect(dia dialect.Dialect) Option

WithDialect overrides the SQL dialect used by the engine. When set, auto-detection is skipped for the dialect.

func WithDriver added in v0.3.0

func WithDriver(drv driver.Driver) Option

WithDriver overrides the driver used by the engine. When set, auto-detection is skipped for the driver.

func WithLogger added in v0.3.0

func WithLogger(logger *slog.Logger) Option

WithLogger attaches a structured logger. Combined with WithQueryLog, WithSlowQueryThreshold, or WithDevMode it emits query and diagnostic logs.

func WithQueryLog added in v0.3.0

func WithQueryLog(enabled bool) Option

WithQueryLog logs every executed query (SQL, arg count, duration) at debug level. Requires a logger (WithLogger); if none is set, slog.Default is used.

func WithReadReplica added in v0.3.0

func WithReadReplica(dsn string) Option

WithReadReplica registers a read replica. Read queries issued through repositories (not within a transaction, and not forced to Primary) are routed round-robin across all registered replicas; writes and transactions always go to the primary.

func WithSlowQueryThreshold added in v0.3.0

func WithSlowQueryThreshold(d time.Duration) Option

WithSlowQueryThreshold logs a warning for any query whose execution exceeds d.

func WithTracer added in v0.3.0

func WithTracer(tracer Tracer) Option

WithTracer emits an OpenTelemetry-style span per executed query.

type OrderExpr

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

func (OrderExpr) ToAST

func (o OrderExpr) ToAST() ast.OrderByExpr

type OrderedColumn

type OrderedColumn[T cmp.Ordered] struct {
	// contains filtered or unexported fields
}

func NewOrderedCol

func NewOrderedCol[T cmp.Ordered](name string) OrderedColumn[T]

func (OrderedColumn[T]) Asc

func (c OrderedColumn[T]) Asc() OrderExpr

func (OrderedColumn[T]) Between

func (c OrderedColumn[T]) Between(low, high T) Predicate

func (OrderedColumn[T]) ColRef added in v0.3.0

func (c OrderedColumn[T]) ColRef() ColumnRef

ColRef returns a ColumnRef for use with Select projections and aggregates.

func (OrderedColumn[T]) Desc

func (c OrderedColumn[T]) Desc() OrderExpr

func (OrderedColumn[T]) Eq

func (c OrderedColumn[T]) Eq(v T) Predicate

func (OrderedColumn[T]) GT

func (c OrderedColumn[T]) GT(v T) Predicate

func (OrderedColumn[T]) GTE

func (c OrderedColumn[T]) GTE(v T) Predicate

func (OrderedColumn[T]) In

func (c OrderedColumn[T]) In(vs ...T) Predicate

func (OrderedColumn[T]) IsNotNull

func (c OrderedColumn[T]) IsNotNull() Predicate

func (OrderedColumn[T]) IsNull

func (c OrderedColumn[T]) IsNull() Predicate

func (OrderedColumn[T]) LT

func (c OrderedColumn[T]) LT(v T) Predicate

func (OrderedColumn[T]) LTE

func (c OrderedColumn[T]) LTE(v T) Predicate

func (OrderedColumn[T]) NEQ

func (c OrderedColumn[T]) NEQ(v T) Predicate

func (OrderedColumn[T]) Name

func (c OrderedColumn[T]) Name() string

func (OrderedColumn[T]) NotIn

func (c OrderedColumn[T]) NotIn(vs ...T) Predicate

type OutboxMessage added in v0.3.0

type OutboxMessage struct {
	Type    string
	Payload any
}

OutboxMessage is a row written to the transactional outbox. Type identifies the event kind; Payload is JSON-serialized into the outbox table's payload column. Messages are written within the same transaction as the changes that produced the events, giving exactly-once hand-off to an external relay (Debezium CDC, a polling worker, etc.).

type OutboxOption added in v0.3.0

type OutboxOption func(*outboxConfig)

OutboxOption configures UseOutbox.

func WithOutboxMapper added in v0.3.0

func WithOutboxMapper(fn func(event any) (OutboxMessage, bool)) OutboxOption

WithOutboxMapper customizes how domain events are mapped to outbox messages. Return ok=false to skip an event. The default maps every event to a message whose Type is the event's Go type name and whose Payload is the event itself.

type Predicate

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

func And

func And(preds ...Predicate) Predicate

func Not

func Not(pred Predicate) Predicate

func Or

func Or(preds ...Predicate) Predicate

func Raw

func Raw(sql string, args ...any) Predicate

Raw creates a predicate from a raw SQL expression with bound arguments. Use ? as placeholder for each argument; they are rewritten to $N for Postgres. Panics if the number of ? placeholders does not match the number of arguments.

func (Predicate) ToAST

func (p Predicate) ToAST() ast.WhereClause

type QueryBuilder

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

QueryBuilder constructs and executes typed queries with an immutable builder pattern.

func (*QueryBuilder[T]) After added in v0.3.0

func (q *QueryBuilder[T]) After(cursor string) *QueryBuilder[T]

After positions a cursor-paginated query immediately past the row encoded by the cursor (as returned in a prior CursorPage.NextCursor). Combine with OrderBy and Take, then call Page.

func (*QueryBuilder[T]) All

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

All executes the query and returns all matching results.

func (*QueryBuilder[T]) BulkDelete

func (q *QueryBuilder[T]) BulkDelete(ctx context.Context) (int, error)

BulkDelete deletes all rows matching the builder's WHERE conditions. If the model has soft delete, it performs a soft delete instead. Returns the number of affected rows.

func (*QueryBuilder[T]) BulkUpdate

func (q *QueryBuilder[T]) BulkUpdate(ctx context.Context, sets ...SetClause) (int, error)

BulkUpdate updates all rows matching the builder's WHERE conditions. Returns the number of affected rows.

func (*QueryBuilder[T]) Count

func (q *QueryBuilder[T]) Count(ctx context.Context) (int, error)

Count returns the number of matching rows.

func (*QueryBuilder[T]) Exists

func (q *QueryBuilder[T]) Exists(ctx context.Context) (bool, error)

Exists returns true if at least one matching row exists.

func (*QueryBuilder[T]) First

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

First returns the first matching result or ErrNotFound if none exist.

func (*QueryBuilder[T]) FirstOrNil

func (q *QueryBuilder[T]) FirstOrNil(ctx context.Context) (*T, error)

FirstOrNil returns the first matching result or nil if none exist.

func (*QueryBuilder[T]) Limit

func (q *QueryBuilder[T]) Limit(n int) *QueryBuilder[T]

Limit restricts the number of results returned.

func (*QueryBuilder[T]) OrderBy

func (q *QueryBuilder[T]) OrderBy(exprs ...OrderExpr) *QueryBuilder[T]

OrderBy sets the ordering for the query.

func (*QueryBuilder[T]) Page added in v0.3.0

func (q *QueryBuilder[T]) Page(ctx context.Context) (*CursorPage[T], error)

Page executes a keyset (cursor) page query. OrderBy is required; Take sets the page size. When After is set the page begins past that cursor.

func (*QueryBuilder[T]) PageOffset added in v0.3.0

func (q *QueryBuilder[T]) PageOffset(ctx context.Context) (*OffsetPage[T], error)

PageOffset executes an offset-based page query. Take (or Limit) sets the page size and Skip sets the offset. It runs a COUNT to populate Total/TotalPages.

func (*QueryBuilder[T]) Primary added in v0.3.0

func (q *QueryBuilder[T]) Primary() *QueryBuilder[T]

Primary forces this query to read from the primary connection instead of a read replica. Has no effect when no replicas are configured.

func (*QueryBuilder[T]) Skip

func (q *QueryBuilder[T]) Skip(n int) *QueryBuilder[T]

Skip sets the offset for the query (number of rows to skip).

func (*QueryBuilder[T]) Take added in v0.3.0

func (q *QueryBuilder[T]) Take(n int) *QueryBuilder[T]

Take limits the number of results returned. It is an alias for Limit that reads naturally alongside Skip/After in pagination expressions.

func (*QueryBuilder[T]) Unscoped

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

Unscoped returns a new builder with all global filters removed.

func (*QueryBuilder[T]) Where

func (q *QueryBuilder[T]) Where(pred Predicate) *QueryBuilder[T]

Where adds a filter predicate. Multiple calls are ANDed together.

func (*QueryBuilder[T]) WithoutFilter

func (q *QueryBuilder[T]) WithoutFilter(name string) *QueryBuilder[T]

WithoutFilter returns a new builder with the named filter removed.

type QueryEvent

type QueryEvent struct {
	SQL      string
	Args     []any
	Duration time.Duration
	Err      error
}

QueryEvent contains information about an executed query.

type QueryHook

type QueryHook func(ctx context.Context, event QueryEvent)

QueryHook is called after every query execution.

type RawExpr

type RawExpr struct {
	SQL string
}

RawExpr represents a raw SQL expression to embed in a query (e.g., NOW()).

type Relation

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

func NewRelation

func NewRelation[T any](name string) Relation[T]

func (Relation[T]) Name

func (r Relation[T]) Name() string

type RelationInfo

type RelationInfo struct {
	Name        string
	Type        RelationType
	FKColumn    string
	JoinTable   string // pivot table name (many-to-many only)
	RefColumn   string // target FK column in pivot table (many-to-many only)
	RelatedMeta *ModelMetaBase
	FieldSetter func(parent any, related any)
}

RelationInfo describes a relationship between two models.

type RelationType

type RelationType int

RelationType describes the kind of relationship between two models.

const (
	// HasMany indicates the parent has many related entities (1:N).
	HasMany RelationType = iota
	// HasOne indicates the parent has one related entity (1:1).
	HasOne
	// BelongsTo indicates the entity references a parent via a foreign key.
	BelongsTo
	// ManyToMany indicates a many-to-many relationship via a pivot table.
	ManyToMany
)

type Repository

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

Repository provides typed query access for a specific model.

func NewRepository

func NewRepository[T any](engine *Engine, meta ModelMeta[T]) *Repository[T]

NewRepository creates a new Repository for the given model metadata.

func (*Repository[T]) After added in v0.3.0

func (r *Repository[T]) After(cursor string) *QueryBuilder[T]

After starts a cursor-paginated query positioned past the given cursor.

func (*Repository[T]) All

func (r *Repository[T]) All(ctx context.Context) ([]*T, error)

All returns all records for this model.

func (*Repository[T]) BulkInsert

func (r *Repository[T]) BulkInsert(ctx context.Context, entities []*T) (int, error)

BulkInsert inserts multiple entities in batches, bypassing change tracking. Returns the total number of rows affected.

func (*Repository[T]) BulkUpsert

func (r *Repository[T]) BulkUpsert(ctx context.Context, entities []*T, opts ...UpsertOption) (int, error)

func (*Repository[T]) Count

func (r *Repository[T]) Count(ctx context.Context) (int, error)

Count returns the total number of records for this model.

func (*Repository[T]) Exists

func (r *Repository[T]) Exists(ctx context.Context) (bool, error)

Exists returns true if any records exist for this model.

func (*Repository[T]) Find

func (r *Repository[T]) Find(ctx context.Context, id any) (*T, error)

Find looks up a single record by its primary key.

func (*Repository[T]) First

func (r *Repository[T]) First(ctx context.Context) (*T, error)

First returns the first record or ErrNotFound.

func (*Repository[T]) FirstOrNil

func (r *Repository[T]) FirstOrNil(ctx context.Context) (*T, error)

FirstOrNil returns the first record or nil.

func (*Repository[T]) Include

func (r *Repository[T]) Include(rels ...IncludeSpec) *IncludableQuery[T]

Include begins a query that will eagerly load the given relationships.

func (*Repository[T]) Limit

func (r *Repository[T]) Limit(n int) *QueryBuilder[T]

Limit starts a query with a row limit.

func (*Repository[T]) OrderBy

func (r *Repository[T]) OrderBy(exprs ...OrderExpr) *QueryBuilder[T]

OrderBy starts an ordered query.

func (*Repository[T]) Primary added in v0.3.0

func (r *Repository[T]) Primary() *QueryBuilder[T]

Primary returns a query builder that reads from the primary connection, bypassing read replicas (useful for read-your-writes consistency).

func (*Repository[T]) Skip added in v0.3.0

func (r *Repository[T]) Skip(n int) *QueryBuilder[T]

Skip starts a query with a row offset.

func (*Repository[T]) Take added in v0.3.0

func (r *Repository[T]) Take(n int) *QueryBuilder[T]

Take starts a query with a row limit (alias for Limit, reads naturally with pagination).

func (*Repository[T]) Unscoped

func (r *Repository[T]) Unscoped() *QueryBuilder[T]

Unscoped returns a query builder with all global filters removed.

func (*Repository[T]) Where

func (r *Repository[T]) Where(pred Predicate) *QueryBuilder[T]

Where starts a filtered query.

func (*Repository[T]) WithoutFilter

func (r *Repository[T]) WithoutFilter(name string) *QueryBuilder[T]

WithoutFilter returns a query builder with the named filter removed.

type Row

type Row interface {
	Scan(dest ...any) error
}

Row represents a single database row that can scan values into destinations.

type Rows

type Rows interface {
	Next() bool
	Scan(dest ...any) error
	Close()
	Err() error
}

Rows represents a set of database rows from a query result.

type SetClause

type SetClause struct {
	Column string
	Value  any
}

SetClause pairs a column name with a value for use in bulk updates and upserts.

func Set

func Set[C interface{ Name() string }, V any](col C, value V) SetClause

Set creates a SetClause from a typed column and value.

type SoftDelete

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

func (SoftDelete) DeletedAt

func (s SoftDelete) DeletedAt() *time.Time

func (*SoftDelete) DeletedAtPtr

func (s *SoftDelete) DeletedAtPtr() **time.Time

func (SoftDelete) IsDeleted

func (s SoftDelete) IsDeleted() bool

type Span added in v0.3.0

type Span interface {
	End()
	RecordError(err error)
}

Span is a minimal tracing span. It is satisfied by a thin adapter over any tracing library (e.g. OpenTelemetry), keeping drel free of tracing dependencies. End is called when the traced operation completes; RecordError attaches an error to the span (a nil error is ignored).

type StringColumn

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

func NewStringCol

func NewStringCol(name string) StringColumn

func (StringColumn) Asc

func (c StringColumn) Asc() OrderExpr

func (StringColumn) ColRef added in v0.3.0

func (c StringColumn) ColRef() ColumnRef

ColRef returns a ColumnRef for use with Select projections and aggregates.

func (StringColumn) Contains

func (c StringColumn) Contains(substr string) Predicate

func (StringColumn) Desc

func (c StringColumn) Desc() OrderExpr

func (StringColumn) Eq

func (c StringColumn) Eq(v string) Predicate

func (StringColumn) GT

func (c StringColumn) GT(v string) Predicate

func (StringColumn) GTE

func (c StringColumn) GTE(v string) Predicate

func (StringColumn) HasPrefix

func (c StringColumn) HasPrefix(prefix string) Predicate

func (StringColumn) HasSuffix

func (c StringColumn) HasSuffix(suffix string) Predicate

func (StringColumn) ILike

func (c StringColumn) ILike(pattern string) Predicate

func (StringColumn) In

func (c StringColumn) In(vs ...string) Predicate

func (StringColumn) IsNotNull

func (c StringColumn) IsNotNull() Predicate

func (StringColumn) IsNull

func (c StringColumn) IsNull() Predicate

func (StringColumn) LT

func (c StringColumn) LT(v string) Predicate

func (StringColumn) LTE

func (c StringColumn) LTE(v string) Predicate

func (StringColumn) Like

func (c StringColumn) Like(pattern string) Predicate

func (StringColumn) NEQ

func (c StringColumn) NEQ(v string) Predicate

func (StringColumn) Name

func (c StringColumn) Name() string

func (StringColumn) NotIn

func (c StringColumn) NotIn(vs ...string) Predicate

type Tracer added in v0.3.0

type Tracer interface {
	// Start begins a span named name and returns a context carrying it.
	Start(ctx context.Context, name string) (context.Context, Span)
}

Tracer starts spans for drel operations. Provide one via WithTracer to emit a span per executed query. A nil Tracer disables tracing.

type Tx

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

Tx represents an active database transaction with change tracking.

func (*Tx) Exec

func (tx *Tx) Exec(ctx context.Context, sql string, args ...any) (int64, error)

Exec executes a raw SQL statement within the transaction.

func (*Tx) HardRemove

func (tx *Tx) HardRemove(entity any) error

HardRemove marks a tracked entity for permanent (hard) deletion on the next flush, bypassing soft delete even when the model supports it.

func (*Tx) QueryRow

func (tx *Tx) QueryRow(ctx context.Context, sql string, args ...any) Row

QueryRow executes a raw SQL query within the transaction that returns at most one row.

func (*Tx) SaveChanges

func (tx *Tx) SaveChanges(ctx context.Context) error

SaveChanges flushes all tracked changes within this transaction.

func (*Tx) Savepoint added in v0.3.0

func (tx *Tx) Savepoint(ctx context.Context, name string, fn func(sp *Tx) error) error

Savepoint runs fn within a nested SAVEPOINT. If fn returns nil the savepoint is released; otherwise the transaction is rolled back to the savepoint and the change tracker is reverted to its state before the savepoint, so entities added inside fn are dropped and prior entities keep their earlier state. The outer transaction continues either way. Savepoints may be nested, including reusing the same name.

Note: rollback reverts the change tracker, not the in-memory Go structs. If a flush inside fn populated generated fields on an entity (id, version, created_at) and the savepoint is then rolled back, that entity still carries those values in memory even though its row no longer exists. Discard such entities rather than reusing them after a rolled-back savepoint.

type TxOption

type TxOption func(*txConfig)

TxOption configures transaction behaviour.

func WithIsolation

func WithIsolation(level IsolationLevel) TxOption

WithIsolation sets the isolation level for the transaction.

type TxQueryBuilder

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

TxQueryBuilder constructs and executes typed queries within a transaction. By default, results are tracked by the transaction's change tracker so that mutations are detected on SaveChanges; use AsNoTracking to opt out.

func (*TxQueryBuilder[T]) After added in v0.3.0

func (q *TxQueryBuilder[T]) After(cursor string) *TxQueryBuilder[T]

After positions a cursor-paginated query past the row encoded by the cursor.

func (*TxQueryBuilder[T]) All

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

All executes the query and returns all matching results.

func (*TxQueryBuilder[T]) AsNoTracking added in v0.3.0

func (q *TxQueryBuilder[T]) AsNoTracking() *TxQueryBuilder[T]

AsNoTracking returns a copy of the builder whose results will not be tracked.

func (*TxQueryBuilder[T]) Count

func (q *TxQueryBuilder[T]) Count(ctx context.Context) (int, error)

Count returns the number of matching rows.

func (*TxQueryBuilder[T]) Exists

func (q *TxQueryBuilder[T]) Exists(ctx context.Context) (bool, error)

Exists returns true if at least one matching row exists.

func (*TxQueryBuilder[T]) First

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

First returns the first matching result or ErrNotFound if none exist.

func (*TxQueryBuilder[T]) FirstOrNil

func (q *TxQueryBuilder[T]) FirstOrNil(ctx context.Context) (*T, error)

FirstOrNil returns the first matching result or nil if none exist.

func (*TxQueryBuilder[T]) Limit

func (q *TxQueryBuilder[T]) Limit(n int) *TxQueryBuilder[T]

Limit restricts the number of results returned.

func (*TxQueryBuilder[T]) OrderBy

func (q *TxQueryBuilder[T]) OrderBy(exprs ...OrderExpr) *TxQueryBuilder[T]

OrderBy sets the ordering for the query.

func (*TxQueryBuilder[T]) Page added in v0.3.0

func (q *TxQueryBuilder[T]) Page(ctx context.Context) (*CursorPage[T], error)

Page executes a keyset (cursor) page query within the transaction.

func (*TxQueryBuilder[T]) PageOffset added in v0.3.0

func (q *TxQueryBuilder[T]) PageOffset(ctx context.Context) (*OffsetPage[T], error)

PageOffset executes an offset-based page query within the transaction.

func (*TxQueryBuilder[T]) Skip

func (q *TxQueryBuilder[T]) Skip(n int) *TxQueryBuilder[T]

Skip sets the offset for the query (number of rows to skip).

func (*TxQueryBuilder[T]) Take added in v0.3.0

func (q *TxQueryBuilder[T]) Take(n int) *TxQueryBuilder[T]

Take limits the number of results returned. Alias for Limit.

func (*TxQueryBuilder[T]) Unscoped

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

Unscoped returns a new builder with all global filters removed.

func (*TxQueryBuilder[T]) Where

func (q *TxQueryBuilder[T]) Where(pred Predicate) *TxQueryBuilder[T]

Where adds a filter predicate. Multiple calls are ANDed together.

func (*TxQueryBuilder[T]) WithoutFilter

func (q *TxQueryBuilder[T]) WithoutFilter(name string) *TxQueryBuilder[T]

WithoutFilter returns a new builder with the named filter removed.

type TxRepository

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

TxRepository provides tracked query and mutation access within a transaction.

func NewTxRepository

func NewTxRepository[T any](tx *Tx, meta ModelMeta[T]) *TxRepository[T]

NewTxRepository creates a new TxRepository for the given transaction and model metadata.

func (*TxRepository[T]) Add

func (r *TxRepository[T]) Add(entity *T)

Add marks an entity for insertion on the next flush.

func (*TxRepository[T]) All

func (r *TxRepository[T]) All(ctx context.Context) ([]*T, error)

All returns all records for this model within the transaction, tracking them.

func (*TxRepository[T]) AsNoTracking added in v0.3.0

func (r *TxRepository[T]) AsNoTracking() *TxQueryBuilder[T]

AsNoTracking returns a query builder whose results are not tracked, for read-only queries within the transaction.

func (*TxRepository[T]) Attach added in v0.3.0

func (r *TxRepository[T]) Attach(entity *T, state EntityState)

Attach begins tracking an externally-constructed entity (e.g. deserialized from a request) in the given state. StateModified flushes a full-column UPDATE; StateAdded behaves like Add; StateUnchanged snapshots the entity so subsequent mutations are detected.

func (*TxRepository[T]) Detach added in v0.3.0

func (r *TxRepository[T]) Detach(entity *T)

Detach stops tracking an entity so its mutations are no longer flushed.

func (*TxRepository[T]) Find

func (r *TxRepository[T]) Find(ctx context.Context, id any) (*T, error)

Find looks up a single record by primary key and begins tracking it.

func (*TxRepository[T]) OrderBy added in v0.3.0

func (r *TxRepository[T]) OrderBy(exprs ...OrderExpr) *TxQueryBuilder[T]

OrderBy starts an ordered, tracked query within the transaction.

func (*TxRepository[T]) Remove

func (r *TxRepository[T]) Remove(entity *T) error

Remove marks a tracked entity for deletion on the next flush.

func (*TxRepository[T]) Unscoped

func (r *TxRepository[T]) Unscoped() *TxQueryBuilder[T]

Unscoped returns a query builder with all global filters removed.

func (*TxRepository[T]) Where

func (r *TxRepository[T]) Where(pred Predicate) *TxQueryBuilder[T]

Where starts a filtered, tracked query within the transaction.

func (*TxRepository[T]) WithoutFilter

func (r *TxRepository[T]) WithoutFilter(name string) *TxQueryBuilder[T]

WithoutFilter returns a query builder with the named filter removed.

type UnitOfWork added in v0.3.0

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

UnitOfWork is a change-tracking work session in the Entity Framework DbContext style. Entities loaded through its repositories are tracked; Add/Remove/Attach stage inserts, deletes, and updates; SaveChanges flushes all staged changes in a single transaction and dispatches domain events on commit.

Unlike an explicit transaction (Engine.Transaction), a UnitOfWork does not hold a database connection open between calls — reads run on demand and SaveChanges opens a short transaction to flush. It is therefore not safe for concurrent use; use one per logical operation (e.g. per request).

Typed access (uow.Users.Find/Add/...) is provided by the generated UnitOfWork wrapper in your db package; this type carries the shared tracker and SaveChanges.

func (*UnitOfWork) SaveChanges added in v0.3.0

func (u *UnitOfWork) SaveChanges(ctx context.Context) (retErr error)

SaveChanges flushes all staged changes within a single transaction and, on success, dispatches recorded domain events to after-commit hooks. The unit of work may continue to be used after a successful SaveChanges.

type UoWRepository added in v0.3.0

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

UoWRepository provides tracked query and mutation access for a model within a UnitOfWork. Reads run on the primary connection (read-your-writes) and track their results; Add/Remove/Attach/Detach stage changes for the next SaveChanges.

func NewUoWRepository added in v0.3.0

func NewUoWRepository[T any](uow *UnitOfWork, meta ModelMeta[T]) *UoWRepository[T]

NewUoWRepository creates a tracked repository bound to a UnitOfWork.

func (*UoWRepository[T]) Add added in v0.3.0

func (r *UoWRepository[T]) Add(entity *T)

Add stages an entity for insertion on the next SaveChanges.

func (*UoWRepository[T]) All added in v0.3.0

func (r *UoWRepository[T]) All(ctx context.Context) ([]*T, error)

All returns all records, tracking them.

func (*UoWRepository[T]) AsNoTracking added in v0.3.0

func (r *UoWRepository[T]) AsNoTracking() *QueryBuilder[T]

AsNoTracking returns a query builder whose results are not tracked.

func (*UoWRepository[T]) Attach added in v0.3.0

func (r *UoWRepository[T]) Attach(entity *T, state EntityState)

Attach begins tracking an externally-constructed entity in the given state.

func (*UoWRepository[T]) Count added in v0.3.0

func (r *UoWRepository[T]) Count(ctx context.Context) (int, error)

Count returns the number of matching records.

func (*UoWRepository[T]) Detach added in v0.3.0

func (r *UoWRepository[T]) Detach(entity *T)

Detach stops tracking an entity.

func (*UoWRepository[T]) Exists added in v0.3.0

func (r *UoWRepository[T]) Exists(ctx context.Context) (bool, error)

Exists reports whether any matching record exists.

func (*UoWRepository[T]) Find added in v0.3.0

func (r *UoWRepository[T]) Find(ctx context.Context, id any) (*T, error)

Find looks up a record by primary key and tracks it.

func (*UoWRepository[T]) First added in v0.3.0

func (r *UoWRepository[T]) First(ctx context.Context) (*T, error)

First returns the first record (or ErrNotFound), tracking it.

func (*UoWRepository[T]) HardRemove added in v0.3.0

func (r *UoWRepository[T]) HardRemove(entity *T) error

HardRemove stages a permanent delete, bypassing soft delete.

func (*UoWRepository[T]) Include added in v0.3.0

func (r *UoWRepository[T]) Include(rels ...IncludeSpec) *IncludableQuery[T]

Include begins a tracked query that eagerly loads the given relationships.

func (*UoWRepository[T]) OrderBy added in v0.3.0

func (r *UoWRepository[T]) OrderBy(exprs ...OrderExpr) *QueryBuilder[T]

OrderBy starts an ordered, tracked query.

func (*UoWRepository[T]) Remove added in v0.3.0

func (r *UoWRepository[T]) Remove(entity *T) error

Remove stages a tracked entity for deletion on the next SaveChanges.

func (*UoWRepository[T]) Unscoped added in v0.3.0

func (r *UoWRepository[T]) Unscoped() *QueryBuilder[T]

Unscoped returns a tracked query builder with all global filters removed.

func (*UoWRepository[T]) Where added in v0.3.0

func (r *UoWRepository[T]) Where(pred Predicate) *QueryBuilder[T]

Where starts a filtered, tracked query.

type UpsertOption

type UpsertOption func(*upsertConfig)

UpsertOption configures BulkUpsert behavior.

func ConflictColumns

func ConflictColumns[C interface{ Name() string }](cols ...C) UpsertOption

ConflictColumns specifies which columns form the conflict target for an upsert.

func UpdateOnConflict

func UpdateOnConflict[C interface{ Name() string }](cols ...C) UpsertOption

UpdateOnConflict specifies which columns to update when a conflict is detected.

type Versioned

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

func (Versioned) Version

func (v Versioned) Version() int

func (*Versioned) VersionPtr

func (v *Versioned) VersionPtr() *int

Directories

Path Synopsis
cmd
drel command
examples
api command
Example: api
Example: api
bulk-ops command
Example: bulk-ops
Example: bulk-ops
getting-started command
Example: getting-started
Example: getting-started
internals command
Example: internals
Example: internals
model-features command
Example: model-features
Example: model-features
multi-model command
Example: multi-model
Example: multi-model
relationships command
Example: relationships
Example: relationships
sqlite-todo command
Example: sqlite-todo
Example: sqlite-todo
internal
ast

Jump to

Keyboard shortcuts

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