psql

package module
v0.5.6 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 25 Imported by: 3

README

Go Reference Build Status Coverage Status Go Report Card

psql

Platform SQL library for Go with object binding, query builder, hooks, associations, and vector support. Works with MySQL, PostgreSQL, CockroachDB, and SQLite.

Similar to GORM but focused on modern Go features (generics, 1.23 iterators) and a lighter footprint.

Quick Start

import (
    "github.com/portablesql/psql"
    _ "github.com/portablesql/psql-sqlite" // import the driver you need
)

// Connect (engine auto-detected from DSN)
be, err := psql.New(":memory:")
ctx := be.Plug(context.Background())

// Define a table
type User struct {
    psql.Name `sql:"users"`
    ID        uint64 `sql:",key=PRIMARY"`
    Name      string `sql:",type=VARCHAR,size=128"`
    Email     string `sql:",type=VARCHAR,size=255"`
}

// CRUD operations
err = psql.Insert(ctx, &User{ID: 1, Name: "Alice", Email: "alice@example.com"})

user, err := psql.Get[User](ctx, map[string]any{"ID": uint64(1)})

user.Name = "Alice Smith"
err = psql.Update(ctx, user)

users, err := psql.Fetch[User](ctx, map[string]any{"Name": "Alice Smith"})

Features

Multi-Engine Support

Import only the driver submodule you need:

import _ "github.com/portablesql/psql-mysql"   // MySQL / MariaDB
import _ "github.com/portablesql/psql-pgsql"   // PostgreSQL / CockroachDB
import _ "github.com/portablesql/psql-sqlite"  // SQLite

DSN format is auto-detected:

be, _ := psql.New("postgresql://...")        // PostgreSQL / CockroachDB
be, _ := psql.New("user:pass@tcp(...)/db")   // MySQL
be, _ := psql.New(":memory:")                // SQLite
Hooks

Lifecycle callbacks via Go interfaces:

func (u *User) BeforeInsert(ctx context.Context) error {
    u.CreatedAt = time.Now()
    return nil
}

func (u *User) BeforeSave(ctx context.Context) error {
    if !strings.Contains(u.Email, "@") {
        return errors.New("invalid email")
    }
    return nil
}

Available hooks: BeforeSave, AfterSave, BeforeInsert, AfterInsert, BeforeUpdate, AfterUpdate, AfterScan.

Associations

Declare relationships and batch-preload to avoid N+1 queries:

type Book struct {
    psql.Name `sql:"books"`
    ID        int64   `sql:",key=PRIMARY"`
    AuthorID  int64   `sql:",type=BIGINT"`
    Title     string  `sql:",type=VARCHAR,size=256"`
    Author    *Author `psql:"belongs_to:AuthorID"`
}

// Fetch books with authors preloaded (2 queries, not N+1)
books, err := psql.Fetch[Book](ctx, nil, psql.WithPreload("Author"))

Supports belongs_to, has_one, has_many, and many_to_many.

Scopes

Reusable query modifiers:

var Active psql.Scope = func(q *psql.QueryBuilder) *psql.QueryBuilder {
    return q.Where(map[string]any{"Status": "active"})
}

func RecentN(n int) psql.Scope {
    return func(q *psql.QueryBuilder) *psql.QueryBuilder {
        return q.OrderBy(psql.S("CreatedAt", "DESC")).Limit(n)
    }
}

users, err := psql.Fetch[User](ctx, nil, psql.WithScope(Active, RecentN(10)))
Lazy Loading

Batch-optimized deferred queries:

future1 := psql.Lazy[User]("ID", "1")
future2 := psql.Lazy[User]("ID", "2")

// Resolving any future batches all pending futures into a single IN query
user1, err := future1.Resolve(ctx)
user2, err := future2.Resolve(ctx) // already resolved by the batch
Soft Delete

Automatic soft delete with timestamp columns:

type Post struct {
    psql.Name `sql:"posts"`
    ID        uint64     `sql:",key=PRIMARY"`
    Title     string     `sql:",type=VARCHAR,size=256"`
    DeletedAt *time.Time `sql:",type=DATETIME"`  // soft delete field (auto-detected)
}

psql.Delete[Post](ctx, map[string]any{"ID": uint64(1)})  // sets DeletedAt
psql.Restore[Post](ctx, map[string]any{"ID": uint64(1)}) // clears DeletedAt
psql.ForceDelete[Post](ctx, map[string]any{"ID": uint64(1)}) // hard DELETE

// Include soft-deleted records
posts, _ := psql.Fetch[Post](ctx, nil, psql.IncludeDeleted())
Iterators (Go 1.23+)
iter, err := psql.Iter[User](ctx, map[string]any{"Status": "active"})
for user := range iter {
    fmt.Println(user.Name)
}
Transactions
err := psql.Tx(ctx, func(ctx context.Context) error {
    psql.Insert(ctx, &user)
    psql.Insert(ctx, &profile)
    return nil // commit; return error to rollback
})

Supports nested transactions via savepoints.

type Item struct {
    psql.Name `sql:"items"`
    ID        uint64      `sql:",key=PRIMARY"`
    Embedding psql.Vector `sql:",type=VECTOR,size=384"`
}

// Nearest neighbor search
query := psql.B().Select("*").From("items").
    OrderBy(psql.VecCosineDistance(psql.F("Embedding"), queryVec)).
    Limit(10)
Query Builder
query := psql.B().
    Select("id", "name").
    From("users").
    Where(
        psql.Equal(psql.F("status"), "active"),
        psql.Gte(psql.F("age"), 18),
    ).
    OrderBy(psql.S("name", "ASC")).
    Limit(50)

rows, err := query.RunQuery(ctx)
Portable Timestamp Arithmetic

Engine-aware date/time helpers that generate correct SQL for all backends:

// Records created in the last 24 hours (works on MySQL, PostgreSQL, and SQLite)
query := psql.B().Select().From("events").
    Where(psql.Gt(psql.F("created_at"), psql.DateSub(psql.Now(), 24*time.Hour)))
// MySQL:      "created_at">NOW() - INTERVAL 1 DAY
// PostgreSQL: "created_at">NOW() - INTERVAL '1 day'
// SQLite:     "created_at">datetime(CURRENT_TIMESTAMP,'-1 days')
Error Helpers
err := psql.Insert(ctx, &duplicateRecord)
if psql.IsDuplicate(err) {
    // handle unique constraint violation (works across all engines)
}
if psql.IsNotExist(err) {
    // handle missing table/column
}
Enum Support
type StatusEnum string
const (
    StatusActive   StatusEnum = "active"
    StatusInactive StatusEnum = "inactive"
)

type Account struct {
    ID     uint64     `sql:",key=PRIMARY"`
    Status StatusEnum `sql:",type=enum,values=active,inactive"`
}

Documentation

Topic Description
Getting Started Installation, connecting, driver imports, basic CRUD
Object Binding Struct tags, column types, keys, enums, custom types
Hooks Lifecycle callbacks, execution order, validation
Associations belongs_to, has_one, has_many, many_to_many, preloading
Query Builder SELECT, WHERE, JOIN, GROUP BY, subqueries, ON CONFLICT
Transactions Transactions, nested savepoints, safe deletion
Vectors Vector columns, similarity search, distance functions
Naming Strategies DefaultNamer, CamelSnakeNamer, LegacyNamer
Scopes & Lazy Reusable scopes, lazy loading, change detection
Soft Delete Automatic soft delete, restore, force delete

Documentation

Overview

Package psql provides a multi-engine SQL ORM and query builder for Go.

It supports MySQL, PostgreSQL (including CockroachDB), and SQLite with automatic table creation, object binding via struct tags, lifecycle hooks, association preloading, vector similarity search, and transactions.

Connecting

Use New to connect with automatic engine detection from the DSN:

be, err := psql.New("postgresql://user:pass@localhost:5432/mydb")
be, err := psql.New("user:pass@tcp(localhost:3306)/mydb")
be, err := psql.New("sqlite:mydata.db")

Attach the backend to a context for all subsequent operations:

ctx := be.Plug(context.Background())

Defining Tables

Map Go structs to database tables using struct tags. Embed Name with an sql tag to set the table name explicitly:

type User struct {
    psql.Name `sql:"users"`
    ID        uint64 `sql:",key=PRIMARY"`
    Email     string `sql:",type=VARCHAR,size=255"`
    Name      string `sql:",type=VARCHAR,size=128"`
}

The sql tag format is "[column_name],attr=value,attr=value,...". Supported attributes include type, size, scale, key, default, and values (for enums). Use sql:"-" to exclude a field. Pointer types are automatically nullable.

Tables are created or updated automatically on first use to match the struct definition.

CRUD Operations

The package provides generic functions for common operations:

// Insert one or more records
err := psql.Insert(ctx, &User{ID: 1, Name: "Alice"})

// Get a single record by key
user, err := psql.Get[User](ctx, map[string]any{"ID": uint64(1)})

// Fetch multiple records
users, err := psql.Fetch[User](ctx, map[string]any{"Name": "Alice"})

// Update a record (only changed fields)
user.Name = "Alice Smith"
err = psql.Update(ctx, user)

// Replace (upsert)
err = psql.Replace(ctx, user)

// Delete
_, err = psql.Delete[User](ctx, map[string]any{"ID": uint64(1)})

// Count
n, err := psql.Count[User](ctx, map[string]any{"Name": "Alice"})

Fetch Options

Control query behavior with FetchOptions:

users, _ := psql.Fetch[User](ctx, nil, psql.Limit(10))
users, _ := psql.Fetch[User](ctx, nil, psql.LimitFrom(20, 10))
users, _ := psql.Fetch[User](ctx, nil, psql.Sort(psql.S("Name", "ASC")))
users, _ := psql.Fetch[User](ctx, nil, psql.FetchLock) // SELECT FOR UPDATE

Iterators

Go 1.23 range iterators are supported via Iter:

iter, err := psql.Iter[User](ctx, map[string]any{"Active": true})
for user := range iter {
    fmt.Println(user.Name)
}

Hooks

Implement hook interfaces on your struct for lifecycle callbacks:

Returning an error from a "Before" hook prevents the operation. Example:

func (u *User) BeforeInsert(ctx context.Context) error {
    if u.CreatedAt.IsZero() {
        u.CreatedAt = time.Now()
    }
    return nil
}

Associations

Declare relationships using the psql struct tag (separate from sql):

type Book struct {
    psql.Name `sql:"books"`
    ID        int64   `sql:",key=PRIMARY"`
    AuthorID  int64   `sql:",type=BIGINT"`
    Author    *Author `psql:"belongs_to:AuthorID"`
}

type Author struct {
    psql.Name `sql:"authors"`
    ID        int64   `sql:",key=PRIMARY"`
    Books     []*Book `psql:"has_many:AuthorID"`
    Profile   *Profile `psql:"has_one:AuthorID"`
}

Use Preload or WithPreload to batch-load associations efficiently:

books, _ := psql.Fetch[Book](ctx, nil, psql.WithPreload("Author"))

Query Builder

Build SQL queries programmatically with B:

rows, err := psql.B().
    Select("id", "name").
    From("users").
    Where(psql.Equal(psql.F("status"), "active")).
    OrderBy(psql.S("name", "ASC")).
    Limit(50).
    RunQuery(ctx)

Use F for field references, V for value literals, S for sort fields, and Raw for raw SQL fragments.

Transactions

Use Tx for callback-based transactions or BeginTx for manual control:

err := psql.Tx(ctx, func(ctx context.Context) error {
    psql.Insert(ctx, &user)
    psql.Insert(ctx, &profile)
    return nil // commit; return error to rollback
})

Nested transactions are supported via SQL savepoints. To run a query outside the current transaction (e.g., logging a failure before rolling back), use the original pre-transaction context or call EscapeTx:

outerCtx := ctx
psql.Tx(ctx, func(ctx context.Context) error {
    if err := psql.Insert(ctx, &order); err != nil {
        psql.Insert(outerCtx, &AuditLog{Event: "failed"}) // persists after rollback
        return err
    }
    return nil
})

Vector Support

Use the Vector type for similarity search with PostgreSQL pgvector or CockroachDB native vectors:

type Item struct {
    psql.Name `sql:"items"`
    ID        uint64 `sql:",key=PRIMARY"`
    Embedding Vector `sql:",type=VECTOR,size=384"`
}

Distance functions: VecL2Distance, VecCosineDistance, VecInnerProduct.

Index

Constants

View Source
const (
	// Use " for ANSI SQL, and ` for MySQL's own thing
	NameQuoteChar = `"`
	NameQuoteRune = '"'
)
View Source
const (
	KeyPrimary  = 1
	KeyUnique   = 2
	KeyIndex    = 3
	KeyFulltext = 4
	KeySpatial  = 5
	KeyVector   = 6
)

Key type constants for StructKey.Typ.

Variables

View Source
var (
	ErrNotReady           = errors.New("database is not ready (no connection is available)")
	ErrNotNillable        = errors.New("field is nil but cannot be nil")
	ErrTxAlreadyProcessed = errors.New("transaction has already been committed or rollbacked")
	ErrDeleteBadAssert    = errors.New("delete operation failed assertion")
	ErrBreakLoop          = errors.New("exiting loop (not an actual error, used to break out of loop callbacks)")
)
View Source
var FetchLock = &FetchOptions{Lock: true}

FetchLock is a FetchOptions that adds SELECT ... FOR UPDATE to lock the selected rows.

View Source
var FetchLockNoWait = &FetchOptions{Lock: true, NoWait: true}

FetchLockNoWait is a FetchOptions that adds FOR UPDATE NOWAIT.

View Source
var FetchLockSkipLocked = &FetchOptions{Lock: true, SkipLocked: true}

FetchLockSkipLocked is a FetchOptions that adds FOR UPDATE SKIP LOCKED.

View Source
var FormatTableName = formatCamelSnakeCase

FormatTableName is a variable that holds the default table name formatter. It defaults to formatCamelSnakeCase but can be overridden. This is kept for backwards compatibility - new code should use Backend.Namer.

View Source
var NumericTypes = map[string]bool{
	"bit":              true,
	"tinyint":          true,
	"tinyint(1)":       false,
	"smallint":         true,
	"mediumint":        true,
	"int":              true,
	"integer":          true,
	"bigint":           true,
	"float":            true,
	"double":           true,
	"double precision": true,
}

NumericTypes lists MySQL numeric types and whether their length specification can be ignored for comparison purposes. Starting MySQL 8.0.19, numeric types length specification has been deprecated. The bool value indicates whether the length should be ignored (true) or is significant (false, e.g., tinyint(1)).

Functions

func Case added in v0.5.1

func Case(operand ...any) *caseExpr

Case creates a new CASE expression. Pass no arguments for a searched CASE (CASE WHEN condition THEN ...), or pass one argument for a simple CASE (CASE expr WHEN value THEN ...).

func CollectEnumConstraints

func CollectEnumConstraints(tv TableView, be *Backend) map[string]*EnumConstraint

CollectEnumConstraints collects all enum columns in a table and groups them by their values. This is exported so that database submodules can use it for schema checking.

func ContextBackend

func ContextBackend(ctx context.Context, be *Backend) context.Context

ContextBackend attaches a Backend to the context. All psql operations using the returned context will use this backend.

func ContextConn

func ContextConn(ctx context.Context, conn *sql.Conn) context.Context

ContextConn attaches a *sql.Conn to the context, pinning queries to a single connection.

func ContextDB

func ContextDB(ctx context.Context, db *sql.DB) context.Context

ContextDB attaches a *sql.DB to the context, causing queries to use it directly.

func ContextTx

func ContextTx(ctx context.Context, tx *TxProxy) context.Context

ContextTx attaches a TxProxy transaction to the context. All queries using the returned context will execute within this transaction.

func Count

func Count[T any](ctx context.Context, where any, opts ...*FetchOptions) (int, error)

Count returns the number of records matching the where clause. Pass nil to count all records. Optional FetchOptions can be passed to include soft-deleted records or apply scopes.

func DefaultExportArg

func DefaultExportArg(v any) any

DefaultExportArg handles shared export logic for types common to all engines. Submodule dialects should call this as a fallback from their ExportArg implementations.

func DefineMagicType

func DefineMagicType(typ string, definition string)

func DefineMagicTypeEngine

func DefineMagicTypeEngine(e Engine, typ string, definition string)

func Delete

func Delete[T any](ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)

Delete will delete values from the table matching the where parameters. If the table has a soft delete field (DeletedAt), this performs an UPDATE setting the timestamp instead of a hard DELETE. Use ForceDelete to bypass soft delete.

func DeleteOne

func DeleteOne[T any](ctx context.Context, where any, opts ...*FetchOptions) error

DeleteOne will operate the deletion in a separate transaction and ensure only 1 row was deleted or it will rollback the deletion and return an error. This is useful when working with important data and security is more important than performance.

func ErrorNumber

func ErrorNumber(err error) uint16

ErrorNumber extracts a database error number from the error chain. It delegates to registered ErrorClassifier implementations. Returns 0 for nil errors, 0xffff for unrecognized errors.

func Escape

func Escape(val any) string

Escape takes any value and transforms it into a string that can be included in a MySQL query

func EscapeTx

func EscapeTx(ctx context.Context) (context.Context, bool)

EscapeTx returns the context that was active before the innermost transaction was attached. This is useful when you need to run a query outside the current transaction — for example, to store an audit log or error record that must persist even if the transaction is rolled back.

If no transaction is found in the context chain, EscapeTx returns (ctx, false). The returned context still carries the Backend and any other values that were set above the transaction layer.

func logFailure(ctx context.Context, msg string) {
    outerCtx, ok := psql.EscapeTx(ctx)
    if !ok {
        outerCtx = ctx // no transaction, use as-is
    }
    psql.Insert(outerCtx, &ErrorLog{Message: msg})
}

func Exec deprecated

func Exec(q *SQLQuery) error

Exec simply runs a query against the DefaultBackend

Deprecated: use .Exec() instead

func ExecContext

func ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

ExecContext executes a query (INSERT, UPDATE, DELETE, etc.) using whatever database object is attached to the context (transaction, connection, or backend).

func Factory

func Factory[T any](ctxs ...context.Context) *T

Factory returns a new object T pre-initialized with its defaults

func Fetch

func Fetch[T any](ctx context.Context, where any, opts ...*FetchOptions) ([]*T, error)

Fetch returns all records matching the where clause. Pass nil for where to fetch all records. Use FetchOptions to control sorting, limits, and preloading.

func FetchGrouped

func FetchGrouped[T any](ctx context.Context, where map[string]any, key string, opts ...*FetchOptions) (map[string][]*T, error)

FetchGrouped fetches records and returns them grouped by the string representation of the given column. Each key maps to a slice of matching records.

func FetchMapped

func FetchMapped[T any](ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string]*T, error)

FetchMapped fetches records and returns them as a map keyed by the string representation of the given column. Each key maps to a single record (last wins if duplicates exist).

func FetchOne

func FetchOne[T any](ctx context.Context, target *T, where any, opts ...*FetchOptions) error

FetchOne loads a single record into target. Unlike Get, it does not allocate a new object; instead it scans into the provided pointer. Returns os.ErrNotExist if no record matches.

func ForceDelete

func ForceDelete[T any](ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)

ForceDelete performs a hard DELETE regardless of whether the table uses soft delete.

func GenerateEnumCheckSQL

func GenerateEnumCheckSQL(constraint *EnumConstraint, tableName string) string

GenerateEnumCheckSQL generates the CHECK constraint SQL for enum columns According to PGSQL.md, it should create a single constraint that validates all columns with the same enum values This is exported for testing purposes

func Get

func Get[T any](ctx context.Context, where any, opts ...*FetchOptions) (*T, error)

Get will instanciate a new object of type T and return a pointer to it after loading from database

func GetEnumConstraintName

func GetEnumConstraintName(values string) string

GetEnumConstraintName generates a deduplicated constraint name based on the hash of the values It is exported for testing purposes but should generally not be used directly

func GetEnumTypeName

func GetEnumTypeName(values string) string

GetEnumTypeName is kept for backward compatibility but returns the constraint name

func HasChanged

func HasChanged[T any](obj *T) bool

HasChanged returns true if the object has been modified since it was last loaded from or saved to the database. It compares current field values against the stored state from the last scan.

func Init

func Init(dsn string) error

Init creates a new Backend using the given DSN and sets it as DefaultBackend. See New for supported DSN formats.

func Insert

func Insert[T any](ctx context.Context, target ...*T) error

Insert is a short way to insert objects into database

psql.Insert(ctx, obj)

Is equivalent to:

psql.Table(obj).Insert(ctx, obj)

All passed objects must be of the same type

func InsertIgnore

func InsertIgnore[T any](ctx context.Context, target ...*T) error

InsertIgnore inserts records, silently ignoring conflicts (e.g., duplicate keys). On PostgreSQL this uses ON CONFLICT DO NOTHING, on MySQL INSERT IGNORE, on SQLite INSERT OR IGNORE. Hooks are called the same as Insert.

func IsDuplicate

func IsDuplicate(err error) bool

IsDuplicate returns true if the error indicates a unique constraint violation (duplicate key). Works across MySQL (error 1062), PostgreSQL (SQLSTATE 23505), and SQLite (UNIQUE constraint failed) via registered DuplicateChecker dialects.

func IsNotExist

func IsNotExist(err error) bool

IsNotExist returns true if the error is relative to a table not existing.

See: https://mariadb.com/kb/en/mariadb-error-codes/

Example: Error 1146: Table 'test.Test_Table1' doesn't exist

func Iter

func Iter[T any](ctx context.Context, where any, opts ...*FetchOptions) (func(func(v *T) bool), error)

Iter returns a Go 1.23 iterator function that yields records one at a time. This is more memory-efficient than Fetch for large result sets since rows are scanned lazily. Use with range:

iter, err := psql.Iter[User](ctx, nil)
for user := range iter { ... }

func Preload

func Preload[T any](ctx context.Context, targets []*T, fields ...string) error

Preload loads associations for the given targets. Association fields must be declared with psql struct tags (e.g., `psql:"belongs_to:UserID"`). Target types for associations must have been registered via Table[T]().

func Query deprecated

func Query(q *SQLQuery, cb func(*sql.Rows) error) error

Query performs a query and use a callback to advance results, meaning there is no need to call sql.Rows.Close()

err = psql.Query(psql.Q("SELECT ..."), func(row *sql.Rows) error { ... })

Deprecated: use .Each() instead

func QueryContext deprecated

func QueryContext(ctx context.Context, q *SQLQuery, cb func(*sql.Rows) error) error

QueryContext performs a query and use a callback to advance results, meaning there is no need to call sql.Rows.Close()

Deprecated: use .Each() instead

func QuoteName

func QuoteName(v string) string

QuoteName quotes a SQL identifier (table name, column name, etc.) with double quotes, escaping any embedded double quotes. This does not apply naming strategy transformations.

func RegisterBackendFactory

func RegisterBackendFactory(f BackendFactory)

RegisterBackendFactory registers a BackendFactory that can create backends from DSN strings. Factories are tried in registration order by New.

func RegisterDialect

func RegisterDialect(e Engine, d Dialect)

RegisterDialect registers a Dialect for the given engine. This allows backend submodules to provide their own dialect implementations.

func Replace

func Replace[T any](ctx context.Context, target ...*T) error

Replace performs an upsert operation: inserts the record if it doesn't exist, or replaces it if a conflicting key exists. On MySQL this uses REPLACE INTO, on PostgreSQL it uses INSERT ... ON CONFLICT DO UPDATE, on SQLite INSERT OR REPLACE. Fires BeforeSaveHook and AfterSaveHook if implemented.

func Restore

func Restore[T any](ctx context.Context, where any) (sql.Result, error)

Restore clears the soft delete timestamp on records matching the where clause, effectively un-deleting them. Returns ErrNotReady if the table has no soft delete field.

func RunQueryT

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

RunQueryT executes the query and scans all result rows into []*T using the table metadata for T. Useful for JOIN queries or custom SELECTs where the result maps to a known struct type.

func RunQueryTOne

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

RunQueryTOne executes the query and scans a single result row into *T. Returns os.ErrNotExist if no rows are returned.

func SetLogger

func SetLogger(l Logger)

SetLogger sets a global logger for debugging psql This can be called easily as follows using go's slog package:

psql.SetLogger(slog.Default())

Or a better option:

psql.SetLogger(slog.Default().With("backend", "psql")) etc...

func Tx

func Tx(ctx context.Context, cb func(ctx context.Context) error) error

Tx runs cb inside a SQL transaction. If cb returns nil the transaction is committed; otherwise it is rolled back and the error is returned.

The context passed to cb carries the transaction, so all psql operations using it execute within that transaction. To run a query outside the transaction (e.g. to persist a log entry before rolling back), use the original context captured before calling Tx, or call EscapeTx on the transactional context:

outerCtx := ctx
err := psql.Tx(ctx, func(ctx context.Context) error {
    // ctx is transactional; outerCtx is not
    if err := psql.Insert(ctx, &order); err != nil {
        psql.Insert(outerCtx, &AuditLog{Event: "order_failed"})
        return err // rolls back, but the audit log persists
    }
    return nil
})

func Update

func Update[T any](ctx context.Context, target ...*T) error

Update saves changes to existing database records. Only fields that have changed since the last load are updated (if the object was previously fetched). Fires BeforeSaveHook, BeforeUpdateHook, AfterUpdateHook, and AfterSaveHook if implemented. All passed objects must be of the same type.

func V

func V(v driver.Value) driver.Value

V ensures a given value is a value and cannot be interpreted as something else

func WithPoolDefaults

func WithPoolDefaults(b *Backend)

WithPoolDefaults configures standard connection pool settings (128 max open, 32 max idle, 3 min lifetime).

Types

type AfterInsertHook

type AfterInsertHook interface {
	AfterInsert(ctx context.Context) error
}

AfterInsertHook is called after a successful INSERT operation.

type AfterSaveHook

type AfterSaveHook interface {
	AfterSave(ctx context.Context) error
}

AfterSaveHook is called after successful INSERT, UPDATE, and REPLACE operations.

type AfterScanHook

type AfterScanHook interface {
	AfterScan(ctx context.Context) error
}

AfterScanHook is called after a row is scanned from the database during fetch operations.

type AfterUpdateHook

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

AfterUpdateHook is called after a successful UPDATE operation.

type Any

type Any struct {
	Values any // must be a slice
}

Any wraps a slice value for use with PostgreSQL's = ANY($1) syntax. On PostgreSQL with parameterized queries, it renders as = ANY($N) passing the slice as a single array parameter. On MySQL/SQLite (or non-parameterized), it expands to IN(v1,v2,...).

type Backend

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

Backend represents a database connection with engine-specific behavior. Create one with New, or use a submodule constructor (e.g., mysql.New, pgsql.New, sqlite.New), then attach it to a context with Backend.Plug or ContextBackend.

var DefaultBackend *Backend

DefaultBackend is the global backend used when no backend is attached to the context. Set it with Init, or assign directly.

func GetBackend

func GetBackend(ctx context.Context) *Backend

GetBackend will attempt to find a backend in the provided context and return it, or it will return DefaultBackend if no backend was found.

func New

func New(dsn string) (*Backend, error)

New returns a Backend that connects to the database identified by dsn. The engine is auto-detected by trying registered BackendFactory implementations. Import a database submodule (e.g., _ "github.com/portablesql/psql-sqlite") to register its factory.

func NewBackend

func NewBackend(engine Engine, db *sql.DB, opts ...BackendOption) *Backend

NewBackend creates a Backend with the given engine and *sql.DB. This is called by submodule factories to construct backends.

func (*Backend) DB

func (be *Backend) DB() *sql.DB

DB returns the underlying *sql.DB connection. Panics if the backend is nil.

func (*Backend) DriverData

func (be *Backend) DriverData() any

DriverData returns the engine-specific driver data (e.g., *pgxpool.Pool for PostgreSQL).

func (*Backend) Engine

func (be *Backend) Engine() Engine

Engine returns the database engine type (EngineMySQL, EnginePostgreSQL, or EngineSQLite).

func (*Backend) Namer

func (be *Backend) Namer() Namer

Namer returns the configured naming strategy

func (*Backend) Plug

func (be *Backend) Plug(ctx context.Context) context.Context

Plug attaches this backend to the given context. All psql operations using the returned context will use this backend. Equivalent to ContextBackend.

func (*Backend) SetNamer

func (be *Backend) SetNamer(n Namer)

SetNamer allows changing the naming strategy Use DefaultNamer to keep names exactly as defined in structs (e.g., "HelloWorld" stays "HelloWorld") Use LegacyNamer (default) for backward compatibility (e.g., "HelloWorld" becomes "Hello_World") Use CamelSnakeNamer to convert all names to Camel_Snake_Case

type BackendFactory

type BackendFactory interface {
	MatchDSN(dsn string) bool
	CreateBackend(dsn string) (*Backend, error)
}

BackendFactory creates backends from DSN strings.

type BackendOption

type BackendOption func(*Backend)

BackendOption is a functional option for NewBackend.

func WithDriverData

func WithDriverData(data any) BackendOption

WithDriverData sets engine-specific driver data (e.g., *pgxpool.Pool for PostgreSQL).

func WithNamer

func WithNamer(n Namer) BackendOption

WithNamer sets the naming strategy for table/column names.

type BeforeInsertHook

type BeforeInsertHook interface {
	BeforeInsert(ctx context.Context) error
}

BeforeInsertHook is called before an INSERT operation.

type BeforeSaveHook

type BeforeSaveHook interface {
	BeforeSave(ctx context.Context) error
}

BeforeSaveHook is called before INSERT, UPDATE, and REPLACE operations.

type BeforeUpdateHook

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

BeforeUpdateHook is called before an UPDATE operation.

type CamelSnakeNamer

type CamelSnakeNamer struct{}

CamelSnakeNamer is a namer that converts names to Camel_Snake_Case

func (CamelSnakeNamer) CheckerName

func (CamelSnakeNamer) CheckerName(table, column string) string

CheckerName returns the checker name with table and column in Camel_Snake_Case format

func (CamelSnakeNamer) ColumnName

func (CamelSnakeNamer) ColumnName(table, column string) string

ColumnName returns the column name in Camel_Snake_Case format

func (CamelSnakeNamer) EnumTypeName

func (CamelSnakeNamer) EnumTypeName(table, column string) string

EnumTypeName returns the enum type name with table and column in Camel_Snake_Case format

func (CamelSnakeNamer) IndexName

func (CamelSnakeNamer) IndexName(table, column string) string

IndexName returns the index name with table and column in Camel_Snake_Case format

func (CamelSnakeNamer) JoinTableName

func (CamelSnakeNamer) JoinTableName(joinTable string) string

JoinTableName returns the join table name in Camel_Snake_Case format

func (CamelSnakeNamer) SchemaName

func (CamelSnakeNamer) SchemaName(table string) string

SchemaName returns the schema name in Camel_Snake_Case format

func (CamelSnakeNamer) TableName

func (CamelSnakeNamer) TableName(table string) string

TableName returns the table name in Camel_Snake_Case format

func (CamelSnakeNamer) UniqueName

func (CamelSnakeNamer) UniqueName(table, column string) string

UniqueName returns the unique constraint name with table and column in Camel_Snake_Case format

type Comparison

type Comparison struct {
	A, B any
	Op   string // one of "=", "<", ">", etc...
}

Comparison represents a binary comparison expression (e.g., A = B, A > B). Use the helper constructors Equal, Gt, Gte, Lt, Lte instead of creating Comparison values directly.

func (*Comparison) EscapeValue

func (c *Comparison) EscapeValue() string

type Decrement

type Decrement struct {
	Value any
}

Decrement represents a SET expression that decrements a field by a value. Used in UPDATE queries: SET "field"="field"-value

func Decr

func Decr(v any) *Decrement

Decr creates a Decrement expression for use in UPDATE SET clauses:

psql.B().Update("counters").Set(map[string]any{"stock": psql.Decr(1)})
// UPDATE "counters" SET "stock"="stock"-1

type DefaultNamer

type DefaultNamer struct{}

DefaultNamer is a namer that returns names as they are provided

func (DefaultNamer) CheckerName

func (DefaultNamer) CheckerName(table, column string) string

CheckerName returns the checker name as is

func (DefaultNamer) ColumnName

func (DefaultNamer) ColumnName(table, column string) string

ColumnName returns the column name as is

func (DefaultNamer) EnumTypeName

func (DefaultNamer) EnumTypeName(table, column string) string

EnumTypeName returns the enum type name using original table and column names

func (DefaultNamer) IndexName

func (DefaultNamer) IndexName(table, column string) string

IndexName returns the index name as is

func (DefaultNamer) JoinTableName

func (DefaultNamer) JoinTableName(joinTable string) string

JoinTableName returns the join table name as is

func (DefaultNamer) SchemaName

func (DefaultNamer) SchemaName(table string) string

SchemaName returns the schema name as is

func (DefaultNamer) TableName

func (DefaultNamer) TableName(table string) string

TableName returns the table name as is

func (DefaultNamer) UniqueName

func (DefaultNamer) UniqueName(table, column string) string

UniqueName returns the unique constraint name as is

type Dialect

type Dialect interface {
	// Placeholder returns the SQL placeholder for the nth parameter (1-indexed).
	// For example, MySQL/SQLite return "?" (ignoring n), PostgreSQL returns "$1", "$2", etc.
	Placeholder(n int) string

	// ExportArg transforms a Go value for use as a query parameter.
	// This handles engine-specific formatting (e.g., time.Time representation).
	ExportArg(v any) any

	// LimitOffset renders a two-argument LIMIT clause. The arguments are passed
	// through as stored by [QueryBuilder.Limit](a, b). MySQL renders "LIMIT a, b",
	// while PostgreSQL/SQLite render "LIMIT a OFFSET b".
	LimitOffset(a, b int) string
}

Dialect defines engine-specific SQL behaviors. Each database engine registers its own Dialect implementation via RegisterDialect. This interface is the foundation for moving backend-specific logic into separate submodules.

type DuplicateChecker

type DuplicateChecker interface {
	IsDuplicate(err error) bool
}

DuplicateChecker is implemented by dialects that can detect duplicate key errors.

type Engine

type Engine int

Engine identifies the SQL database engine in use.

const (
	EngineUnknown    Engine = iota // Unknown or unset engine
	EngineMySQL                    // MySQL / MariaDB
	EnginePostgreSQL               // PostgreSQL / CockroachDB
	EngineSQLite                   // SQLite (via modernc.org/sqlite)
)

func (Engine) Placeholders

func (e Engine) Placeholders(n, offset int) string

Placeholders returns a comma-separated list of n placeholders for the dialect, starting at the given offset (1-indexed). For example, with n=3 and offset=1: MySQL/SQLite → "?,?,?", PostgreSQL → "$1,$2,$3".

func (Engine) String

func (e Engine) String() string

type EnumConstraint

type EnumConstraint struct {
	Name    string              // Constraint name (chk_enum_XXXXXXXX)
	Values  []string            // Allowed enum values
	Columns map[string][]string // Map of table -> columns using this constraint
}

EnumConstraint represents a CHECK constraint for enum columns

type Error

type Error struct {
	Query string
	Err   error
}

Error wraps a SQL error with the query that caused it. It implements the error and Unwrap interfaces for use with errors.Is and errors.As.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type ErrorClassifier

type ErrorClassifier interface {
	ErrorNumber(err error) uint16
	IsNotExist(err error) bool
}

ErrorClassifier handles engine-specific error interpretation.

type EscapeTableable

type EscapeTableable interface {
	EscapeTable() string
}

EscapeTableable is a type of value that can be used as a table

func SubTable added in v0.5.1

func SubTable(sub *QueryBuilder, alias string) EscapeTableable

SubTable creates a derived table (subquery) that can be used in FROM or JOIN positions. The alias is required and used to reference the subquery's columns:

psql.B().Select("u.id", "vc.vote_count").From("users AS u").LeftJoin(
    psql.SubTable(
        psql.B().Select("user_id", psql.Raw("COUNT(*) AS vote_count")).
            From("votes").GroupByFields("user_id"),
        "vc",
    ),
    psql.Equal(psql.F("u.id"), psql.F("vc.user_id")),
)
// → ... LEFT JOIN (SELECT "user_id",COUNT(*) AS vote_count FROM "votes" GROUP BY "user_id") AS "vc" ON ...

type EscapeValueable

type EscapeValueable interface {
	EscapeValue() string
}

EscapeValueable is implemented by types that can render themselves as SQL expressions. Used by the query builder for WHERE conditions, field references, and values.

func Between

func Between(a, start, end any) EscapeValueable

Between is a BETWEEN SQL operation. The BETWEEN operator is inclusive: begin and end values are included.

func Coalesce added in v0.5.1

func Coalesce(args ...any) EscapeValueable

Coalesce creates a SQL COALESCE(a, b, ...) expression that returns the first non-NULL argument. Arguments can be field references, values, or subqueries:

psql.Coalesce(psql.F("nickname"), psql.F("name"))
// → COALESCE("nickname","name")

psql.Coalesce(
    psql.B().Select(psql.Raw("COUNT(*)")).From("messages").Where(...),
    0,
)
// → COALESCE((SELECT COUNT(*) FROM "messages" WHERE ...), 0)

func DateAdd added in v0.5.6

func DateAdd(expr any, d time.Duration) EscapeValueable

DateAdd adds a Go time.Duration to a SQL timestamp expression, generating engine-appropriate interval arithmetic:

psql.DateAdd(psql.F("created_at"), 24*time.Hour)
// MySQL:      "created_at" + INTERVAL 1 DAY
// PostgreSQL: "created_at" + INTERVAL '1 day'
// SQLite:     datetime("created_at",'+1 day')

psql.DateAdd(psql.Now(), -30*time.Minute)
// MySQL:      NOW() - INTERVAL 30 MINUTE
// PostgreSQL: NOW() - INTERVAL '30 minute'
// SQLite:     datetime(CURRENT_TIMESTAMP,'-30 minutes')

func DateSub added in v0.5.6

func DateSub(expr any, d time.Duration) EscapeValueable

DateSub subtracts a Go time.Duration from a SQL timestamp expression. It is equivalent to DateAdd(expr, -d).

func Equal

func Equal(a, b any) EscapeValueable

Equal creates an equality comparison (A = B). Use with F for field references:

psql.Equal(psql.F("status"), "active")

func Exists added in v0.5.1

func Exists(sub *QueryBuilder) EscapeValueable

Exists creates an EXISTS (subquery) condition for use in WHERE clauses:

psql.B().Select().From("users").Where(
    psql.Exists(psql.B().Select(psql.Raw("1")).From("orders").Where(
        psql.Equal(psql.F("orders.user_id"), psql.F("users.id")),
    )),
)
// → ... WHERE EXISTS (SELECT 1 FROM "orders" WHERE "orders"."user_id"="users"."id")

func F

func F(field ...string) EscapeValueable

F allows passing a field name to the query builder. It can be used in multiple ways:

psql.F("field") psql.F("table.field") psql.F("", "field.with.dots") psql.F("table", "field") psql.F("table.with.dots", "field.with.dots") and more...

func Greatest added in v0.5.1

func Greatest(args ...any) EscapeValueable

Greatest creates a SQL GREATEST(a, b, ...) expression that returns the largest value among the arguments. On SQLite (which lacks GREATEST), it renders as a nested MAX() expression.

psql.Greatest(psql.F("user_count"), 0)
// MySQL/PG: → GREATEST("user_count",0)
// SQLite:   → MAX("user_count",0)

func Gt

func Gt(a, b any) EscapeValueable

Gt creates a greater-than comparison (A > B).

func Gte

func Gte(a, b any) EscapeValueable

Gte creates a greater-than-or-equal comparison (A >= B).

func Least added in v0.5.1

func Least(args ...any) EscapeValueable

Least creates a SQL LEAST(a, b, ...) expression that returns the smallest value among the arguments. On SQLite (which lacks LEAST), it renders as a nested MIN() expression.

psql.Least(psql.F("stock"), 100)
// MySQL/PG: → LEAST("stock",100)
// SQLite:   → MIN("stock",100)

func Lt

func Lt(a, b any) EscapeValueable

Lt creates a less-than comparison (A < B).

func Lte

func Lte(a, b any) EscapeValueable

Lte creates a less-than-or-equal comparison (A <= B).

func NotExists added in v0.5.1

func NotExists(sub *QueryBuilder) EscapeValueable

NotExists creates a NOT EXISTS (subquery) condition for use in WHERE clauses:

psql.B().Select().From("channels").Where(
    psql.NotExists(psql.B().Select(psql.Raw("1")).From("subscriptions").Where(
        psql.Equal(psql.F("subscriptions.channel_id"), psql.F("channels.id")),
    )),
)

func Now added in v0.5.6

func Now() EscapeValueable

Now returns a portable SQL expression for the current timestamp.

psql.Now()
// MySQL/PostgreSQL: NOW()
// SQLite:           CURRENT_TIMESTAMP

func Raw

func Raw(s string) EscapeValueable

Raw creates a raw SQL expression that is injected verbatim into queries without escaping. Use carefully to avoid SQL injection:

psql.B().Select(psql.Raw("COUNT(*)")).From("users")

type FetchOptions

type FetchOptions struct {
	Lock        bool
	SkipLocked  bool            // append SKIP LOCKED after FOR UPDATE
	NoWait      bool            // append NOWAIT after FOR UPDATE
	LimitCount  int             // number of results to return if >0
	LimitStart  int             // seek first record if >0
	Sort        []SortValueable // fields to sort by
	Preload     []string        // association fields to preload after fetching
	Scopes      []Scope         // reusable query modifiers
	WithDeleted bool            // include soft-deleted records
	HardDelete  bool            // force hard delete even with soft delete
}

FetchOptions controls the behavior of Fetch, Get, FetchOne, and related operations. Use helper constructors Sort, Limit, LimitFrom, WithPreload, WithScope, IncludeDeleted, and FetchLock to create options, or combine multiple options by passing them as variadic arguments.

func IncludeDeleted

func IncludeDeleted() *FetchOptions

IncludeDeleted returns a FetchOptions that includes soft-deleted records in query results.

func Limit

func Limit(cnt int) *FetchOptions

Limit returns a FetchOptions that limits the number of results returned.

func LimitFrom

func LimitFrom(start, cnt int) *FetchOptions

LimitFrom returns a FetchOptions with both an offset (start) and a limit (cnt). Equivalent to LIMIT cnt OFFSET start in PostgreSQL/SQLite, or LIMIT start, cnt in MySQL.

func Sort

func Sort(fields ...SortValueable) *FetchOptions

Sort returns a FetchOptions that orders results by the given fields. Use S to create sort fields: psql.Sort(psql.S("Name", "ASC"))

func WithPreload

func WithPreload(fields ...string) *FetchOptions

WithPreload returns a FetchOptions that automatically preloads the given associations after fetching.

func WithScope

func WithScope(scopes ...Scope) *FetchOptions

WithScope returns a FetchOptions that applies the given scopes to the query.

type FindInSet

type FindInSet struct {
	Field any
	Value string
}

FindInSet represents a MySQL FIND_IN_SET() function call, which searches for a string value within a comma-separated list stored in a column.

func (*FindInSet) EscapeValue

func (f *FindInSet) EscapeValue() string

func (*FindInSet) String

func (f *FindInSet) String() string

type Future

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

Future represents a lazily-loaded database record. Created by Lazy, it defers the actual database query until Future.Resolve is called. When resolved, it automatically batches all pending futures for the same table and column into a single query, significantly reducing database round trips.

Concurrent Resolve calls share the same result. Future also implements json.Marshaler.

func Lazy

func Lazy[T any](col, val string) *Future[T]

Lazy returns an instance of Future that will be resolved in the future. Multiple calls to Lazy in different goroutines will return the same value until it is resolved.

When any Future is resolved, all pending futures for the same table and column are batched into a single WHERE col IN (...) query, reducing database round trips.

func (*Future[T]) MarshalContextJSON

func (f *Future[T]) MarshalContextJSON(ctx context.Context) ([]byte, error)

func (*Future[T]) MarshalJSON

func (f *Future[T]) MarshalJSON() ([]byte, error)

func (*Future[T]) Resolve

func (f *Future[T]) Resolve(ctx context.Context) (*T, error)

type Hex

type Hex []byte

Hex is a binary value stored as hexadecimal in database

func (*Hex) Scan

func (h *Hex) Scan(src interface{}) error

func (*Hex) Value

func (h *Hex) Value() (driver.Value, error)

type Increment

type Increment struct {
	Value any
}

Increment represents a SET expression that increments a field by a value. Used in UPDATE queries: SET "field"="field"+value

func Incr

func Incr(v any) *Increment

Incr creates an Increment expression for use in UPDATE SET clauses:

psql.B().Update("counters").Set(map[string]any{"views": psql.Incr(1)})
// UPDATE "counters" SET "views"="views"+1

type Join

type Join struct {
	Table     string
	Condition string // condition for join
	Type      string // LEFT|INNER|RIGHT
	Alias     string // if any
}

Join represents a SQL JOIN clause with a table, condition, type, and optional alias.

func (*Join) JoinTable

func (j *Join) JoinTable(ctx context.Context) string

JoinTable returns the formatted table name using the namer if available

type Key

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

Name allows specifying the table name when associating a table with a struct

For example: type X struct { KeyName psql.Key `sql:",type=UNIQUE,fields='A,B'"` ... }

type KeyRenderer

type KeyRenderer interface {
	KeyDef(k *StructKey, tableName string) string
	InlineKeyDef(k *StructKey, tableName string) string // for CREATE TABLE
	CreateIndex(k *StructKey, tableName string) string  // for standalone CREATE INDEX
}

KeyRenderer handles engine-specific key/index definitions.

type LegacyNamer

type LegacyNamer struct{}

LegacyNamer reproduces the behavior of the original implementation: - Table names use CamelSnakeCase - Column names are kept as is (no transformation) - Other names use standard prefixes with the original names

func (LegacyNamer) CheckerName

func (LegacyNamer) CheckerName(table, column string) string

CheckerName returns the checker name with table in Camel_Snake_Case format and original column name

func (LegacyNamer) ColumnName

func (LegacyNamer) ColumnName(table, column string) string

ColumnName returns the column name as is (no transformation)

func (LegacyNamer) EnumTypeName

func (LegacyNamer) EnumTypeName(table, column string) string

EnumTypeName returns the enum type name with table in Camel_Snake_Case format and original column name

func (LegacyNamer) IndexName

func (LegacyNamer) IndexName(table, column string) string

IndexName returns the index name with table in Camel_Snake_Case format and original column name

func (LegacyNamer) JoinTableName

func (LegacyNamer) JoinTableName(joinTable string) string

JoinTableName returns the join table name in Camel_Snake_Case format

func (LegacyNamer) SchemaName

func (LegacyNamer) SchemaName(table string) string

SchemaName returns the schema name in Camel_Snake_Case format

func (LegacyNamer) TableName

func (LegacyNamer) TableName(table string) string

TableName returns the table name in Camel_Snake_Case format (original behavior)

func (LegacyNamer) UniqueName

func (LegacyNamer) UniqueName(table, column string) string

UniqueName returns the unique constraint name with table in Camel_Snake_Case format and original column name

type Like

type Like struct {
	Field           any
	Like            string
	CaseInsensitive bool
}

Like represents a SQL LIKE condition. Use in WHERE clauses:

psql.B().Select().From("users").Where(&psql.Like{Field: psql.F("name"), Like: "John%"})

Set CaseInsensitive to true for case-insensitive matching. This renders as ILIKE on PostgreSQL, LIKE on MySQL (case-insensitive by default collation), and LIKE with COLLATE NOCASE on SQLite.

func CILike

func CILike(field any, pattern string) *Like

CILike creates a case-insensitive Like condition:

psql.CILike(psql.F("name"), "john%")

func (*Like) EscapeValue

func (l *Like) EscapeValue() string

func (*Like) String

func (l *Like) String() string

type Logger

type Logger interface {
	DebugContext(ctx context.Context, msg string, args ...any)
}

Logger is compatible with go's slog.Logger

type Name

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

Name allows specifying the table name when associating a table with a struct

For example: type X struct { TableName psql.Name `sql:"X"` ... }

type Namer

type Namer interface {
	TableName(table string) string
	SchemaName(table string) string
	ColumnName(table, column string) string
	JoinTableName(joinTable string) string
	CheckerName(table, column string) string
	IndexName(table, column string) string
	UniqueName(table, column string) string
	EnumTypeName(table, column string) string // For PostgreSQL ENUM types
}

Namer is an object that provides names to functions. This is based on gorm

type Not

type Not struct {
	V any
}

Not negates a condition. Wraps any value to produce IS NOT NULL, !=, NOT LIKE, etc.

&psql.Not{V: nil}                          // IS NOT NULL
&psql.Not{V: psql.Equal(psql.F("a"), "b")} // a != b

func (*Not) EscapeValue

func (n *Not) EscapeValue() string

type QueryBuilder

type QueryBuilder struct {
	Query       string
	Fields      []any
	Tables      []EscapeTableable
	FieldsSet   []any
	WhereData   WhereAND
	GroupBy     []any
	HavingData  WhereAND
	OrderByData []SortValueable
	LimitData   []int

	// conflict/upsert
	ConflictColumns []string // ON CONFLICT (columns)
	ConflictUpdate  []any    // DO UPDATE SET fields (map[string]any entries)
	ConflictNothing bool     // DO NOTHING / INSERT IGNORE

	// flags
	Distinct      bool
	CalcFoundRows bool
	UpdateIgnore  bool
	InsertIgnore  bool
	ForUpdate     bool
	SkipLocked    bool
	NoWait        bool
	// contains filtered or unexported fields
}

QueryBuilder constructs SQL queries using a fluent API. Create one with B, then chain methods to build SELECT, INSERT, UPDATE, DELETE, or REPLACE queries. Execute with QueryBuilder.RunQuery, QueryBuilder.ExecQuery, or QueryBuilder.Render.

rows, err := psql.B().Select("id", "name").From("users").
    Where(map[string]any{"active": true}).
    OrderBy(psql.S("name", "ASC")).
    Limit(10).
    RunQuery(ctx)

func B

func B() *QueryBuilder

B creates a new empty QueryBuilder. Chain methods to build a query:

psql.B().Select("*").From("users").Where(...)
psql.B().Update("users").Set(...).Where(...)
psql.B().Delete().From("users").Where(...)

func (*QueryBuilder) AlsoSelect

func (q *QueryBuilder) AlsoSelect(fields ...any) *QueryBuilder

AlsoSelect adds additional fields to an existing SELECT query.

func (*QueryBuilder) Apply

func (q *QueryBuilder) Apply(scopes ...Scope) *QueryBuilder

Apply runs the given scopes on this query builder, returning the modified builder. Scopes can add WHERE, ORDER BY, LIMIT, or any other clause.

func (*QueryBuilder) Delete

func (q *QueryBuilder) Delete() *QueryBuilder

Delete sets the query type to DELETE. Use QueryBuilder.From to specify the table.

func (*QueryBuilder) DoNothing

func (q *QueryBuilder) DoNothing() *QueryBuilder

DoNothing sets the ON CONFLICT action to DO NOTHING (PostgreSQL/SQLite) or INSERT IGNORE (MySQL).

func (*QueryBuilder) DoUpdate

func (q *QueryBuilder) DoUpdate(fields ...any) *QueryBuilder

DoUpdate specifies the fields to update on conflict. Accepts map[string]any entries, similar to QueryBuilder.Set.

func (*QueryBuilder) EscapeValue

func (q *QueryBuilder) EscapeValue() string

EscapeValue renders the QueryBuilder as a parenthesized subquery (non-parameterized).

func (*QueryBuilder) ExecQuery

func (q *QueryBuilder) ExecQuery(ctx context.Context) (sql.Result, error)

ExecQuery executes the query and returns sql.Result. Used for INSERT, UPDATE, DELETE, and other non-row-returning queries.

func (*QueryBuilder) From

func (q *QueryBuilder) From(table any) *QueryBuilder

From specifies the source table. Accepts a string (table name) or EscapeTableable.

func (*QueryBuilder) GroupByFields

func (q *QueryBuilder) GroupByFields(fields ...any) *QueryBuilder

GroupByFields adds GROUP BY clause to the query.

func (*QueryBuilder) Having

func (q *QueryBuilder) Having(having ...any) *QueryBuilder

Having adds a HAVING clause to the query (used with GROUP BY).

func (*QueryBuilder) InnerJoin

func (q *QueryBuilder) InnerJoin(table any, condition ...any) *QueryBuilder

InnerJoin adds an INNER JOIN clause. The table can be a string or EscapeTableable.

func (*QueryBuilder) Insert

func (q *QueryBuilder) Insert(fields ...any) *QueryBuilder

Insert sets the query type to INSERT and specifies the fields/values to insert.

func (*QueryBuilder) InsertSelect added in v0.5.1

func (q *QueryBuilder) InsertSelect(destTable any) *QueryBuilder

InsertSelect creates an INSERT ... SELECT query. Specify the destination table, then chain QueryBuilder.Select, QueryBuilder.From, and QueryBuilder.Where to define the source query:

psql.B().InsertSelect("archive").Select("id", "name").From("users").
    Where(map[string]any{"active": false})
// → INSERT INTO "archive" SELECT "id","name" FROM "users" WHERE ("active"=FALSE)

func (*QueryBuilder) Into

func (q *QueryBuilder) Into(table EscapeTableable) *QueryBuilder

Into specifies the target table for INSERT queries.

func (*QueryBuilder) Join

func (q *QueryBuilder) Join(joinType string, table any, condition ...any) *QueryBuilder

Join adds a JOIN clause to the query. The table can be a string (table name) or an EscapeTableable such as SubTable for subquery joins:

q.Join("LEFT", "orders", psql.Equal(psql.F("orders.user_id"), psql.F("users.id")))
q.Join("LEFT", psql.SubTable(subQuery, "sq"), psql.Equal(psql.F("sq.id"), psql.F("t.id")))

func (*QueryBuilder) LeftJoin

func (q *QueryBuilder) LeftJoin(table any, condition ...any) *QueryBuilder

LeftJoin adds a LEFT JOIN clause. The table can be a string or EscapeTableable.

func (*QueryBuilder) Limit

func (q *QueryBuilder) Limit(v ...int) *QueryBuilder

Limit sets the LIMIT clause. With one argument, limits the row count. With two arguments, Limit(count, offset) renders as LIMIT count OFFSET offset (PostgreSQL/SQLite) or LIMIT count, offset (MySQL).

func (*QueryBuilder) OnConflict

func (q *QueryBuilder) OnConflict(columns ...string) *QueryBuilder

OnConflict specifies the conflict columns for INSERT ... ON CONFLICT.

func (*QueryBuilder) OrderBy

func (q *QueryBuilder) OrderBy(field ...SortValueable) *QueryBuilder

OrderBy adds ORDER BY clauses. Use S to create sort fields: OrderBy(psql.S("name", "ASC"), psql.S("created_at", "DESC"))

func (*QueryBuilder) Prepare

func (q *QueryBuilder) Prepare(ctx context.Context) (*sql.Stmt, error)

Prepare creates a prepared statement from the query. The caller must close the returned statement.

func (*QueryBuilder) Render

func (q *QueryBuilder) Render(ctx context.Context) (string, error)

Render generates the SQL query string for the current engine. Values are embedded directly (not parameterized). For parameterized queries, use QueryBuilder.RenderArgs.

func (*QueryBuilder) RenderArgs

func (q *QueryBuilder) RenderArgs(ctx context.Context) (string, []any, error)

RenderArgs generates the SQL query string with parameterized placeholders and returns the arguments separately. Uses $1/$2/... for PostgreSQL and ? for MySQL/SQLite.

func (*QueryBuilder) Replace

func (q *QueryBuilder) Replace(table EscapeTableable) *QueryBuilder

Replace sets the query type to REPLACE (MySQL) or equivalent upsert.

func (*QueryBuilder) RightJoin

func (q *QueryBuilder) RightJoin(table any, condition ...any) *QueryBuilder

RightJoin adds a RIGHT JOIN clause. The table can be a string or EscapeTableable.

func (*QueryBuilder) RunQuery

func (q *QueryBuilder) RunQuery(ctx context.Context) (*sql.Rows, error)

RunQuery executes the query and returns *sql.Rows for reading results. The caller must close the returned rows. Typically used for SELECT queries.

func (*QueryBuilder) Select

func (q *QueryBuilder) Select(fields ...any) *QueryBuilder

Select sets the query type to SELECT and specifies the fields to retrieve. String arguments are treated as field names. Pass no arguments to select all (*). When called after QueryBuilder.InsertSelect, the query type is preserved.

func (*QueryBuilder) Set

func (q *QueryBuilder) Set(fields ...any) *QueryBuilder

Set specifies the fields to update in UPDATE or INSERT queries. Typically pass a map[string]any: Set(map[string]any{"name": "Alice"}).

func (*QueryBuilder) SetDistinct

func (q *QueryBuilder) SetDistinct() *QueryBuilder

SetDistinct enables the DISTINCT keyword in the query.

func (*QueryBuilder) SetForUpdate

func (q *QueryBuilder) SetForUpdate() *QueryBuilder

SetForUpdate adds FOR UPDATE locking to the query.

func (*QueryBuilder) SetNoWait

func (q *QueryBuilder) SetNoWait() *QueryBuilder

SetNoWait adds NOWAIT after FOR UPDATE. The query fails immediately if any selected row is locked by another transaction.

func (*QueryBuilder) SetSkipLocked

func (q *QueryBuilder) SetSkipLocked() *QueryBuilder

SetSkipLocked adds SKIP LOCKED after FOR UPDATE. Rows locked by other transactions are skipped instead of blocking.

func (*QueryBuilder) Table

func (q *QueryBuilder) Table(table any) *QueryBuilder

Table adds a table to the query. Accepts a string or EscapeTableable.

func (*QueryBuilder) Update

func (q *QueryBuilder) Update(table any) *QueryBuilder

Update sets the query type to UPDATE and specifies the target table. Use QueryBuilder.Set to specify the fields to update.

func (*QueryBuilder) Where

func (q *QueryBuilder) Where(where ...any) *QueryBuilder

Where adds conditions to the WHERE clause. Accepts map[string]any for equality conditions, EscapeValueable for comparisons (e.g., Equal, Gt, Like), or multiple arguments which are joined with AND.

type ReturningRenderer

type ReturningRenderer interface {
	SupportsReturning() bool
}

ReturningRenderer is implemented by dialects that support RETURNING clauses on INSERT/REPLACE/UPDATE statements (e.g., PostgreSQL).

type SQLQuery

type SQLQuery struct {
	Query string
	Args  []any
}

SQLQuery represents a raw SQL query with arguments. Create one with Q. Use SQLQuery.Each to iterate rows or SQLQuery.Exec to execute without results.

func Q

func Q(q string, args ...any) *SQLQuery

Q creates a raw SQLQuery for executing arbitrary SQL:

func (*SQLQuery) Each

func (q *SQLQuery) Each(ctx context.Context, cb func(*sql.Rows) error) error

Each will execute the query and call cb for each row, so you do not need to call .Next() or .Close() on the object.

Example use: err := psql.Q("SELECT ...").Each(ctx, func(row *sql.Rows) error { ... })

func (*SQLQuery) Exec

func (q *SQLQuery) Exec(ctx context.Context) error

Exec simply executes the query and returns any error that could have happened

type SQLQueryT

type SQLQueryT[T any] struct {
	Query string
	Args  []any
}

SQLQueryT is a typed raw SQL query that automatically scans results into type T. Create one with QT. Use SQLQueryT.Each, SQLQueryT.Single, or SQLQueryT.All to execute and get typed results.

func QT

func QT[T any](q string, args ...any) *SQLQueryT[T]

QT creates a typed SQLQueryT that scans results into type T:

func (*SQLQueryT[T]) All

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

All will execute the query and return all the results

func (*SQLQueryT[T]) Each

func (q *SQLQueryT[T]) Each(ctx context.Context, cb func(*T) error) error

Each will execute the query and call cb for each row

func (*SQLQueryT[T]) Single

func (q *SQLQueryT[T]) Single(ctx context.Context) (*T, error)

Single will execute the query and fetch a single result

type SchemaChecker

type SchemaChecker interface {
	CheckStructure(ctx context.Context, be *Backend, tv TableView) error
}

SchemaChecker handles table structure verification and creation. Dialects that implement this interface will be called to check and create tables.

type Scope

type Scope func(q *QueryBuilder) *QueryBuilder

Scope is a reusable query modifier function. Scopes can add WHERE conditions, ORDER BY, LIMIT, JOINs, or any other SQL clause to a QueryBuilder.

Define scopes as package-level variables or functions:

var Active Scope = func(q *QueryBuilder) *QueryBuilder {
    return q.Where(map[string]any{"Status": "active"})
}

func RecentN(n int) Scope {
    return func(q *QueryBuilder) *QueryBuilder {
        return q.OrderBy(S("CreatedAt", "DESC")).Limit(n)
    }
}

Use with Fetch, Get, Count, or the query builder:

users, err := psql.Fetch[User](ctx, nil, psql.WithScope(Active, RecentN(10)))
rows, err := psql.B().Select().From("users").Apply(Active).RunQuery(ctx)

type Set

type Set []string

Set represents a SQL SET column type, stored as a comma-separated string in the database. It provides Set/Unset/Has methods for manipulating individual values and implements sql.Scanner and driver.Valuer for automatic serialization.

func (Set) Has

func (s Set) Has(k string) bool

func (*Set) Scan

func (s *Set) Scan(src any) error

func (*Set) Set

func (s *Set) Set(k string)

func (*Set) Unset

func (s *Set) Unset(k string)

func (Set) Value

func (s Set) Value() (driver.Value, error)

type SetRaw

type SetRaw struct {
	SQL string
}

SetRaw represents a SET expression using raw SQL. Used in UPDATE queries: SET "field"=<raw SQL>

type SortValueable

type SortValueable interface {
	// contains filtered or unexported methods
}

SortValueable is a kind of value that can be used for sorting

func S

func S(field ...string) SortValueable

S creates a sort expression for ORDER BY clauses. The last argument may be "ASC" or "DESC" to set the sort direction. If omitted, the database default (typically ASC) is used:

psql.S("name", "ASC")         // "name" ASC
psql.S("table", "field", "DESC") // "table"."field" DESC

func VecOrderBy

func VecOrderBy(field any, vec Vector, op VectorDistanceOp) SortValueable

VecOrderBy is a convenience that returns a SortValueable for ordering by vector distance. The direction defaults to ASC (nearest first).

Usage:

psql.B().Select().From("items").OrderBy(psql.VecOrderBy(psql.F("Embedding"), queryVec, psql.VectorL2))

type StructField

type StructField struct {
	Index    int
	Name     string
	Column   string // column name, can be != name
	Nullable bool   // if a ptr or a kind of nullable value
	Attrs    map[string]string

	Rattrs map[Engine]map[string]string // resolved attrs
	// contains filtered or unexported fields
}

StructField holds metadata for a single table field/column, including its Go struct index, SQL column name, type attributes, and scan function.

func (*StructField) DefString

func (f *StructField) DefString(be *Backend) string

DefString returns the full column definition SQL for this field, dispatching to the engine's TypeMapper if available.

func (*StructField) DefStringAlter

func (f *StructField) DefStringAlter(be *Backend) string

DefStringAlter returns a column definition suitable for ALTER TABLE ADD COLUMN. If the dialect implements TypeMapper, it delegates to FieldDefAlter; otherwise falls back to DefString.

func (*StructField) GetAttrs

func (f *StructField) GetAttrs(be *Backend) map[string]string

GetAttrs returns the fields' attrs for a given Engine, which can be cached for performance

func (*StructField) Matches

func (f *StructField) Matches(be *Backend, typ, null string, col, dflt *string) (bool, error)

Matches checks if this field matches the given database column properties.

func (*StructField) SqlType

func (f *StructField) SqlType(be *Backend) string

SqlType returns the SQL type string for this field, dispatching to the engine's TypeMapper if available.

type StructKey

type StructKey struct {
	Index  int
	Name   string
	Key    string // key name, can be != name
	Typ    int
	Attrs  map[string]string
	Fields []string
}

StructKey holds metadata for a table key/index, including its type, column list, and attributes.

func (*StructKey) CreateIndexSQL

func (k *StructKey) CreateIndexSQL(be *Backend, tableName string) string

CreateIndexSQL returns a CREATE INDEX statement, or empty string if the key should be created inline.

func (*StructKey) DefString

func (k *StructKey) DefString(be *Backend) string

DefString returns the key definition SQL, dispatching to the engine's KeyRenderer if available.

func (*StructKey) InlineDefString

func (k *StructKey) InlineDefString(be *Backend, tableName string) string

InlineDefString returns the inline constraint definition for CREATE TABLE.

func (*StructKey) IsUnique

func (k *StructKey) IsUnique() bool

IsUnique returns true if this key is a primary key or unique index.

func (*StructKey) Keyname

func (k *StructKey) Keyname() string

Keyname returns "PRIMARY" for primary keys, or the key name otherwise.

func (*StructKey) SqlKeyName

func (k *StructKey) SqlKeyName() string

SqlKeyName returns "PRIMARY KEY" for primary keys, or "INDEX keyname" otherwise.

type SubIn

type SubIn struct {
	Sub *QueryBuilder
}

SubIn wraps a QueryBuilder subquery for use with IN (subquery) in WHERE conditions:

psql.B().Select().From("users").Where(map[string]any{
    "id": &psql.SubIn{psql.B().Select("user_id").From("orders")},
})
// → ... WHERE "id" IN (SELECT "user_id" FROM "orders")

type TableMeta

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

TableMeta holds the metadata for a registered table type T, including its fields, keys, associations, and SQL column mappings. Obtain one via Table.

func Table

func Table[T any]() *TableMeta[T]

Table returns the table object for T against DefaultBackend unless the provided ctx value has a backend.

func (*TableMeta[T]) AllFields

func (t *TableMeta[T]) AllFields() []*StructField

AllFields returns all fields (implements TableView).

func (*TableMeta[T]) AllKeys

func (t *TableMeta[T]) AllKeys() []*StructKey

AllKeys returns all keys (implements TableView).

func (*TableMeta[T]) Count

func (t *TableMeta[T]) Count(ctx context.Context, where any, opts ...*FetchOptions) (int, error)

func (*TableMeta[T]) Delete

func (t *TableMeta[T]) Delete(ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)

func (*TableMeta[T]) DeleteOne

func (t *TableMeta[T]) DeleteOne(ctx context.Context, where any, opts ...*FetchOptions) error

func (*TableMeta[T]) Factory

func (t *TableMeta[T]) Factory(ctx context.Context) *T

Factory returns a new object T pre-initialized with its defaults

func (*TableMeta[T]) Fetch

func (t *TableMeta[T]) Fetch(ctx context.Context, where any, opts ...*FetchOptions) ([]*T, error)

func (*TableMeta[T]) FetchGrouped

func (t *TableMeta[T]) FetchGrouped(ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string][]*T, error)

func (*TableMeta[T]) FetchMapped

func (t *TableMeta[T]) FetchMapped(ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string]*T, error)

func (*TableMeta[T]) FetchOne

func (t *TableMeta[T]) FetchOne(ctx context.Context, target *T, where any, opts ...*FetchOptions) error

func (*TableMeta[T]) FieldByColumn

func (t *TableMeta[T]) FieldByColumn(col string) *StructField

FieldByColumn returns a field by its column name (implements TableView).

func (*TableMeta[T]) FieldStr

func (t *TableMeta[T]) FieldStr() string

FieldStr returns the comma-separated quoted field list (implements TableView).

func (*TableMeta[T]) FormattedName

func (t *TableMeta[T]) FormattedName(be *Backend) string

FormattedName returns the table name, applying the namer transformation if needed

func (*TableMeta[T]) Get

func (t *TableMeta[T]) Get(ctx context.Context, where any, opts ...*FetchOptions) (*T, error)

func (*TableMeta[T]) HasChanged

func (t *TableMeta[T]) HasChanged(obj *T) bool

func (*TableMeta[T]) HasSoftDelete

func (t *TableMeta[T]) HasSoftDelete() bool

HasSoftDelete returns true if the table has a soft delete field (implements TableView).

func (*TableMeta[T]) Insert

func (t *TableMeta[T]) Insert(ctx context.Context, targets ...*T) error

func (*TableMeta[T]) InsertIgnore

func (t *TableMeta[T]) InsertIgnore(ctx context.Context, targets ...*T) error

func (*TableMeta[T]) Iter

func (t *TableMeta[T]) Iter(ctx context.Context, where any, opts ...*FetchOptions) (func(func(v *T) bool), error)

func (*TableMeta[T]) MainKey

func (t *TableMeta[T]) MainKey() *StructKey

MainKey returns the primary/unique key (implements TableView).

func (*TableMeta[T]) Name

func (t *TableMeta[T]) Name() string

func (*TableMeta[T]) Replace

func (t *TableMeta[T]) Replace(ctx context.Context, targets ...*T) error

func (*TableMeta[T]) Restore

func (t *TableMeta[T]) Restore(ctx context.Context, where any) (sql.Result, error)

func (*TableMeta[T]) ScanTo

func (t *TableMeta[T]) ScanTo(ctx context.Context, row *sql.Rows, v *T) error

func (*TableMeta[T]) TableAttrs

func (t *TableMeta[T]) TableAttrs() map[string]string

TableAttrs returns the table-level attributes (implements TableView).

func (*TableMeta[T]) TableName

func (t *TableMeta[T]) TableName() string

TableName returns the raw table name (implements TableView).

func (*TableMeta[T]) Update

func (t *TableMeta[T]) Update(ctx context.Context, target ...*T) error

type TableMetaIntf

type TableMetaIntf interface {
	Name() string
}

type TableView

type TableView interface {
	TableName() string
	FormattedName(be *Backend) string
	AllFields() []*StructField
	AllKeys() []*StructKey
	MainKey() *StructKey
	FieldByColumn(col string) *StructField
	FieldStr() string
	TableAttrs() map[string]string
	HasSoftDelete() bool
}

TableView is a non-generic interface for accessing table metadata from dialect implementations (which cannot use type parameters).

type TxProxy

type TxProxy struct {
	*sql.Tx
	// contains filtered or unexported fields
}

TxProxy wraps a *sql.Tx with support for nested transactions via SQL savepoints. Create one with BeginTx or Tx. Calling TxProxy.BeginTx on an existing TxProxy creates a savepoint instead of a new transaction. Commit releases the savepoint (or commits the real transaction at depth 0), and Rollback rolls back to the savepoint (or the full transaction at depth 0).

func BeginTx

func BeginTx(ctx context.Context, opts *sql.TxOptions) (*TxProxy, error)

BeginTx starts a new transaction. If the context already contains a transaction, a nested transaction is created using a SQL savepoint. Use ContextTx to attach the returned TxProxy to a context for use with psql operations.

func (*TxProxy) BeginTx

func (t *TxProxy) BeginTx(ctx context.Context, opts *sql.TxOptions) (*TxProxy, error)

func (*TxProxy) Commit

func (tx *TxProxy) Commit() error

func (*TxProxy) Rollback

func (tx *TxProxy) Rollback() error

type TypeMapper

type TypeMapper interface {
	SqlType(baseType string, attrs map[string]string) string
	FieldDef(column, sqlType string, nullable bool, attrs map[string]string) string
	FieldDefAlter(column, sqlType string, nullable bool, attrs map[string]string) string
}

TypeMapper handles engine-specific SQL type mapping and field definitions.

type UpsertRenderer

type UpsertRenderer interface {
	ReplaceSQL(tableName, fldStr, placeholders string, mainKey *StructKey, fields []*StructField) string
	InsertIgnoreSQL(tableName, fldStr, placeholders string) string
}

UpsertRenderer handles engine-specific REPLACE and INSERT IGNORE syntax.

type Vector

type Vector []float32

Vector represents a vector of float32 values, compatible with PostgreSQL pgvector and CockroachDB native vector types.

Usage in struct tags:

Embedding Vector `sql:",type=VECTOR,size=3"` // 3-dimensional vector

func (Vector) Dimensions

func (v Vector) Dimensions() int

Dimensions returns the number of dimensions in the vector.

func (*Vector) Scan

func (v *Vector) Scan(src any) error

Scan implements sql.Scanner for reading vector values from the database. Supports PostgreSQL pgvector format [1,2,3] and string representations.

func (Vector) String

func (v Vector) String() string

String returns the vector in PostgreSQL/CockroachDB format: [1,2,3]

func (Vector) Value

func (v Vector) Value() (driver.Value, error)

Value implements driver.Valuer for writing vector values to the database.

type VectorComparison

type VectorComparison struct {
	Field any    // typically a fieldName via psql.F()
	Vec   Vector // the vector to compare against
	Op    string // "=" or "<>"
}

VectorComparison represents a vector equality or inequality comparison. It implements EscapeValueable for use in WHERE clauses.

Use VecEqual and VecNotEqual instead of constructing directly.

func VecEqual

func VecEqual(field any, vec Vector) *VectorComparison

VecEqual creates a vector equality comparison (field = vector).

psql.B().Select().From("items").Where(psql.VecEqual(psql.F("Embedding"), targetVec))

func VecNotEqual

func VecNotEqual(field any, vec Vector) *VectorComparison

VecNotEqual creates a vector inequality comparison (field <> vector).

psql.B().Select().From("items").Where(psql.VecNotEqual(psql.F("Embedding"), targetVec))

func (*VectorComparison) EscapeValue

func (c *VectorComparison) EscapeValue() string

EscapeValue renders the comparison without engine context.

func (*VectorComparison) String

func (c *VectorComparison) String() string

String returns a display representation.

type VectorDistance

type VectorDistance struct {
	Field any              // typically a fieldName via psql.F()
	Vec   Vector           // the query vector
	Op    VectorDistanceOp // distance type
}

VectorDistance represents a vector distance calculation between a field and a vector. It implements EscapeValueable and SortValueable so it can be used in WHERE and ORDER BY.

Usage:

// In ORDER BY (nearest neighbor search):
psql.B().Select().From("items").OrderBy(psql.VecL2Distance(psql.F("Embedding"), queryVec))

// In WHERE (filter by distance threshold):
psql.B().Select().From("items").Where(
    psql.Lt(psql.VecCosineDistance(psql.F("Embedding"), queryVec), 0.5),
)

func VecCosineDistance

func VecCosineDistance(field any, vec Vector) *VectorDistance

VecCosineDistance creates a cosine distance expression. PostgreSQL/CockroachDB renders as: field <=> '[1,2,3]'

func VecInnerProduct

func VecInnerProduct(field any, vec Vector) *VectorDistance

VecInnerProduct creates a negative inner product distance expression. PostgreSQL/CockroachDB renders as: field <#> '[1,2,3]'

func VecL2Distance

func VecL2Distance(field any, vec Vector) *VectorDistance

VecL2Distance creates an L2 (Euclidean) distance expression. PostgreSQL/CockroachDB renders as: field <-> '[1,2,3]'

func (*VectorDistance) EscapeValue

func (d *VectorDistance) EscapeValue() string

EscapeValue renders the distance expression without engine context (defaults to function syntax).

func (*VectorDistance) String

func (d *VectorDistance) String() string

String returns a display representation (not engine-specific).

type VectorDistanceOp

type VectorDistanceOp int

VectorDistanceOp represents the type of vector distance operation.

const (
	// VectorL2 is the L2 (Euclidean) distance operator.
	// PostgreSQL/CockroachDB: <->   Fallback: vec_l2_distance()
	VectorL2 VectorDistanceOp = iota
	// VectorCosine is the cosine distance operator.
	// PostgreSQL/CockroachDB: <=>   Fallback: vec_cosine_distance()
	VectorCosine
	// VectorInnerProduct is the negative inner product operator.
	// PostgreSQL/CockroachDB: <#>   Fallback: vec_inner_product()
	VectorInnerProduct
)

type VectorRenderer

type VectorRenderer interface {
	VectorDistanceExpr(fieldExpr, vecExpr string, op VectorDistanceOp) string
}

VectorRenderer handles engine-specific vector distance syntax.

type WhereAND

type WhereAND []any

func (WhereAND) EscapeValue

func (w WhereAND) EscapeValue() string

func (WhereAND) String

func (w WhereAND) String() string

type WhereOR

type WhereOR []any

func (WhereOR) EscapeValue

func (w WhereOR) EscapeValue() string

func (WhereOR) String

func (w WhereOR) String() string

Jump to

Keyboard shortcuts

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