backend

package
v0.17.1 Latest Latest
Warning

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

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

Documentation

Overview

Package backend defines the contract every den storage engine implements: the Backend interface, plus the supporting types passed across it (Query, LockMode, AggregateOp, etc.). Concrete backend implementations live in subpackages such as den/backend/sqlite and den/backend/postgres; they register themselves at init time via github.com/oliverandrich/den.RegisterBackend.

Most application code does not import this package directly — it works with backends through den's QuerySet and CRUD entry points. Direct imports are useful when building a custom backend, admin tooling, or migration logic that needs to dispatch on backend capabilities.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AggregateOp

type AggregateOp string

AggregateOp identifies a SQL aggregate function.

const (
	OpSum   AggregateOp = "SUM"
	OpAvg   AggregateOp = "AVG"
	OpMin   AggregateOp = "MIN"
	OpMax   AggregateOp = "MAX"
	OpCount AggregateOp = "COUNT"
)

type Backend

type Backend interface {
	Get(ctx context.Context, collection, id string) ([]byte, error)
	Put(ctx context.Context, collection, id string, data []byte) error
	Delete(ctx context.Context, collection, id string) error

	Query(ctx context.Context, collection string, q *Query) (Iterator, error)
	Count(ctx context.Context, collection string, q *Query) (int64, error)
	Exists(ctx context.Context, collection string, q *Query) (bool, error)
	Aggregate(ctx context.Context, collection string, op AggregateOp, field string, q *Query) (*float64, error)
	GroupBy(ctx context.Context, collection string, groupFields []string, aggs []GroupByAgg, q *Query) ([]GroupByRow, error)

	EnsureIndex(ctx context.Context, collection string, idx IndexDefinition) error
	DropIndex(ctx context.Context, collection string, name string) error
	ListRecordedIndexes(ctx context.Context, collection string) ([]RecordedIndex, error)

	EnsureCollection(ctx context.Context, name string, meta CollectionMeta) error
	DropCollection(ctx context.Context, name string) error

	Begin(ctx context.Context) (Transaction, error)

	Ping(ctx context.Context) error
	Close() error
}

Backend defines the contract that all storage engines must implement.

Byte-ownership contract

Any []byte returned from a Backend method (Get, Iterator.Bytes, Transaction.GetForUpdate, …) MUST be caller-owned: implementations must return a fresh slice that does not alias any internal scratch buffer, and must not retain a reference to it after returning. Callers may keep the slice indefinitely and may mutate it without affecting backend state.

Conversely, any []byte argument passed INTO a Backend method (Put's data, …) is borrowed for the duration of the call only: implementations must not retain a reference past return. Callers may reuse or mutate the slice afterwards.

Den relies on these guarantees to skip defensive copies in change tracking and link hydration. Both bundled backends (SQLite via database/sql.Rows.Scan, PostgreSQL via pgx.Rows.Scan) honor the contract — Scan into a *[]byte target is documented by both libraries to allocate a fresh caller-owned slice per call.

type CollectionMeta

type CollectionMeta struct {
	Name              string
	Fields            []FieldMeta
	Indexes           []IndexDefinition
	HasSoftDelete     bool
	HasRevision       bool
	HasChangeTracking bool
}

CollectionMeta holds structural metadata for a registered collection.

HasSoftDelete is derived from the document struct: true when the type embeds document.SoftDelete (detected structurally via the `_deleted_at` JSON field). HasRevision reflects DenSettings.UseRevision — a runtime flag on the collection, not a structural property. HasChangeTracking is true when the type implements document.Trackable (typically by embedding document.Tracked); since the snapshot lives only in memory it has no persistence impact, but tooling that walks Meta can use the flag to know which collections expose IsChanged / GetChanges / Revert.

type FieldMeta

type FieldMeta struct {
	Name      string
	GoName    string
	Type      string
	Indexed   bool
	Unique    bool
	FTS       bool
	IsPointer bool
}

FieldMeta describes a single field within a collection.

type GroupByAgg

type GroupByAgg struct {
	Op    AggregateOp
	Field string // source field (ignored for OpCount)
}

GroupByAgg describes a single aggregate expression in a GROUP BY query.

type GroupByRow

type GroupByRow struct {
	Keys   []string  // group key values (text representation), one per group field
	Values []float64 // aggregate values, matching GroupByAgg order
}

GroupByRow holds one result row from a GROUP BY query. Keys holds one entry per field passed to GroupBy (in the same order); Values holds one entry per GroupByAgg in the order they were requested.

type GroupBySortEntry

type GroupBySortEntry struct {
	Op    AggregateOp
	Field string
	Dir   SortDirection
}

GroupBySortEntry describes an ORDER BY entry over an aggregate expression inside a GROUP BY query. Op selects which aggregate column to order by; Field names the aggregate's source field (ignored for OpCount).

type IndexDefinition

type IndexDefinition struct {
	Name   string
	Fields []string
	Unique bool
}

IndexDefinition describes a secondary index on a collection.

type Iterator

type Iterator interface {
	Next() bool
	Bytes() []byte
	ID() string
	Err() error
	Close() error
}

Iterator provides sequential access to query results.

Bytes returns the current row's document bytes. The returned slice is caller-owned per the Backend byte-ownership contract — implementations must allocate a fresh slice per row (typically via Scan into a *[]byte target). Callers may retain the slice beyond the next Next() call; Next() does not invalidate or mutate previously returned slices.

type LinkFieldMeta added in v0.17.0

type LinkFieldMeta struct {
	JSONName         string       // JSON key of the field
	GoName           string       // Go struct field name
	Slice            bool         // true for []Link[T], false for Link[T]
	Eager            bool         // true when tagged den:"eager"
	TargetCollection string       // collection of T; "" if T is not registered
	TargetType       reflect.Type // T in Link[T]
}

LinkFieldMeta describes a single Link[T] / []Link[T] relation field on a document type. Link fields are not part of CollectionMeta.Fields (they reference other collections rather than storing scalar data); enumerate them with den.LinkFields to validate or allowlist expandable relations.

type LockMode

type LockMode int

LockMode selects the row-locking behavior used by GetForUpdate.

const (
	// LockDefault acquires the lock and blocks if another transaction holds it.
	LockDefault LockMode = iota
	// LockSkipLocked returns no row (ErrNotFound) if another transaction
	// already holds the lock. Mapped to FOR UPDATE SKIP LOCKED on PostgreSQL.
	LockSkipLocked
	// LockNoWait returns ErrLocked immediately if another transaction
	// already holds the lock. Mapped to FOR UPDATE NOWAIT on PostgreSQL.
	LockNoWait
)

type Query

type Query struct {
	Collection string
	Conditions []where.Condition
	SortFields []SortEntry
	LimitN     int // 0 = no limit
	SkipN      int // 0 = no skip
	AfterID    string
	BeforeID   string
	// Lock requests a row-level lock on every matching row (PostgreSQL
	// only; SQLite ignores it because IMMEDIATE tx already serializes
	// writers). nil means no lock; a non-nil pointer's value selects the
	// lock mode. The pointer form rules out the previously-possible
	// invalid pair of (ForUpdate=false, LockMode!=LockDefault).
	Lock *LockMode

	// GroupBySort carries ORDER BY entries that target aggregates in a
	// GROUP BY query. SortFields are used for group-key ordering; aggregate
	// ordering needs the (Op, Field) tuple because no source-field name
	// identifies a synthetic aggregate column. Only consumed by GroupBy
	// paths — other terminals ignore this slice.
	GroupBySort []GroupBySortEntry
}

Query represents an abstract query that backends translate into their native query mechanism.

type ReadWriter

type ReadWriter interface {
	Get(ctx context.Context, collection, id string) ([]byte, error)
	Put(ctx context.Context, collection, id string, data []byte) error
	Delete(ctx context.Context, collection, id string) error
	Query(ctx context.Context, collection string, q *Query) (Iterator, error)
	Count(ctx context.Context, collection string, q *Query) (int64, error)
	Exists(ctx context.Context, collection string, q *Query) (bool, error)
	Aggregate(ctx context.Context, collection string, op AggregateOp, field string, q *Query) (*float64, error)
	GroupBy(ctx context.Context, collection string, groupFields []string, aggs []GroupByAgg, q *Query) ([]GroupByRow, error)
}

ReadWriter is the common interface for both Backend and Transaction, providing the core CRUD operations that all write paths need.

type RecordedIndex

type RecordedIndex struct {
	Name   string
	Fields []string
	Unique bool
}

RecordedIndex describes a secondary index that was previously created by Den and is tracked in the backend's metadata table. Managed indexes (such as the PostgreSQL GIN index or FTS auxiliary objects) are not recorded.

type SortDirection

type SortDirection int

SortDirection specifies ascending or descending sort order.

const (
	Asc SortDirection = iota
	Desc
)

type SortEntry

type SortEntry struct {
	Field string
	Dir   SortDirection
}

SortEntry defines a single sort criterion.

type Transaction

type Transaction interface {
	ReadWriter
	Commit() error
	Rollback() error

	// GetForUpdate reads a document and acquires a row-level lock that
	// persists until the transaction commits or rolls back. On PostgreSQL
	// this maps to SELECT ... FOR UPDATE, optionally with SKIP LOCKED or
	// NOWAIT. On SQLite it is a no-op because IMMEDIATE transactions
	// already serialize writers; the mode parameter is ignored.
	GetForUpdate(ctx context.Context, collection, id string, mode LockMode) ([]byte, error)

	// AdvisoryLock acquires an application-defined lock identified by key
	// that persists until the transaction commits or rolls back. Concurrent
	// transactions attempting to acquire the same key block until the holder
	// ends. Unlike GetForUpdate this does not require a row to exist, so it
	// is suitable for bootstrap paths like coordinating concurrent migration
	// starters before any state row has been written.
	//
	// On PostgreSQL this maps to pg_advisory_xact_lock. On SQLite it is a
	// no-op because IMMEDIATE transactions already serialize writers on the
	// whole database.
	AdvisoryLock(ctx context.Context, key int64) error
}

Transaction provides CRUD operations within a transaction boundary.

Directories

Path Synopsis
Package postgres implements the Den backend for PostgreSQL via jackc/pgx/v5.
Package postgres implements the Den backend for PostgreSQL via jackc/pgx/v5.
Package sqlite implements the Den backend for SQLite via modernc.org/sqlite (pure Go, no CGO).
Package sqlite implements the Den backend for SQLite via modernc.org/sqlite (pure Go, no CGO).

Jump to

Keyboard shortcuts

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