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:
- BeforeSaveHook — fires before Insert, Update, and Replace
- AfterSaveHook — fires after Insert, Update, and Replace
- BeforeInsertHook / AfterInsertHook — fires on Insert and InsertIgnore
- BeforeUpdateHook / AfterUpdateHook — fires on Update
- AfterScanHook — fires after scanning a row from any fetch operation
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
- Variables
- func Case(operand ...any) *caseExpr
- func CollectEnumConstraints(tv TableView, be *Backend) map[string]*EnumConstraint
- func ContextBackend(ctx context.Context, be *Backend) context.Context
- func ContextConn(ctx context.Context, conn *sql.Conn) context.Context
- func ContextDB(ctx context.Context, db *sql.DB) context.Context
- func ContextTx(ctx context.Context, tx *TxProxy) context.Context
- func Count[T any](ctx context.Context, where any, opts ...*FetchOptions) (int, error)
- func DefaultExportArg(v any) any
- func DefineMagicType(typ string, definition string)
- func DefineMagicTypeEngine(e Engine, typ string, definition string)
- func Delete[T any](ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)
- func DeleteOne[T any](ctx context.Context, where any, opts ...*FetchOptions) error
- func ErrorNumber(err error) uint16
- func Escape(val any) string
- func EscapeTx(ctx context.Context) (context.Context, bool)
- func Exec(q *SQLQuery) errordeprecated
- func ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
- func Factory[T any](ctxs ...context.Context) *T
- func Fetch[T any](ctx context.Context, where any, opts ...*FetchOptions) ([]*T, error)
- func FetchGrouped[T any](ctx context.Context, where map[string]any, key string, opts ...*FetchOptions) (map[string][]*T, error)
- func FetchMapped[T any](ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string]*T, error)
- func FetchOne[T any](ctx context.Context, target *T, where any, opts ...*FetchOptions) error
- func ForceDelete[T any](ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)
- func GenerateEnumCheckSQL(constraint *EnumConstraint, tableName string) string
- func Get[T any](ctx context.Context, where any, opts ...*FetchOptions) (*T, error)
- func GetEnumConstraintName(values string) string
- func GetEnumTypeName(values string) string
- func HasChanged[T any](obj *T) bool
- func Init(dsn string) error
- func Insert[T any](ctx context.Context, target ...*T) error
- func InsertIgnore[T any](ctx context.Context, target ...*T) error
- func IsDuplicate(err error) bool
- func IsNotExist(err error) bool
- func Iter[T any](ctx context.Context, where any, opts ...*FetchOptions) (func(func(v *T) bool), error)
- func Preload[T any](ctx context.Context, targets []*T, fields ...string) error
- func Query(q *SQLQuery, cb func(*sql.Rows) error) errordeprecated
- func QueryContext(ctx context.Context, q *SQLQuery, cb func(*sql.Rows) error) errordeprecated
- func QuoteName(v string) string
- func RegisterBackendFactory(f BackendFactory)
- func RegisterDialect(e Engine, d Dialect)
- func Replace[T any](ctx context.Context, target ...*T) error
- func Restore[T any](ctx context.Context, where any) (sql.Result, error)
- func RunQueryT[T any](ctx context.Context, q *QueryBuilder) ([]*T, error)
- func RunQueryTOne[T any](ctx context.Context, q *QueryBuilder) (*T, error)
- func SetLogger(l Logger)
- func Tx(ctx context.Context, cb func(ctx context.Context) error) error
- func Update[T any](ctx context.Context, target ...*T) error
- func V(v driver.Value) driver.Value
- func WithPoolDefaults(b *Backend)
- type AfterInsertHook
- type AfterSaveHook
- type AfterScanHook
- type AfterUpdateHook
- type Any
- type Backend
- type BackendFactory
- type BackendOption
- type BeforeInsertHook
- type BeforeSaveHook
- type BeforeUpdateHook
- type CamelSnakeNamer
- func (CamelSnakeNamer) CheckerName(table, column string) string
- func (CamelSnakeNamer) ColumnName(table, column string) string
- func (CamelSnakeNamer) EnumTypeName(table, column string) string
- func (CamelSnakeNamer) IndexName(table, column string) string
- func (CamelSnakeNamer) JoinTableName(joinTable string) string
- func (CamelSnakeNamer) SchemaName(table string) string
- func (CamelSnakeNamer) TableName(table string) string
- func (CamelSnakeNamer) UniqueName(table, column string) string
- type Comparison
- type Decrement
- type DefaultNamer
- func (DefaultNamer) CheckerName(table, column string) string
- func (DefaultNamer) ColumnName(table, column string) string
- func (DefaultNamer) EnumTypeName(table, column string) string
- func (DefaultNamer) IndexName(table, column string) string
- func (DefaultNamer) JoinTableName(joinTable string) string
- func (DefaultNamer) SchemaName(table string) string
- func (DefaultNamer) TableName(table string) string
- func (DefaultNamer) UniqueName(table, column string) string
- type Dialect
- type DuplicateChecker
- type Engine
- type EnumConstraint
- type Error
- type ErrorClassifier
- type EscapeTableable
- type EscapeValueable
- func Between(a, start, end any) EscapeValueable
- func Coalesce(args ...any) EscapeValueable
- func DateAdd(expr any, d time.Duration) EscapeValueable
- func DateSub(expr any, d time.Duration) EscapeValueable
- func Equal(a, b any) EscapeValueable
- func Exists(sub *QueryBuilder) EscapeValueable
- func F(field ...string) EscapeValueable
- func Greatest(args ...any) EscapeValueable
- func Gt(a, b any) EscapeValueable
- func Gte(a, b any) EscapeValueable
- func Least(args ...any) EscapeValueable
- func Lt(a, b any) EscapeValueable
- func Lte(a, b any) EscapeValueable
- func NotExists(sub *QueryBuilder) EscapeValueable
- func Now() EscapeValueable
- func Raw(s string) EscapeValueable
- type FetchOptions
- type FindInSet
- type Future
- type Hex
- type Increment
- type Join
- type Key
- type KeyRenderer
- type LegacyNamer
- func (LegacyNamer) CheckerName(table, column string) string
- func (LegacyNamer) ColumnName(table, column string) string
- func (LegacyNamer) EnumTypeName(table, column string) string
- func (LegacyNamer) IndexName(table, column string) string
- func (LegacyNamer) JoinTableName(joinTable string) string
- func (LegacyNamer) SchemaName(table string) string
- func (LegacyNamer) TableName(table string) string
- func (LegacyNamer) UniqueName(table, column string) string
- type Like
- type Logger
- type Name
- type Namer
- type Not
- type QueryBuilder
- func (q *QueryBuilder) AlsoSelect(fields ...any) *QueryBuilder
- func (q *QueryBuilder) Apply(scopes ...Scope) *QueryBuilder
- func (q *QueryBuilder) Delete() *QueryBuilder
- func (q *QueryBuilder) DoNothing() *QueryBuilder
- func (q *QueryBuilder) DoUpdate(fields ...any) *QueryBuilder
- func (q *QueryBuilder) EscapeValue() string
- func (q *QueryBuilder) ExecQuery(ctx context.Context) (sql.Result, error)
- func (q *QueryBuilder) From(table any) *QueryBuilder
- func (q *QueryBuilder) GroupByFields(fields ...any) *QueryBuilder
- func (q *QueryBuilder) Having(having ...any) *QueryBuilder
- func (q *QueryBuilder) InnerJoin(table any, condition ...any) *QueryBuilder
- func (q *QueryBuilder) Insert(fields ...any) *QueryBuilder
- func (q *QueryBuilder) InsertSelect(destTable any) *QueryBuilder
- func (q *QueryBuilder) Into(table EscapeTableable) *QueryBuilder
- func (q *QueryBuilder) Join(joinType string, table any, condition ...any) *QueryBuilder
- func (q *QueryBuilder) LeftJoin(table any, condition ...any) *QueryBuilder
- func (q *QueryBuilder) Limit(v ...int) *QueryBuilder
- func (q *QueryBuilder) OnConflict(columns ...string) *QueryBuilder
- func (q *QueryBuilder) OrderBy(field ...SortValueable) *QueryBuilder
- func (q *QueryBuilder) Prepare(ctx context.Context) (*sql.Stmt, error)
- func (q *QueryBuilder) Render(ctx context.Context) (string, error)
- func (q *QueryBuilder) RenderArgs(ctx context.Context) (string, []any, error)
- func (q *QueryBuilder) Replace(table EscapeTableable) *QueryBuilder
- func (q *QueryBuilder) RightJoin(table any, condition ...any) *QueryBuilder
- func (q *QueryBuilder) RunQuery(ctx context.Context) (*sql.Rows, error)
- func (q *QueryBuilder) Select(fields ...any) *QueryBuilder
- func (q *QueryBuilder) Set(fields ...any) *QueryBuilder
- func (q *QueryBuilder) SetDistinct() *QueryBuilder
- func (q *QueryBuilder) SetForUpdate() *QueryBuilder
- func (q *QueryBuilder) SetNoWait() *QueryBuilder
- func (q *QueryBuilder) SetSkipLocked() *QueryBuilder
- func (q *QueryBuilder) Table(table any) *QueryBuilder
- func (q *QueryBuilder) Update(table any) *QueryBuilder
- func (q *QueryBuilder) Where(where ...any) *QueryBuilder
- type ReturningRenderer
- type SQLQuery
- type SQLQueryT
- type SchemaChecker
- type Scope
- type Set
- type SetRaw
- type SortValueable
- type StructField
- func (f *StructField) DefString(be *Backend) string
- func (f *StructField) DefStringAlter(be *Backend) string
- func (f *StructField) GetAttrs(be *Backend) map[string]string
- func (f *StructField) Matches(be *Backend, typ, null string, col, dflt *string) (bool, error)
- func (f *StructField) SqlType(be *Backend) string
- type StructKey
- func (k *StructKey) CreateIndexSQL(be *Backend, tableName string) string
- func (k *StructKey) DefString(be *Backend) string
- func (k *StructKey) InlineDefString(be *Backend, tableName string) string
- func (k *StructKey) IsUnique() bool
- func (k *StructKey) Keyname() string
- func (k *StructKey) SqlKeyName() string
- type SubIn
- type TableMeta
- func (t *TableMeta[T]) AllFields() []*StructField
- func (t *TableMeta[T]) AllKeys() []*StructKey
- func (t *TableMeta[T]) Count(ctx context.Context, where any, opts ...*FetchOptions) (int, error)
- func (t *TableMeta[T]) Delete(ctx context.Context, where any, opts ...*FetchOptions) (sql.Result, error)
- func (t *TableMeta[T]) DeleteOne(ctx context.Context, where any, opts ...*FetchOptions) error
- func (t *TableMeta[T]) Factory(ctx context.Context) *T
- func (t *TableMeta[T]) Fetch(ctx context.Context, where any, opts ...*FetchOptions) ([]*T, error)
- func (t *TableMeta[T]) FetchGrouped(ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string][]*T, error)
- func (t *TableMeta[T]) FetchMapped(ctx context.Context, where any, key string, opts ...*FetchOptions) (map[string]*T, error)
- func (t *TableMeta[T]) FetchOne(ctx context.Context, target *T, where any, opts ...*FetchOptions) error
- func (t *TableMeta[T]) FieldByColumn(col string) *StructField
- func (t *TableMeta[T]) FieldStr() string
- func (t *TableMeta[T]) FormattedName(be *Backend) string
- func (t *TableMeta[T]) Get(ctx context.Context, where any, opts ...*FetchOptions) (*T, error)
- func (t *TableMeta[T]) HasChanged(obj *T) bool
- func (t *TableMeta[T]) HasSoftDelete() bool
- func (t *TableMeta[T]) Insert(ctx context.Context, targets ...*T) error
- func (t *TableMeta[T]) InsertIgnore(ctx context.Context, targets ...*T) error
- func (t *TableMeta[T]) Iter(ctx context.Context, where any, opts ...*FetchOptions) (func(func(v *T) bool), error)
- func (t *TableMeta[T]) MainKey() *StructKey
- func (t *TableMeta[T]) Name() string
- func (t *TableMeta[T]) Replace(ctx context.Context, targets ...*T) error
- func (t *TableMeta[T]) Restore(ctx context.Context, where any) (sql.Result, error)
- func (t *TableMeta[T]) ScanTo(ctx context.Context, row *sql.Rows, v *T) error
- func (t *TableMeta[T]) TableAttrs() map[string]string
- func (t *TableMeta[T]) TableName() string
- func (t *TableMeta[T]) Update(ctx context.Context, target ...*T) error
- type TableMetaIntf
- type TableView
- type TxProxy
- type TypeMapper
- type UpsertRenderer
- type Vector
- type VectorComparison
- type VectorDistance
- type VectorDistanceOp
- type VectorRenderer
- type WhereAND
- type WhereOR
Constants ¶
const ( // Use " for ANSI SQL, and ` for MySQL's own thing NameQuoteChar = `"` NameQuoteRune = '"' )
const ( KeyPrimary = 1 KeyUnique = 2 KeyIndex = 3 KeyFulltext = 4 KeySpatial = 5 KeyVector = 6 )
Key type constants for StructKey.Typ.
Variables ¶
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)") )
var FetchLock = &FetchOptions{Lock: true}
FetchLock is a FetchOptions that adds SELECT ... FOR UPDATE to lock the selected rows.
var FetchLockNoWait = &FetchOptions{Lock: true, NoWait: true}
FetchLockNoWait is a FetchOptions that adds FOR UPDATE NOWAIT.
var FetchLockSkipLocked = &FetchOptions{Lock: true, SkipLocked: true}
FetchLockSkipLocked is a FetchOptions that adds FOR UPDATE SKIP LOCKED.
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.
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 ¶
ContextBackend attaches a Backend to the context. All psql operations using the returned context will use this backend.
func ContextConn ¶
ContextConn attaches a *sql.Conn to the context, pinning queries to a single connection.
func ContextTx ¶
ContextTx attaches a TxProxy transaction to the context. All queries using the returned context will execute within this transaction.
func Count ¶
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 ¶
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 DefineMagicTypeEngine ¶
func Delete ¶
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 ¶
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 ¶
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 ¶
Escape takes any value and transforms it into a string that can be included in a MySQL query
func EscapeTx ¶
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 ExecContext ¶
ExecContext executes a query (INSERT, UPDATE, DELETE, etc.) using whatever database object is attached to the context (transaction, connection, or backend).
func Fetch ¶
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 ¶
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 ¶
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 ¶
Get will instanciate a new object of type T and return a pointer to it after loading from database
func GetEnumConstraintName ¶
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 ¶
GetEnumTypeName is kept for backward compatibility but returns the constraint name
func HasChanged ¶
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 ¶
Init creates a new Backend using the given DSN and sets it as DefaultBackend. See New for supported DSN formats.
func Insert ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 QuoteName ¶
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 ¶
RegisterDialect registers a Dialect for the given engine. This allows backend submodules to provide their own dialect implementations.
func Replace ¶
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 ¶
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 ¶
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 ¶
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 WithPoolDefaults ¶
func WithPoolDefaults(b *Backend)
WithPoolDefaults configures standard connection pool settings (128 max open, 32 max idle, 3 min lifetime).
Types ¶
type AfterInsertHook ¶
AfterInsertHook is called after a successful INSERT operation.
type AfterSaveHook ¶
AfterSaveHook is called after successful INSERT, UPDATE, and REPLACE operations.
type AfterScanHook ¶
AfterScanHook is called after a row is scanned from the database during fetch operations.
type AfterUpdateHook ¶
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 ¶
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 ¶
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) DriverData ¶
DriverData returns the engine-specific driver data (e.g., *pgxpool.Pool for PostgreSQL).
func (*Backend) Engine ¶
Engine returns the database engine type (EngineMySQL, EnginePostgreSQL, or EngineSQLite).
func (*Backend) Plug ¶
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 ¶
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 ¶
BeforeInsertHook is called before an INSERT operation.
type BeforeSaveHook ¶
BeforeSaveHook is called before INSERT, UPDATE, and REPLACE operations.
type BeforeUpdateHook ¶
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 ¶
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
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 ¶
DuplicateChecker is implemented by dialects that can detect duplicate key errors.
type Engine ¶
type Engine int
Engine identifies the SQL database engine in use.
func (Engine) Placeholders ¶
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".
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 ¶
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.
type ErrorClassifier ¶
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 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 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 ¶
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 ¶
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 ¶
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 (*Future[T]) MarshalJSON ¶
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
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.
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 ¶
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 ¶
CILike creates a case-insensitive Like condition:
psql.CILike(psql.F("name"), "john%")
func (*Like) EscapeValue ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
type SQLQueryT ¶
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.
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.
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 ¶
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 ¶
CreateIndexSQL returns a CREATE INDEX statement, or empty string if the key should be created inline.
func (*StructKey) DefString ¶
DefString returns the key definition SQL, dispatching to the engine's KeyRenderer if available.
func (*StructKey) InlineDefString ¶
InlineDefString returns the inline constraint definition for CREATE TABLE.
func (*StructKey) SqlKeyName ¶
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 ¶
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]) FetchGrouped ¶
func (*TableMeta[T]) FetchMapped ¶
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 ¶
FieldStr returns the comma-separated quoted field list (implements TableView).
func (*TableMeta[T]) FormattedName ¶
FormattedName returns the table name, applying the namer transformation if needed
func (*TableMeta[T]) HasChanged ¶
func (*TableMeta[T]) HasSoftDelete ¶
HasSoftDelete returns true if the table has a soft delete field (implements TableView).
func (*TableMeta[T]) InsertIgnore ¶
func (*TableMeta[T]) TableAttrs ¶
TableAttrs returns the table-level attributes (implements TableView).
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 ¶
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 ¶
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.
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 ¶
Dimensions returns the number of dimensions in the vector.
func (*Vector) Scan ¶
Scan implements sql.Scanner for reading vector values from the database. Supports PostgreSQL pgvector format [1,2,3] and string representations.
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.
Source Files
¶
- any.go
- assoc.go
- backend.go
- between.go
- builder.go
- builder_typed.go
- caseexpr.go
- change.go
- check.go
- coalesce.go
- comparison.go
- context.go
- count.go
- dateadd.go
- delete.go
- dialect.go
- dialect_util.go
- doc.go
- engine.go
- enum.go
- error.go
- escape.go
- exists.go
- export.go
- factory.go
- fetch.go
- fetchmap.go
- field.go
- fieldname.go
- findinset.go
- formatname.go
- greatest.go
- hex.go
- hooks.go
- ilike.go
- insert.go
- join.go
- key.go
- lazy.go
- like.go
- log.go
- magic.go
- main.go
- mysql.go
- name.go
- namer.go
- query.go
- render.go
- replace.go
- rowstate.go
- scope.go
- set.go
- setexpr.go
- setter.go
- softdelete.go
- subquery.go
- table.go
- tag.go
- tx.go
- types.go
- update.go
- vector.go
- vector_distance.go
- where.go