norm

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2025 License: MIT Imports: 17 Imported by: 0

README

norm (Next ORM)

Production-ready, lightweight ORM and query builder for PostgreSQL on top of PGX v5. Ships with connection pooling, automatic migrations from struct tags, a fluent query builder, generic repository, soft delete, optimistic locking, transactions, read/write splitting, retry/backoff, a circuit breaker, and comprehensive e2e tests.

Features
  • Fast, reliable connections via PGX v5 (pgxpool)
  • Flexible Config: pool limits, timeouts, statement cache, app name, etc.
  • Auto-migration from struct tags: tables/columns/indexes/FKs, idempotent plan, transactional apply, rename diffs, type/nullability warnings
  • Query builder: Select/Where/Join/OrderBy/Limit/Offset, Raw, First/Last, Delete (soft by default, HardDelete() to force hard), INSERT ... RETURNING, ON CONFLICT DO UPDATE
  • Condition DSL: Eq/Ne/Gt/Ge/Lt/Le/In/And/Or, date helpers
  • Keyset pagination: After/Before
  • Repository: generic CRUD, bulk create, partial update, soft delete, scopes, optimistic locking
  • Transactions: TxManager, transaction-bound QueryBuilder
  • Read/Write splitting: optional read pool and transparent routing
  • Retry: exponential backoff
  • Circuit Breaker: optional open/half-open/closed with metrics hooks

Note: OpenTelemetry/Prometheus integrations are not included yet.

Install
go get github.com/kintsdev/norm
Quick Start
package main

import (
    "context"
    "time"

    "github.com/kintsdev/norm"
)

type User struct {
    ID        int64      `db:"id" norm:"primary_key,auto_increment"`
    Email     string     `db:"email" norm:"unique,not_null,index,varchar(255)"`
    Username  string     `db:"username" norm:"unique,not_null,varchar(50)"`
    Password  string     `db:"password" norm:"not_null,varchar(255)"`
    IsActive  bool       `db:"is_active" norm:"default:true"`
    CreatedAt time.Time  `db:"created_at" norm:"not_null,default:now()"`
    UpdatedAt time.Time  `db:"updated_at" norm:"not_null,default:now(),on_update:now()"`
    DeletedAt *time.Time `db:"deleted_at" norm:"index"`
    Version   int64      `db:"version" norm:"version"`
}

func main() {
    cfg := &norm.Config{
        Host: "127.0.0.1", Port: 5432, Database: "postgres", Username: "postgres", Password: "postgres",
        SSLMode: "disable", StatementCacheCapacity: 256,
    }
    kn, _ := norm.New(cfg)
    defer kn.Close()

    // Auto-migrate schema from struct tags
    _ = kn.AutoMigrate(&User{})

    // Repository
    repo := norm.NewRepository[User](kn)
    _ = repo.Create(context.Background(), &User{Email: "u@example.com", Username: "u", Password: "x"})

    // Query builder
    var users []User
    _ = kn.Query().Table("users").Where("is_active = ?", true).OrderBy("id ASC").Limit(10).Find(context.Background(), &users)
}
Struct Tags
  • db:"column_name": Column name; if empty, the field name is converted to snake_case.
  • norm:"...": Primary tag for schema/behavior (legacy orm:"..." still works as a fallback).

Supported norm tokens (mix and match, comma separated):

  • Primary key: primary_key, composite via primary_key:group
  • Auto-increment identity: auto_increment
  • Unique: unique, composite via unique:group, optional index name via unique_name:name
  • Indexing: index, index:name, index method using:gin|btree|hash, partial index index_where:(expr)
  • Foreign keys: fk:other_table(other_id), fk_name:name, actions on_delete:cascade|restrict|set null|set default, optional deferrable, initially_deferred
  • Nullability: not_null, or explicit nullable
  • Default: default:<expr> (e.g., default:now())
  • On update: on_update:now() (repository auto-sets NOW() on update for such columns)
  • Version column for optimistic locking: version (treated as BIGINT)
  • Rename diff: rename:old_column
  • Collation: collate:<name>
  • Comment: comment:...
  • Type override: type:decimal(20,8) or direct types like varchar(50), text, timestamptz, numeric(10,2), citext
  • Ignore field: - or ignore (excluded from migrations and insert/update helpers)

Examples:

// Composite unique
Slug   string `db:"slug" norm:"not_null,unique:tenant_slug"`
Tenant int64  `db:"tenant_id" norm:"not_null,unique:tenant_slug,unique_name:uq_accounts_tenant_slug"`

// Partial index and method
Email  string `db:"email" norm:"index,using:gin,index_where:(deleted_at IS NULL)"`

// Decimal override
Amount float64 `db:"amount" norm:"type:decimal(20,8)"`

// FK with actions
UserID int64 `db:"user_id" norm:"not_null,fk:users(id),on_delete:cascade,fk_name:fk_posts_user"`
Migrations
  • Plan/preview: reads current schema via information_schema, builds a safe plan
  • Creates tables/columns, composite indexes/uniques, and foreign keys (with actions)
  • Rename-safe diffs: ALTER TABLE ... RENAME COLUMN ...
  • Type/nullability changes produce warnings and unsafe statements
  • Transactional apply with advisory lock
  • Records checksums in schema_migrations (idempotent)

Manual migrations (file-based Up/Down) and rollback support exist with safety guards; see migration package and tests.

Read/Write Splitting, Retry, Circuit Breaker
  • If Config.ReadOnlyConnString is set, a read pool is opened and Query() routes read queries there automatically. Writes go to primary.
  • Override per-query: UsePrimary() or UseReadPool().
  • Retry with RetryAttempts and RetryBackoff (exponential + jitter).
  • Circuit breaker: CircuitBreakerEnabled, CircuitFailureThreshold, CircuitOpenTimeout, CircuitHalfOpenMaxCalls.
Optional Cache Hooks
  • Provide a cache via WithCache(cache) (e.g., a Redis adapter)
  • Read-through: Query().WithCacheKey(key, ttl).Find/First
  • Invalidation: WithInvalidateKeys(keys...).Exec/Insert/Update/Delete
Testing
  • Make targets spin up Postgres 17.5 in Docker and run e2e tests
make db-up
make test-e2e
make db-down
Benchmarks

Micro and end-to-end benchmarks are included. Run micro (no DB required) or full (requires Postgres env like the e2e tests).

Run all benchmarks (micro + e2e):

go test -bench=. -benchmem -run=^$ ./...

Only micro (root package):

go test -bench=. -benchmem -run=^$

Examples (Apple M3, Go 1.22, local PG):

  • Placeholder conversion and builder (ns–µs level)

    • ConvertQMarksToPgPlaceholders: ~250 ns/op, 208 B/op, 9 alloc/op
    • ConvertNamedToPgPlaceholders (scalars/reuse, slice expansion): ~390–690 ns/op
    • StructMapper (cached): ~9 ns/op, 0 alloc/op
    • Build SELECT with JOINs: ~1.1 µs/op
  • E2E (depends on DB latency)

    • FindPage (COUNT + SELECT): ~0.3 ms/op
    • Scan 100 rows: ~0.25–0.30 ms/op
    • CopyFrom(500 rows): ~0.08 ms/op
    • Single-row writes (Insert/Upsert/Tx): ~6–7 ms/op

Notes:

  • Results vary by CPU, Go version, and Postgres settings; numbers above are indicative.
  • Micro benchmarks live in bench_test.go, e2e in e2e/bench_e2e_test.go.
License

This project is licensed under the MIT License. See the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EagerLoadMany

func EagerLoadMany[T any, R any](ctx context.Context, kn *KintsNorm, parents []*T, getParentID func(*T) any, childForeignKey string, set func(parent *T, children []*R)) error

EagerLoadMany loads related rows of type R for the given parents of type T using childForeignKey. getParentID extracts the parent id used to match against childForeignKey. It groups children by childForeignKey matching each parent's id and invokes set(parent, children).

func LazyLoadMany

func LazyLoadMany[R any](ctx context.Context, kn *KintsNorm, parentID any, childForeignKey string) ([]*R, error)

LazyLoadMany loads children by a single parent ID via childForeignKey

func QuoteIdentifier

func QuoteIdentifier(identifier string) string

QuoteIdentifier safely quotes a SQL identifier by wrapping in double quotes and escaping embedded quotes

Types

type AfterCreate

type AfterCreate interface {
	AfterCreate(ctx context.Context) error
}

AfterCreate can be implemented by a model to run logic after insert

type AfterDelete

type AfterDelete interface {
	AfterDelete(ctx context.Context, id any) error
}

type AfterPurgeTrashed

type AfterPurgeTrashed interface {
	AfterPurgeTrashed(ctx context.Context, affected int64) error
}

type AfterRestore

type AfterRestore interface {
	AfterRestore(ctx context.Context, id any) error
}

type AfterSoftDelete

type AfterSoftDelete interface {
	AfterSoftDelete(ctx context.Context, id any) error
}

type AfterUpdate

type AfterUpdate interface {
	AfterUpdate(ctx context.Context) error
}

AfterUpdate can be implemented by a model to run logic after update

type AfterUpsert

type AfterUpsert interface {
	AfterUpsert(ctx context.Context) error
}

AfterUpsert can be implemented by a model to run logic after upsert

type BeforeCreate

type BeforeCreate interface {
	BeforeCreate(ctx context.Context) error
}

BeforeCreate can be implemented by a model to run logic before insert

type BeforeDelete

type BeforeDelete interface {
	BeforeDelete(ctx context.Context, id any) error
}

Delete hooks

type BeforePurgeTrashed

type BeforePurgeTrashed interface {
	BeforePurgeTrashed(ctx context.Context) error
}

PurgeTrashed hooks

type BeforeRestore

type BeforeRestore interface {
	BeforeRestore(ctx context.Context, id any) error
}

Restore hooks

type BeforeSoftDelete

type BeforeSoftDelete interface {
	BeforeSoftDelete(ctx context.Context, id any) error
}

SoftDelete hooks

type BeforeUpdate

type BeforeUpdate interface {
	BeforeUpdate(ctx context.Context) error
}

BeforeUpdate can be implemented by a model to run logic before update

type BeforeUpsert

type BeforeUpsert interface {
	BeforeUpsert(ctx context.Context) error
}

BeforeUpsert can be implemented by a model to run logic before upsert

type Cache

type Cache interface {
	Get(ctx context.Context, key string) ([]byte, bool, error)
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
	Invalidate(ctx context.Context, keys ...string) error
}

Cache provides optional read-through/write-through hooks

type Condition

type Condition struct {
	Expr string
	Args []any
}

func And

func And(conds ...Condition) Condition

func Between

func Between(col string, start any, end any) Condition

Between builds a generic BETWEEN condition inclusive of both ends

func DateRange

func DateRange(col string, from, to time.Time) Condition

DateRange returns a timestamp range condition inclusive of boundaries

func Eq

func Eq(col string, v any) Condition

func Ge

func Ge(col string, v any) Condition

func Gt

func Gt(col string, v any) Condition

func In

func In(col string, vals []any) Condition

func Le

func Le(col string, v any) Condition

func Lt

func Lt(col string, v any) Condition

func Ne

func Ne(col string, v any) Condition

func OnDate

func OnDate(col string, day time.Time) Condition

OnDate matches rows where timestamp column falls on the given calendar day (UTC-based start/end)

func Or

func Or(conds ...Condition) Condition

func RawCond

func RawCond(expr string, args ...any) Condition

type Config

type Config struct {
	Host                   string
	Port                   int
	Database               string
	Username               string
	Password               string
	SSLMode                string
	MaxConnections         int32
	MinConnections         int32
	MaxConnLifetime        time.Duration
	MaxConnIdleTime        time.Duration
	HealthCheckPeriod      time.Duration
	ConnectTimeout         time.Duration
	ApplicationName        string
	ReadOnlyConnString     string        // optional DSN for read replica(s)
	RetryAttempts          int           // transient error retries (default 0 = no retry)
	RetryBackoff           time.Duration // backoff between retries
	StatementCacheCapacity int           // pgx per-conn statement cache capacity (0 = default)
	// Circuit breaker
	CircuitBreakerEnabled   bool
	CircuitFailureThreshold int           // consecutive failures to open the circuit (default 5 if 0)
	CircuitOpenTimeout      time.Duration // how long to stay open before half-open trial (default 30s if 0)
	CircuitHalfOpenMaxCalls int           // allowed concurrent trial calls in half-open (default 1 if 0)
}

Config holds database and runtime configuration for Kints-Norm

func (*Config) ConnString

func (c *Config) ConnString() string

ConnString returns a PostgreSQL connection string compatible with pgx

type ErrorCode

type ErrorCode int
const (
	ErrCodeConnection ErrorCode = iota
	ErrCodeNotFound
	ErrCodeDuplicate
	ErrCodeConstraint
	ErrCodeTransaction
	ErrCodeMigration
	ErrCodeValidation
	// Specific validation subtypes
	ErrCodeInvalidColumn
	ErrCodeInvalidFunction
	ErrCodeInvalidCast
	ErrCodeStringTooLong
)

type ExpvarMetrics

type ExpvarMetrics struct{}

ExpvarMetrics is a simple stdlib adapter using expvar to expose counters/gauges It is intended as a minimal example adapter; for production use prefer Prometheus/OpenTelemetry. Avoids extra dependencies.

func (ExpvarMetrics) CircuitStateChanged

func (ExpvarMetrics) CircuitStateChanged(state string)

func (ExpvarMetrics) ConnectionCount

func (ExpvarMetrics) ConnectionCount(active, idle int32)

func (ExpvarMetrics) ErrorCount

func (ExpvarMetrics) ErrorCount(errorType string)

func (ExpvarMetrics) QueryDuration

func (ExpvarMetrics) QueryDuration(duration time.Duration, _ string)

type Field

type Field struct {
	Key   string
	Value any
}

Field represents a structured logging field

type KintsNorm

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

KintsNorm is the main ORM entry point

func New

func New(config *Config, opts ...Option) (*KintsNorm, error)

New creates a new KintsNorm instance, initializing the pgx pool

func NewWithConnString

func NewWithConnString(connString string, opts ...Option) (*KintsNorm, error)

NewWithConnString creates a new KintsNorm instance from a full pgx connection string

func (*KintsNorm) AutoMigrate

func (kn *KintsNorm) AutoMigrate(models ...any) error

AutoMigrate runs schema migrations for given models

func (*KintsNorm) AutoMigrateWithOptions

func (kn *KintsNorm) AutoMigrateWithOptions(ctx context.Context, opts migration.ApplyOptions, models ...any) error

AutoMigrateWithOptions allows enabling destructive ops (e.g., drop columns)

func (*KintsNorm) Close

func (kn *KintsNorm) Close() error

Close gracefully closes the connection pool

func (*KintsNorm) Health

func (kn *KintsNorm) Health(ctx context.Context) error

Health performs a simple health check against the database

func (*KintsNorm) MigrateDownDir

func (kn *KintsNorm) MigrateDownDir(ctx context.Context, dir string, steps int) error

MigrateDownDir rolls back the last N migrations using .down.sql files

func (*KintsNorm) MigrateUpDir

func (kn *KintsNorm) MigrateUpDir(ctx context.Context, dir string) error

MigrateUpDir applies pending .up.sql migrations from a directory

func (*KintsNorm) Model

func (kn *KintsNorm) Model(model any) *QueryBuilder

Model initializes a new query builder and sets its table name inferred from the provided model type. Usage: kn.Model(&User{}).Where("id = ?", 1).First(ctx, &u)

func (*KintsNorm) Pool

func (kn *KintsNorm) Pool() *pgxpool.Pool

Pool exposes the underlying pgx pool (read-only)

func (*KintsNorm) Query

func (kn *KintsNorm) Query() *QueryBuilder

Query creates a new query builder

func (*KintsNorm) QueryRead

func (kn *KintsNorm) QueryRead() *QueryBuilder

QueryRead uses the read pool for building queries (falls back to primary)

func (*KintsNorm) ReadPool

func (kn *KintsNorm) ReadPool() *pgxpool.Pool

ReadPool exposes the read-only replica pool if configured, otherwise returns the primary pool

func (*KintsNorm) SetManualMigrationOptions

func (kn *KintsNorm) SetManualMigrationOptions(opts migration.ManualOptions)

SetManualMigrationOptions configures safety gates for manual file-based migrations

func (*KintsNorm) Tx

func (kn *KintsNorm) Tx() TxManager

type LogMode

type LogMode int

LogMode controls verbosity of ORM logging

const (
	// LogSilent disables all logs unless a chain explicitly enables debug
	LogSilent LogMode = iota
	// LogError logs only errors
	LogError
	// LogWarn logs warnings and errors (unused for now but reserved)
	LogWarn
	// LogInfo logs queries and errors
	LogInfo
	// LogDebug logs everything at debug level
	LogDebug
)

type Logger

type Logger interface {
	Debug(msg string, fields ...Field)
	Info(msg string, fields ...Field)
	Warn(msg string, fields ...Field)
	Error(msg string, fields ...Field)
}

type Metrics

type Metrics interface {
	QueryDuration(duration time.Duration, query string)
	ConnectionCount(active, idle int32)
	ErrorCount(errorType string)
	// Circuit breaker metrics
	CircuitStateChanged(state string)
}

type NoopCache

type NoopCache struct{}

NoopCache is a default no-op cache implementation

func (NoopCache) Get

func (NoopCache) Get(ctx context.Context, key string) ([]byte, bool, error)

func (NoopCache) Invalidate

func (NoopCache) Invalidate(ctx context.Context, keys ...string) error

func (NoopCache) Set

func (NoopCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

type NoopLogger

type NoopLogger struct{}

NoopLogger is a default no-op logger

func (NoopLogger) Debug

func (NoopLogger) Debug(msg string, fields ...Field)

func (NoopLogger) Error

func (NoopLogger) Error(msg string, fields ...Field)

func (NoopLogger) Info

func (NoopLogger) Info(msg string, fields ...Field)

func (NoopLogger) Warn

func (NoopLogger) Warn(msg string, fields ...Field)

type NoopMetrics

type NoopMetrics struct{}

NoopMetrics is a default no-op metrics collector

func (NoopMetrics) CircuitStateChanged

func (NoopMetrics) CircuitStateChanged(state string)

func (NoopMetrics) ConnectionCount

func (NoopMetrics) ConnectionCount(active, idle int32)

func (NoopMetrics) ErrorCount

func (NoopMetrics) ErrorCount(errorType string)

func (NoopMetrics) QueryDuration

func (NoopMetrics) QueryDuration(duration time.Duration, query string)

type ORMError

type ORMError struct {
	Code     ErrorCode
	Message  string
	Internal error
	Query    string
	Args     []any
}

ORMError is a structured error for norm

func (*ORMError) Error

func (e *ORMError) Error() string

type Option

type Option func(*options)

func WithCache

func WithCache(c Cache) Option

func WithLogContextFields

func WithLogContextFields(fn func(ctx context.Context) []Field) Option

WithLogContextFields registers a function to derive structured log fields from context (e.g. correlation/request IDs)

func WithLogMode

func WithLogMode(mode LogMode) Option

WithLogMode sets global logging mode similar to GORM's LogMode

func WithLogParameterMasking

func WithLogParameterMasking(mask bool) Option

WithLogParameterMasking masks SQL parameters in logs (hides args and avoids inlining into stmt)

func WithLogger

func WithLogger(l Logger) Option

func WithMetrics

func WithMetrics(m Metrics) Option

func WithSlowQueryThreshold

func WithSlowQueryThreshold(threshold time.Duration) Option

WithSlowQueryThreshold enables slow query logging when duration exceeds threshold

type Page

type Page[T any] struct {
	Items  []*T
	Total  int64
	Limit  int
	Offset int
}

Page represents a paginated result

type PageRequest

type PageRequest struct {
	Limit   int
	Offset  int
	OrderBy string // e.g., "id ASC" or "created_at DESC"
}

PageRequest describes pagination and ordering

type QueryBuilder

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

QueryBuilder provides a fluent API for building SQL queries

func (*QueryBuilder) After

func (qb *QueryBuilder) After(column string, value any) *QueryBuilder

Keyset pagination helpers

func (*QueryBuilder) Before

func (qb *QueryBuilder) Before(column string, value any) *QueryBuilder

func (*QueryBuilder) CrossJoin

func (qb *QueryBuilder) CrossJoin(table string) *QueryBuilder

CrossJoin appends a CROSS JOIN clause (no ON condition)

func (*QueryBuilder) Debug

func (qb *QueryBuilder) Debug() *QueryBuilder

Debug enables debug logging for this builder chain regardless of global LogMode

func (*QueryBuilder) Delete

func (qb *QueryBuilder) Delete(ctx context.Context) (int64, error)

Delete executes a DELETE FROM ... WHERE ... and returns rows affected

func (*QueryBuilder) DoUpdateSet

func (qb *QueryBuilder) DoUpdateSet(setExpr string, args ...any) *QueryBuilder

func (*QueryBuilder) Exec

func (qb *QueryBuilder) Exec(ctx context.Context) error

Exec executes a raw statement

func (*QueryBuilder) ExecInsert

func (qb *QueryBuilder) ExecInsert(ctx context.Context, dest any) (int64, error)

func (*QueryBuilder) ExecUpdate

func (qb *QueryBuilder) ExecUpdate(ctx context.Context, dest any) (int64, error)

func (*QueryBuilder) Find

func (qb *QueryBuilder) Find(ctx context.Context, dest any) error

Find runs the query and scans into dest (slice pointer)

func (*QueryBuilder) First

func (qb *QueryBuilder) First(ctx context.Context, dest any) error

First applies LIMIT 1 and scans the first row into dest (pointer to struct or *[]map[string]any with length 1)

func (*QueryBuilder) FullJoin

func (qb *QueryBuilder) FullJoin(table, on string) *QueryBuilder

FullJoin appends a FULL JOIN clause

func (*QueryBuilder) HardDelete

func (qb *QueryBuilder) HardDelete() *QueryBuilder

HardDelete opts into hard delete for this builder chain

func (*QueryBuilder) InnerJoin

func (qb *QueryBuilder) InnerJoin(table, on string) *QueryBuilder

InnerJoin appends an INNER JOIN clause (alias of Join)

func (*QueryBuilder) Insert

func (qb *QueryBuilder) Insert(columns ...string) *QueryBuilder

Insert builder

func (*QueryBuilder) InsertStruct

func (qb *QueryBuilder) InsertStruct(ctx context.Context, entity any) (int64, error)

InsertStruct inserts a struct using its `db` tags. Zero values with default: tag are skipped to allow DB defaults

func (*QueryBuilder) Join

func (qb *QueryBuilder) Join(table, on string) *QueryBuilder

func (*QueryBuilder) Last

func (qb *QueryBuilder) Last(ctx context.Context, dest any) error

Last requires an explicit OrderBy to be set; applies LIMIT 1 and returns the last row per ordering

func (*QueryBuilder) LeftJoin

func (qb *QueryBuilder) LeftJoin(table, on string) *QueryBuilder

LeftJoin appends a LEFT JOIN clause

func (*QueryBuilder) Limit

func (qb *QueryBuilder) Limit(n int) *QueryBuilder

func (*QueryBuilder) Model

func (qb *QueryBuilder) Model(model any) *QueryBuilder

Model sets the table name by inferring it from a provided model type/value. It follows the same convention used by the repository: snake_case(type name) + "s". Examples:

qb.Model(&User{})
qb.Model(User{})

func (*QueryBuilder) Offset

func (qb *QueryBuilder) Offset(n int) *QueryBuilder

func (*QueryBuilder) OnConflict

func (qb *QueryBuilder) OnConflict(cols ...string) *QueryBuilder

func (*QueryBuilder) OnlyTrashed

func (qb *QueryBuilder) OnlyTrashed() *QueryBuilder

OnlyTrashed restricts to only soft-deleted rows (deleted_at IS NOT NULL)

func (*QueryBuilder) OrderBy

func (qb *QueryBuilder) OrderBy(ob string) *QueryBuilder

func (*QueryBuilder) Raw

func (qb *QueryBuilder) Raw(sql string, args ...any) *QueryBuilder

func (*QueryBuilder) RawNamed

func (qb *QueryBuilder) RawNamed(sql string, namedArgs map[string]any) *QueryBuilder

RawNamed sets a raw SQL with :name placeholders

func (*QueryBuilder) Returning

func (qb *QueryBuilder) Returning(cols ...string) *QueryBuilder

func (*QueryBuilder) RightJoin

func (qb *QueryBuilder) RightJoin(table, on string) *QueryBuilder

RightJoin appends a RIGHT JOIN clause

func (*QueryBuilder) Select

func (qb *QueryBuilder) Select(columns ...string) *QueryBuilder

func (*QueryBuilder) SelectQ

func (qb *QueryBuilder) SelectQ(columns ...string) *QueryBuilder

SelectQ appends quoted column identifiers

func (*QueryBuilder) SelectQI

func (qb *QueryBuilder) SelectQI(columns ...string) *QueryBuilder

SelectQI appends fully quoted identifiers as-is (does not split on '.') Useful for columns that themselves contain dots in their names

func (*QueryBuilder) Set

func (qb *QueryBuilder) Set(setExpr string, args ...any) *QueryBuilder

Update builder (simple form): provide SET expr and args

func (*QueryBuilder) Table

func (qb *QueryBuilder) Table(name string) *QueryBuilder

func (*QueryBuilder) TableQ

func (qb *QueryBuilder) TableQ(name string) *QueryBuilder

TableQ sets the table using quoted identifier(s) (supports schema-qualified like schema.table)

func (*QueryBuilder) Unscoped

func (qb *QueryBuilder) Unscoped() *QueryBuilder

Unscoped is an alias of WithTrashed (GORM-compatible naming)

func (*QueryBuilder) UpdateStructByPK

func (qb *QueryBuilder) UpdateStructByPK(ctx context.Context, entity any, pkColumn string) (int64, error)

UpdateStructByPK updates a row by its primary key using `db` tags

func (*QueryBuilder) UsePrimary

func (qb *QueryBuilder) UsePrimary() *QueryBuilder

UsePrimary routes subsequent calls (Query/Find/First/Last) through the primary pool (overrides auto read routing)

func (*QueryBuilder) UseReadPool

func (qb *QueryBuilder) UseReadPool() *QueryBuilder

UseReadPool forces using the read pool for reads even if no auto routing is enabled Note: Do not use this for writes; Exec/insert/update/delete should go to primary

func (*QueryBuilder) Values

func (qb *QueryBuilder) Values(values ...any) *QueryBuilder

func (*QueryBuilder) ValuesRows

func (qb *QueryBuilder) ValuesRows(rows [][]any) *QueryBuilder

func (*QueryBuilder) Where

func (qb *QueryBuilder) Where(condition string, args ...any) *QueryBuilder

func (*QueryBuilder) WhereCond

func (qb *QueryBuilder) WhereCond(c Condition) *QueryBuilder

WhereCond adds a typed Condition built by helpers in conditions.go

func (*QueryBuilder) WhereNamed

func (qb *QueryBuilder) WhereNamed(condition string, namedArgs map[string]any) *QueryBuilder

WhereNamed adds a WHERE clause with named parameters, converting :name to $n and appending args by map order

func (*QueryBuilder) WithCacheKey

func (qb *QueryBuilder) WithCacheKey(key string, ttl time.Duration) *QueryBuilder

WithCacheKey enables read-through caching for Find/First on this builder. TTL<=0 means no Set.

func (*QueryBuilder) WithInvalidateKeys

func (qb *QueryBuilder) WithInvalidateKeys(keys ...string) *QueryBuilder

WithInvalidateKeys sets keys to invalidate after write operations (Exec/Insert/Update/Delete)

func (*QueryBuilder) WithTrashed

func (qb *QueryBuilder) WithTrashed() *QueryBuilder

WithTrashed includes soft-deleted rows (deleted_at IS NOT NULL or NULL) in results

type Repository

type Repository[T any] interface {
	Create(ctx context.Context, entity *T) error
	CreateBatch(ctx context.Context, entities []*T) error
	GetByID(ctx context.Context, id any) (*T, error)
	Update(ctx context.Context, entity *T) error
	UpdatePartial(ctx context.Context, id any, fields map[string]any) error
	Delete(ctx context.Context, id any) error
	SoftDelete(ctx context.Context, id any) error
	SoftDeleteAll(ctx context.Context) (int64, error)
	Restore(ctx context.Context, id any) error
	PurgeTrashed(ctx context.Context) (int64, error)
	Find(ctx context.Context, conditions ...Condition) ([]*T, error)
	FindOne(ctx context.Context, conditions ...Condition) (*T, error)
	Count(ctx context.Context, conditions ...Condition) (int64, error)
	Exists(ctx context.Context, conditions ...Condition) (bool, error)
	WithTrashed() Repository[T]
	OnlyTrashed() Repository[T]
	FindPage(ctx context.Context, page PageRequest, conditions ...Condition) (Page[T], error)
	CreateCopyFrom(ctx context.Context, entities []*T, columns ...string) (int64, error)
	Upsert(ctx context.Context, entity *T, conflictCols []string, updateCols []string) error
}

Repository defines generic CRUD operations for type T

func NewRepository

func NewRepository[T any](kn *KintsNorm) Repository[T]

NewRepository creates a new generic repository

func NewRepositoryWithExecutor

func NewRepositoryWithExecutor[T any](kn *KintsNorm, exec dbExecuter) Repository[T]

NewRepositoryWithExecutor creates a repository bound to a specific executor (pool or tx)

type StdLogger

type StdLogger struct{}

StdLogger logs to the standard library logger

func (StdLogger) Debug

func (StdLogger) Debug(msg string, fields ...Field)

func (StdLogger) Error

func (StdLogger) Error(msg string, fields ...Field)

func (StdLogger) Info

func (StdLogger) Info(msg string, fields ...Field)

func (StdLogger) Warn

func (StdLogger) Warn(msg string, fields ...Field)

type Transaction

type Transaction interface {
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error
	Repository() Repository[map[string]any]
	Exec() dbExecuter
	Query() *QueryBuilder
}

type TxManager

type TxManager interface {
	WithTransaction(ctx context.Context, fn func(tx Transaction) error) error
	BeginTx(ctx context.Context, opts *TxOptions) (Transaction, error)
}

type TxOptions

type TxOptions struct{}

Directories

Path Synopsis
blog command
decimal command
fk_actions command
ignore_field command
index_partial command
observability command
soft_delete command
tx command
uuid command
internal

Jump to

Keyboard shortcuts

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