sqlex

package module
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 13 Imported by: 0

README

English | 中文

CI Go Report Card GoDoc

sqlex

A modern enhancement of Go's database/sql — inheriting all capabilities of jmoiron/sqlx with added Hook aspects, generic JSON types, and more.

Table of Contents

Highlights

  • 🔄 Fully compatible with database/sql — all standard methods preserved, incremental enhancements
  • 🏗️ Struct scanningGet/Select maps query results directly to Go structs
  • 📝 Named parameters:name style named queries, supporting structs and maps
  • 🪝 Hook aspects — pluggable SQL execution interceptors (logging, tracing, metrics)
  • 📦 JsonValue[T] — generic JSON column type, mapping database JSON fields to strong types
  • 🔀 Transparent IN expansion — auto-detects slice args and expands IN clauses
  • 🚀 Cross-database out of the box — write SQL with ? placeholders universally; the framework auto-converts to target database format ($N, :argN, @pN)
  • 🔐 Enhanced transaction managementCloseWithErr auto-commits/rolls back
  • 🎯 NamedExt/BindExt unified interfaces — DB, Tx, and Conn share the same extended method signatures
  • 🛠️ Multi-driver support — PostgreSQL, MySQL, SQLite, Oracle, SQL Server
  • 🛡️ StrictMode — off by default (lenient mode); enable with SetStrict(true) to catch field mismatches early in development
  • Go 1.24 modernizationany type, modular file structure, enhanced error messages

Installation

go get github.com/go-sqlex/sqlex

Requires Go 1.24 or later.

Version Specification

You can also pin to a specific version using semantic versioning:

# Latest version
go get github.com/go-sqlex/sqlex@latest

# Specific version
go get github.com/go-sqlex/sqlex@v1.5.0

# Or in go.mod
require github.com/go-sqlex/sqlex v1.5.0

For the list of available versions and releases, see GitHub Releases.

Quick Start

package main

import (
    "fmt"
    "log"

    "github.com/go-sqlex/sqlex"
    _ "github.com/mattn/go-sqlite3"
)

type User struct {
    ID    int    `db:"id"`
    Name  string `db:"name"`
    Email string `db:"email"`
}

func main() {
    // Connect to database
    db, err := sqlex.Connect("sqlite3", ":memory:")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Create table
    db.MustExec(`CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)`)
    db.MustExec(`INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')`)
    db.MustExec(`INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')`)

    // Query single row
    var user User
    err = db.Get(&user, "SELECT * FROM users WHERE id = ?", 1)
    fmt.Printf("User: %+v\n", user)

    // Query multiple rows
    var users []User
    err = db.Select(&users, "SELECT * FROM users")
    fmt.Printf("Users: %+v\n", users)
}

Features

Core capabilities inherited from sqlx
  • Struct scanning: Get, Select, StructScan map row results to Go structs
  • Named queries: :name style parameters via NamedQuery, NamedExec, NamedStmt
  • Multi-driver binding: Rebind auto-converts ? to target database bindvars ($1, :arg1, @p1)
  • IN clause expansion: In() function expands slice args into multiple placeholders
  • Multiple scan modes: StructScan, SliceScan, MapScan
  • Prepared statements: Preparex, PrepareNamed with enhanced preparation
  • Connection management: Connect (with Ping), Open, MustConnect
sqlex new capabilities
Feature Description
Hook aspects AddHook — pluggable SQL execution interceptors (onion model)
JsonValue[T] types.JsonValue[T] — generic JSON column type
NamedGet/NamedSelect Named parameter convenience methods on DB/Tx (built-in IN expansion)
NamedExec/NamedExecContext Named parameter execution (built-in IN expansion)
CloseWithErr Auto Commit/Rollback based on error
NamedExt interface Unified named parameter programming interface for DB/Tx/Conn
BindExt interface Unified basic query programming interface for DB/Tx/Conn
Positional auto-IN Select/Get/Exec/Queryx/QueryRowx/MustExec (with Context versions) auto-detect slice args and expand IN clauses
Auto Rebind All query methods auto-convert ? to target database placeholders, zero code change for cross-database support
StrictMode Default lenient mode (strict=false); enable with SetStrict(true) for detailed errors on column-field mismatch

Usage Examples

Basic CRUD
// Use ? placeholders universally — the framework auto-converts
// to target database bindvar format ($N, :argN, @pN)

// Insert
result, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "Alice", "alice@example.com")

// Query single row → struct
var user User
err = db.Get(&user, "SELECT * FROM users WHERE id = ?", 1)

// Query multiple rows → slice
var users []User
err = db.Select(&users, "SELECT * FROM users WHERE age > ?", 18)

// Update
_, err = db.Exec("UPDATE users SET name = ? WHERE id = ?", "Alice Updated", 1)

// Delete
_, err = db.Exec("DELETE FROM users WHERE id = ?", 1)
Named Parameter Queries
// Using struct as parameter
user := User{Name: "Alice", Email: "alice@example.com"}
_, err = db.NamedExec(`INSERT INTO users (name, email) VALUES (:name, :email)`, user)

// Using map as parameter
params := map[string]any{"name": "Alice"}

// NamedGet — query single row
var result User
err = db.NamedGet(&result, `SELECT * FROM users WHERE name = :name`, params)

// NamedSelect — query multiple rows
var results []User
err = db.NamedSelect(&results, `SELECT * FROM users WHERE name = :name`, params)
IN Queries
ids := []int{1, 2, 3, 4, 5}

// Positional: auto-detects slice and expands IN
var users []User
err = db.Select(&users, "SELECT * FROM users WHERE id IN (?)", ids)

// Named: built-in IN expansion
err = db.NamedSelect(&users,
    `SELECT * FROM users WHERE id IN (:ids) AND status = :status`,
    map[string]any{"ids": ids, "status": "active"})

Note: sqlex.In() / sqlex.Named() are legacy top-level functions; the framework calls them automatically. Use the high-level methods above which include Rebind/Hook/StrictMode.

Escape APIs for edge cases
import "github.com/go-sqlex/sqlex"

// sqlex.AsValue(v) — force no expansion (even in (?) context)
db.Select(&rows, `SELECT * FROM t WHERE id = ANY(?)`,
    sqlex.AsValue(pq.Array([]int{1, 2, 3})))

// sqlex.AsList(slice) — force expansion (even outside (?) context)
db.Exec(`SELECT some_func(?, ?)`, 100,
    sqlex.AsList([]int{1, 2, 3}))
Prepared Statements
// Preparex auto-Rebinds — use ? uniformly
stmt, err := db.Preparex("SELECT * FROM users WHERE name = ?")
var user User
err = stmt.Get(&user, "Alice")

// PrepareNamed — named prepared statement
nstmt, err := db.PrepareNamed("SELECT * FROM users WHERE name = :name")
err = nstmt.Get(&user, map[string]any{"name": "Alice"})
Transaction Management
// Recommended pattern: CloseWithErr auto-management
func createUserWithProfile(db *sqlex.DB, user User, profile Profile) (err error) {
    tx, err := db.Beginx()
    if err != nil {
        return err
    }
    defer func() { tx.CloseWithErr(err) }() // auto Commit or Rollback

    _, err = tx.NamedExec(`INSERT INTO users (name) VALUES (:name)`, user)
    if err != nil {
        return err // CloseWithErr detects err != nil, auto Rollback
    }

    _, err = tx.NamedExec(`INSERT INTO profiles (user_name, bio) VALUES (:user_name, :bio)`, profile)
    return nil // CloseWithErr detects err == nil, auto Commit
}
JsonValue[T]
import "github.com/go-sqlex/sqlex/types"

type Article struct {
    ID       int                            `db:"id"`
    Title    string                         `db:"title"`
    Metadata types.JsonValue[ArticleMeta]   `db:"metadata"`
}

type ArticleMeta struct {
    Tags      []string `json:"tags"`
    ViewCount int      `json:"view_count"`
}

// Write — auto-serializes to JSON
article := Article{
    Title:    "Hello World",
    Metadata: types.NewJsonValue(ArticleMeta{
        Tags:      []string{"go", "sql"},
        ViewCount: 0,
    }),
}
db.NamedExec(`INSERT INTO articles (title, metadata) VALUES (:title, :metadata)`, article)

// Read — auto-deserializes
var a Article
db.Get(&a, "SELECT * FROM articles WHERE id = ?", 1)
Hook Aspects
// Custom Hook — e.g., OpenTelemetry tracing
type TracingHook struct{}

func (h *TracingHook) BeforeQuery(ctx context.Context, event *sqlex.QueryEvent) context.Context {
    ctx, span := tracer.Start(ctx, "sql."+event.OperationType)
    span.SetAttributes(attribute.String("db.statement", event.Query))
    return ctx
}

func (h *TracingHook) AfterQuery(ctx context.Context, event *sqlex.QueryEvent) {
    span := trace.SpanFromContext(ctx)
    if event.Error != nil {
        span.RecordError(event.Error)
    }
    span.End()
}

db.AddHook(&TracingHook{})
StrictMode
// Default: lenient mode (strict=false), silently ignores extra columns
db, _ := sqlex.Connect("postgres", dsn)
fmt.Println(db.IsStrict()) // false

// Enable strict mode: returns detailed error on field mismatch
db.SetStrict(true)
err = db.Select(&users, "SELECT * FROM users")
// err: missing destination name email (index 2), age (index 3) in UserPartial

// strict auto-propagates to Tx/Conn
tx, _ := db.Beginx()    // inherits DB's strict setting
conn, _ := db.Connx(ctx) // inherits DB's strict setting
NamedExt / BindExt Unified Interfaces
// NamedExt: write context-agnostic functions (named parameters)
func getUserByName(ext sqlex.NamedExt, name string) (*User, error) {
    var user User
    err := ext.NamedGet(&user, `SELECT * FROM users WHERE name = :name`,
        map[string]any{"name": name})
    return &user, err
}

// Works with DB, Tx, or Conn
user, err := getUserByName(db, "Alice")
tx, _ := db.Beginx()
user, err = getUserByName(tx, "Bob")

// BindExt: write context-agnostic functions (positional parameters)
func listUsers(ext sqlex.BindExt, minAge int) ([]User, error) {
    var users []User
    err := ext.Select(&users, "SELECT * FROM users WHERE age > ?", minAge)
    return users, err
}

Comparison with jmoiron/sqlx

Feature jmoiron/sqlx sqlex
Go version 1.10+ 1.24+
Struct scanning
Named queries
Bindvar conversion ✅ (enhanced: supports \? and ?? escape, skips string literals, identifiers, comments, PG dollar quoting)
IN clause expansion In() ✅ Auto-IN across all DB/Tx/Conn × Exec/Query/Select/Get/Named* paths
Cross-database placeholders ❌ Manual Rebind ✅ All methods auto-Rebind, use ? uniformly (including Preparex)
Field matching unsafe (default strict) StrictMode (default lenient, more intuitive)
Hook aspects AddHook pluggable SQL interceptors
JsonValue[T] types.JsonValue[T]
NamedGet/NamedSelect ✅ DB/Tx convenience methods
CloseWithErr ✅ Auto transaction management
NamedExt/BindExt interfaces ✅ DB/Tx/Conn unified interfaces
Unicode named parameters ⚠️ Unreliable ❌ Not supported (ASCII only; Unicode elsewhere is safe)
PostgreSQL :: ❌ Misidentified ✅ Correctly handled
Named query string literals ❌ Colons misidentified ✅ Skips colons in strings/comments
Named parameter fallback ❌ Errors on misidentification ✅ Missing params preserved as :name literals

Bug Fixes & Improvements

sqlex fixes the following known issues from jmoiron/sqlx:

  • SQL lexical element handling: Original assumes all ?/:name are placeholders, ignoring string literals, comments, identifiers, and PG dollar quoting. sqlex correctly handles all SQL lexical elements.
  • ConnectContext connection leak: ConnectContext didn't close the connection on Ping failure. sqlex calls db.Close().
  • Rebind escaped question marks: Original doesn't support \? or ?? escapes. sqlex supports both.
  • Rebind string literals: Original replaces ? inside string literals. sqlex correctly skips them.
  • Named query string literal colons: Original misidentifies colons in strings (e.g., IPv6 addresses, time formats) as named parameters (#872).
  • Named parameter fallback: Original errors on missing params; sqlex preserves them as :name literals (#892).
  • Unified lexer: Original has duplicated skip logic in Rebind/In/compileNamedQuery. sqlex uses a shared scanSkipSegment in lexer.go.
  • Named parameter name rules: Original allows digits at start (:123). sqlex requires [A-Za-z_][A-Za-z0-9_.]*.
  • :: handling: Original misidentifies PG type cast :: as named parameter. sqlex correctly skips it.
  • Positional query cross-database failure: Original Select/Get/Exec don't auto-Rebind, failing on PostgreSQL. sqlex auto-Rebinds all methods.

Migration Guide

From jmoiron/sqlx

1. Change import path:

// Old
import "github.com/jmoiron/sqlx"

// New
import "github.com/go-sqlex/sqlex"

2. Change package references:

// Old
db, err := sqlx.Connect("postgres", dsn)

// New
db, err := sqlex.Connect("postgres", dsn)

3. StrictMode default change:

sqlex defaults to lenient mode (strict=false), matching jmoiron/sqlx's unsafe=true. To catch mismatches early:

db.SetStrict(true)

4. Gradual adoption:

All new features are optional:

  • Step 1: Replace import path and package name (zero code changes needed)
  • Step 2: Switch transactions to CloseWithErr pattern
  • Step 3: Use NamedGet/NamedSelect instead of NamedQuery + manual scanning
  • Step 4: Register custom Hooks as needed (logging, tracing, metrics)

Testing

# Main package unit tests (no DB dependency)
go test -count=1 -timeout=120s .

# Cross-DB tests (SQLite only, no external dependencies)
SQLX_MYSQL_DSN=skip SQLX_POSTGRES_DSN=skip \
  go test -count=1 -timeout=120s ./tests/cross_db/

# All tests
go test -count=1 -timeout=300s ./...

# With race detector
go test -v -race -count=1 ./...

DSN configuration: Write complete DSNs directly in .env.test using the SQLX_*_DSN namespace. Set to skip to skip tests for that driver. SQLite defaults to :memory:.

Performance

  • Zero-overhead principle: No Hook overhead when unregistered; auto-Rebind is a no-op for QUESTION-type drivers (MySQL/SQLite)
  • Slice arg detection: needsInRewrite uses reflection type checks (nanosecond-level for non-slice args)
  • Mapper caching: Field mapping results cached after first use
  • Hook execution: Hooks run synchronously; use lightweight operations or async for heavy ones

License

MIT License

Based on jmoiron/sqlx — thanks to Jason Moiron for the excellent work.

Documentation

Overview

Package sqlex is a modern enhancement of database/sql based on jmoiron/sqlx.

sqlex's design philosophy is SQL-centric: developers write SQL directly, and sqlex handles parameter binding and result mapping. No chain APIs, implicit query building, or ORM-style abstractions are introduced.

Core Enhancements

  • Hook aspects: onion-model SQL interceptors supporting logging, monitoring, and tracing
  • JsonValue[T]: generic JSON column type, NULL-safe
  • Auto Rebind: all query methods use ? placeholders uniformly, auto-converting to target database format
  • Transparent IN: Select/Get/NamedGet/NamedSelect auto-expand slice parameters
  • StrictMode: optional strict mode detecting query result / struct field mismatches
  • CloseWithErr: auto-commits or rolls back transactions based on error; failures reported via Hook
  • NamedExt/BindExt: unified interfaces, DB and Tx are interchangeable
  • Functional Options: Open/Connect support WithHooks/WithStrictMode etc.

Quick Start

db, err := sqlex.Connect("postgres", dsn,
    sqlex.WithStrictMode(),
)

var users []User
err = db.Select(&users, "SELECT * FROM users WHERE age > ?", 18)

// Named parameters + auto IN expansion
err = db.NamedSelect(&users, "SELECT * FROM users WHERE id IN (:ids)",
    map[string]any{"ids": []int{1, 2, 3}})

Supported Databases

PostgreSQL, MySQL, SQLite, Oracle, SQL Server

Index

Constants

View Source
const (
	UNKNOWN = iota
	QUESTION
	DOLLAR
	NAMED
	AT
)

Bindvar types supported by Rebind, BindMap and BindStruct.

Variables

View Source
var NameMapper = strings.ToLower

NameMapper is used to map column names to struct field names. By default, it uses strings.ToLower to lowercase struct field names. It can be set to whatever you want, but it is encouraged to be set before sqlex is used as name-to-field mappings are cached after first use on a type.

Warning: NameMapper reads and writes are not concurrency-safe. It is recommended to set it only in an init() function; modifying it at runtime may cause data races. If you need different mapping strategies at runtime, use DB.MapperFunc() to set each DB instance individually.

Functions

func AsList

func AsList(slice any) any

AsList wraps a slice, forcing expansion into an IN list (even if ? is not in the (?) form). Returns an error from In if the argument is not a slice or is an empty slice.

db.Exec("WHERE x = ?", sqlex.AsList([]int{1, 2, 3}))
// Expands to "WHERE x = ?, ?, ?"

func AsValue

func AsValue(v any) any

AsValue wraps a value, telling In/autoIn not to expand it into an IN list; it is passed as a single argument as-is.

Use case: INSERT/UPDATE slice field values, PG's ANY(?)/ALL(?) and other patterns that might be misidentified as the (?) form.

db.Exec("INSERT INTO t (col) VALUES (?)", sqlex.AsValue([]int{1, 2, 3}))
db.Select(&rows, "WHERE id = ANY(?)", sqlex.AsValue(pq.Array([]int{1,2,3})))

func BindDriver

func BindDriver(driverName string, bindType int)

BindDriver sets the BindType for driverName to bindType.

func BindNamed deprecated

func BindNamed(bindType int, query string, arg any) (string, []any, error)

BindNamed binds a struct or a map to a query with named parameters.

Deprecated: It is recommended to use db.NamedSelect / NamedExec / NamedQuery etc. directly, as they already include Rebind/Hook/StrictMode aspects automatically, eliminating the need to call BindNamed manually. Use this function only when you specifically need to control bindType yourself (rare).

func BindType

func BindType(driverName string) int

BindType returns the bindtype for a given database given a drivername.

func Get

func Get(q Queryer, dest any, query string, args ...any) error

Get does a QueryRow using the provided Queryer, and scans the resulting row to dest. If dest is scannable, the result must only have one column. Otherwise, StructScan is used. Get will return sql.ErrNoRows like row.Scan would. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

Note: This function only does "scan dispatch"; it does not perform autoIn / Rebind / Hook — these cross-cutting concerns are guaranteed by the Queryer.QueryRowx implementation (see Queryer interface contract).

func GetContext

func GetContext(ctx context.Context, q QueryerContext, dest any, query string, args ...any) error

GetContext does a QueryRow using the provided Queryer, and scans the resulting row to dest. If dest is scannable, the result must only have one column. Otherwise, StructScan is used. Get will return sql.ErrNoRows like row.Scan would. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

Note: This function only does "scan dispatch"; it does not perform autoIn / Rebind / Hook — these cross-cutting concerns are guaranteed by the QueryerContext.QueryRowxContext implementation.

func In

func In(query string, args ...any) (string, []any, error)

In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database. The `query` should use the `?` bindVar. The return value uses the `?` bindVar.

Lexical skip: ? inside single/double/backtick-quoted strings, dollar-quoted strings, and line/block comments will not be recognized as placeholders; `\?` and `??` output a literal ?. Lexical rules are symmetric with Rebind.

Slice expansion rules ("strict (?) context recognition"):

  • ? in strict (?) form (only ? and optional ASCII whitespace between ( and )) + slice -> expand to ?, ?, ?
  • ? elsewhere + slice -> no expansion, passed as a single value to the driver
  • sqlex.AsValue(v) -> force no expansion (even if ? is in (?) form)
  • sqlex.AsList(slice) -> force expansion (even if ? is not in (?) form)
  • driver.Valuer -> .Value() is called first, then the above rules apply
  • []byte -> treated as a single value (standard driver.Value type)

func MapScan

func MapScan(r ColScanner, dest map[string]any) error

MapScan scans a single Row into the dest map[string]any. Use this to get results for SQL that might not be under your control (for instance, if you're building an interface for an SQL server that executes SQL from input). Please do not use this as a primary interface! This will modify the map sent to it in place, so reuse the same map with care. Columns which occur more than once in the result will overwrite each other!

func MustExec

func MustExec(e Execer, query string, args ...any) sql.Result

MustExec execs the query using e and panics if there was an error. Any placeholder parameters are replaced with supplied args.

func MustExecContext

func MustExecContext(ctx context.Context, e ExecerContext, query string, args ...any) sql.Result

MustExecContext execs the query using e and panics if there was an error. Any placeholder parameters are replaced with supplied args.

func Named deprecated

func Named(query string, arg any) (string, []any, error)

Named takes a query using named parameters and an argument and returns a new query with a list of args that can be executed by a database. The return value uses the `?` bindvar.

Deprecated: It is recommended to use db.NamedSelect / NamedExec / NamedQuery etc. directly, as they already include Rebind/Hook/StrictMode aspects automatically. Use this function only in dynamic SQL composition scenarios (e.g. multi-segment UNION, dynamic WHERE condition merging) that cannot be expressed with the higher-level methods.

func NamedExec

func NamedExec(e Ext, query string, arg any) (sql.Result, error)

NamedExec uses BindStruct to get a query executable by the driver and then runs Exec on the result. Returns an error from the binding or the query execution itself.

func NamedExecContext

func NamedExecContext(ctx context.Context, e ExtContext, query string, arg any) (sql.Result, error)

NamedExecContext uses BindStruct to get a query executable by the driver and then runs Exec on the result. Returns an error from the binding or the query execution itself.

func Rebind

func Rebind(bindType int, query string) string

Rebind a query from the default bindtype (QUESTION) to the target bindtype.

Lexical skip: ? inside single/double/backtick-quoted strings, dollar-quoted strings, and line/block comments will not be replaced; `\?` and `??` output a literal ?. Rules are symmetric with compileNamedQuery / In.

func ScanAll

func ScanAll(rows rowsi, dest any) error

ScanAll scans all rows from an sql.Rows or an sqlex.Rows into the dest slice. ScanAll is the preferred alias for StructScan with a clearer naming convention. If rows is sqlex.Rows, it will use its mapper, otherwise it will use the default.

func Select

func Select(q Queryer, dest any, query string, args ...any) error

Select executes a query using the provided Queryer, and StructScans each row into dest, which must be a slice. If the slice elements are scannable, then the result set must have only one column. Otherwise, StructScan is used. The *sql.Rows are closed automatically. Any placeholder parameters are replaced with supplied args.

Note: This function only does "scan dispatch"; it does not perform autoIn / Rebind / Hook — these cross-cutting concerns are guaranteed by the Queryer.Queryx implementation (see Queryer interface contract).

func SelectContext

func SelectContext(ctx context.Context, q QueryerContext, dest any, query string, args ...any) error

SelectContext executes a query using the provided Queryer, and StructScans each row into dest, which must be a slice. If the slice elements are scannable, then the result set must have only one column. Otherwise, StructScan is used. The *sql.Rows are closed automatically. Any placeholder parameters are replaced with supplied args.

Note: This function only does "scan dispatch"; it does not perform autoIn / Rebind / Hook — these cross-cutting concerns are guaranteed by the QueryerContext.QueryxContext implementation (see QueryerContext interface contract).

func SliceScan

func SliceScan(r ColScanner) ([]any, error)

SliceScan a row, returning a []any with values similar to MapScan. This function is primarily intended for use where the number of columns is not known. Because you can pass an []any directly to Scan, it's recommended that you do that as it will not have to allocate new slices per row.

func StructScan deprecated

func StructScan(rows rowsi, dest any) error

StructScan all rows from an sql.Rows or an sqlex.Rows into the dest slice. StructScan will scan in the entire rows result, so if you do not want to allocate structs for the entire result, use Queryx and see sqlex.Rows.StructScan. If rows is sqlex.Rows, it will use its mapper, otherwise it will use the default.

Deprecated: Use ScanAll instead, which has a clearer name.

Types

type BindExt

type BindExt interface {
	// --- High-level convenience methods (struct scanning) ---
	Select(dest any, query string, args ...any) error
	SelectContext(ctx context.Context, dest any, query string, args ...any) error
	Get(dest any, query string, args ...any) error
	GetContext(ctx context.Context, dest any, query string, args ...any) error

	// --- Execution (INSERT/UPDATE/DELETE) ---
	Exec(query string, args ...any) (sql.Result, error)
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

	// --- Low-level query primitives ---
	Queryx(query string, args ...any) (*Rows, error)
	QueryxContext(ctx context.Context, query string, args ...any) (*Rows, error)
	QueryRowx(query string, args ...any) *Row
	QueryRowxContext(ctx context.Context, query string, args ...any) *Row
}

BindExt is a unified interface that keeps the basic query method signatures of DB, Tx, and Conn consistent, enabling generic data access functions that are agnostic to the execution context (DB / Tx / Conn). This interface covers high-level convenience methods, execution, and low-level query primitives, unifying the capabilities of Queryer, Execer, QueryerContext, and ExecerContext.

Contract guarantees (all implementations must satisfy):

  1. Automatic IN expansion: when slice arguments are passed, IN (?) is automatically expanded to IN (?,?,...,?), consistent with NamedExt behavior; zero overhead when no slices. Applicable methods: Exec/ExecContext, Queryx/QueryxContext, QueryRowx/QueryRowxContext, Select/SelectContext, Get/GetContext.
  2. Automatic Rebind: callers use unified ? placeholders; internally converted to the target database's bindvar format (?/$N/:argN/@pN) without worrying about underlying differences.
  3. Hook chain integration: all methods trigger BeforeQuery/AfterQuery hooks; the observable query is the final SQL after autoIn expansion + Rebind (i.e. the SQL actually sent to the driver).

type ColScanner

type ColScanner interface {
	Columns() ([]string, error)
	Scan(dest ...any) error
	Err() error
}

ColScanner is an interface used by MapScan and SliceScan

type Conn

type Conn struct {
	*sql.Conn

	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

Conn is a wrapper around sql.Conn with extra functionality

func (*Conn) BeginTxx

func (c *Conn) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)

BeginTxx begins a transaction and returns an *sqlex.Tx instead of an *sql.Tx.

func (*Conn) BindNamed

func (c *Conn) BindNamed(query string, arg any) (string, []any, error)

BindNamed binds a query within a Conn's bindvar type.

func (*Conn) DriverName

func (c *Conn) DriverName() string

DriverName returns the driverName used by the DB which created this Conn.

func (*Conn) Exec

func (c *Conn) Exec(query string, args ...any) (sql.Result, error)

Exec executes a query without returning any rows. Any placeholder parameters are replaced with supplied args.

func (*Conn) ExecContext

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

ExecContext executes a query without returning any rows. Overrides sql.Conn's ExecContext to integrate Hook logic. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) Get

func (c *Conn) Get(dest any, query string, args ...any) error

Get using this Conn. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*Conn) GetContext

func (c *Conn) GetContext(ctx context.Context, dest any, query string, args ...any) error

GetContext using this Conn. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*Conn) GetMapper

func (c *Conn) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this Conn.

func (*Conn) IsStrict

func (c *Conn) IsStrict() bool

IsStrict returns whether strict mode is currently enabled.

func (*Conn) MustExec

func (c *Conn) MustExec(query string, args ...any) sql.Result

MustExec (panic) runs MustExec using this Conn. Any placeholder parameters are replaced with supplied args.

func (*Conn) MustExecContext

func (c *Conn) MustExecContext(ctx context.Context, query string, args ...any) sql.Result

MustExecContext (panic) runs MustExec using this Conn. Any placeholder parameters are replaced with supplied args.

func (*Conn) NamedExec

func (c *Conn) NamedExec(query string, arg any) (sql.Result, error)

NamedExec using this Conn. Any named placeholder parameters are replaced with fields from arg. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) NamedExecContext

func (c *Conn) NamedExecContext(ctx context.Context, query string, arg any) (sql.Result, error)

NamedExecContext using this Conn. Any named placeholder parameters are replaced with fields from arg.

Note: This method only does named-to-? conversion and forwarding; autoIn / Rebind / Hook are guaranteed by the downstream c.ExecContext implementation (see ExecerContext interface contract).

func (*Conn) NamedGet

func (c *Conn) NamedGet(dest any, query string, param any) error

NamedGet executes a query with named parameters and scans a single row result. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) NamedGetContext

func (c *Conn) NamedGetContext(ctx context.Context, dest any, query string, param any) error

NamedGetContext executes a query with named parameters and scans a single row result.

func (*Conn) NamedQuery

func (c *Conn) NamedQuery(query string, arg any) (*Rows, error)

NamedQuery using this Conn. Any named placeholder parameters are replaced with fields from arg.

func (*Conn) NamedQueryContext

func (c *Conn) NamedQueryContext(ctx context.Context, query string, arg any) (*Rows, error)

NamedQueryContext using this Conn. Any named placeholder parameters are replaced with fields from arg.

func (*Conn) NamedSelect

func (c *Conn) NamedSelect(dest any, query string, param any) error

NamedSelect executes a query with named parameters and scans the result set into a slice. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) NamedSelectContext

func (c *Conn) NamedSelectContext(ctx context.Context, dest any, query string, param any) error

NamedSelectContext executes a query with named parameters and scans the result set into a slice.

func (*Conn) Prepare

func (c *Conn) Prepare(query string) (*sql.Stmt, error)

Prepare creates a prepared statement for later queries or executions.

func (*Conn) PrepareNamedContext

func (c *Conn) PrepareNamedContext(ctx context.Context, query string) (*NamedStmt, error)

PrepareNamedContext returns an sqlex.NamedStmt

func (*Conn) PreparexContext

func (c *Conn) PreparexContext(ctx context.Context, query string) (*Stmt, error)

PreparexContext returns an sqlex.Stmt instead of a sql.Stmt.

func (*Conn) Query

func (c *Conn) Query(query string, args ...any) (*sql.Rows, error)

Query executes a query that returns rows. Any placeholder parameters are replaced with supplied args.

func (*Conn) QueryRowx

func (c *Conn) QueryRowx(query string, args ...any) *Row

QueryRowx queries the database and returns an *sqlex.Row. Any placeholder parameters are replaced with supplied args.

func (*Conn) QueryRowxContext

func (c *Conn) QueryRowxContext(ctx context.Context, query string, args ...any) *Row

QueryRowxContext queries the database and returns an *sqlex.Row. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) Queryx

func (c *Conn) Queryx(query string, args ...any) (*Rows, error)

Queryx queries the database and returns an *sqlex.Rows. Any placeholder parameters are replaced with supplied args.

func (*Conn) QueryxContext

func (c *Conn) QueryxContext(ctx context.Context, query string, args ...any) (*Rows, error)

QueryxContext queries the database and returns an *sqlex.Rows. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Conn) Rebind

func (c *Conn) Rebind(query string) string

Rebind a query within a Conn's bindvar type.

func (*Conn) Select

func (c *Conn) Select(dest any, query string, args ...any) error

Select using this Conn. Any placeholder parameters are replaced with supplied args.

func (*Conn) SelectContext

func (c *Conn) SelectContext(ctx context.Context, dest any, query string, args ...any) error

SelectContext using this Conn. Any placeholder parameters are replaced with supplied args.

func (*Conn) SetStrict

func (c *Conn) SetStrict(strict bool)

SetStrict enables or disables strict mode. In strict mode (true), an error is returned if the query result contains columns that have no corresponding field in the target struct. In lenient mode (false), mismatched columns are silently ignored.

type DB

type DB struct {
	*sql.DB

	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

DB is a wrapper around sql.DB which keeps track of the driverName upon Open, used mostly to automatically bind named queries using the right bindvars.

func Connect

func Connect(driverName, dataSourceName string, opts ...Opt) (*DB, error)

Connect to a database and verify with a ping.

func ConnectContext

func ConnectContext(ctx context.Context, driverName, dataSourceName string, opts ...Opt) (*DB, error)

ConnectContext to a database and verify with a ping.

func MustConnect

func MustConnect(driverName, dataSourceName string, opts ...Opt) *DB

MustConnect connects to a database and panics on error.

func MustOpen

func MustOpen(driverName, dataSourceName string, opts ...Opt) *DB

MustOpen is the same as sql.Open, but returns an *sqlex.DB instead and panics on error.

func NewDb

func NewDb(db *sql.DB, driverName string, opts ...Opt) *DB

NewDb returns a new sqlex DB wrapper for a pre-existing *sql.DB. The driverName of the original database is required for named query support.

func Open

func Open(driverName, dataSourceName string, opts ...Opt) (*DB, error)

Open is the same as sql.Open, but returns an *sqlex.DB instead.

func (*DB) AddHook

func (db *DB) AddHook(hook Hook)

AddHook registers a Hook instance that applies to all queries on this DB.

func (*DB) BeginTxx

func (db *DB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)

BeginTxx begins a transaction and returns an *sqlex.Tx instead of an *sql.Tx.

The provided context is used until the transaction is committed or rolled back. If the context is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if the context provided to BeginTxx is canceled.

func (*DB) Beginx

func (db *DB) Beginx() (*Tx, error)

Beginx begins a transaction and returns an *sqlex.Tx instead of an *sql.Tx.

func (*DB) BindNamed

func (db *DB) BindNamed(query string, arg any) (string, []any, error)

BindNamed binds a query using the DB driver's bindvar type.

func (*DB) Connx

func (db *DB) Connx(ctx context.Context) (*Conn, error)

Connx returns an *sqlex.Conn instead of an *sql.Conn.

func (*DB) DriverName

func (db *DB) DriverName() string

DriverName returns the driverName passed to the Open function for this DB.

func (*DB) Exec

func (db *DB) Exec(query string, args ...any) (sql.Result, error)

Exec executes a query without returning any rows. Any placeholder parameters are replaced with supplied args. Overrides sql.DB's Exec, delegating to ExecContext to integrate Hook logic.

func (*DB) ExecContext

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

ExecContext executes a query without returning any rows. Overrides sql.DB's ExecContext to integrate Hook logic. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*DB) Get

func (db *DB) Get(dest any, query string, args ...any) error

Get using this DB. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*DB) GetContext

func (db *DB) GetContext(ctx context.Context, dest any, query string, args ...any) error

GetContext using this DB. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*DB) GetMapper

func (db *DB) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this DB.

func (*DB) IsStrict

func (db *DB) IsStrict() bool

IsStrict returns whether strict mode is currently enabled.

func (*DB) MapperFunc

func (db *DB) MapperFunc(mf func(string) string)

MapperFunc sets a new mapper for this db using the default sqlex struct tag and the provided mapper function.

func (*DB) MustBegin

func (db *DB) MustBegin() *Tx

MustBegin starts a transaction, and panics on error. Returns an *sqlex.Tx instead of an *sql.Tx.

func (*DB) MustBeginTxx

func (db *DB) MustBeginTxx(ctx context.Context, opts *sql.TxOptions) *Tx

MustBeginTxx starts a transaction, and panics on error. Returns an *sqlex.Tx instead of an *sql.Tx.

The provided context is used until the transaction is committed or rolled back. If the context is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if the context provided to MustBeginContext is canceled.

func (*DB) MustExec

func (db *DB) MustExec(query string, args ...any) sql.Result

MustExec (panic) runs MustExec using this database. Any placeholder parameters are replaced with supplied args.

func (*DB) MustExecContext

func (db *DB) MustExecContext(ctx context.Context, query string, args ...any) sql.Result

MustExecContext (panic) runs MustExec using this database. Any placeholder parameters are replaced with supplied args.

func (*DB) NamedExec

func (db *DB) NamedExec(query string, arg any) (sql.Result, error)

NamedExec using this DB. Any named placeholder parameters are replaced with fields from arg. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*DB) NamedExecContext

func (db *DB) NamedExecContext(ctx context.Context, query string, arg any) (sql.Result, error)

NamedExecContext using this DB. Any named placeholder parameters are replaced with fields from arg.

Note: This method only does named-to-? conversion and forwarding; autoIn / Rebind / Hook are guaranteed by the downstream db.ExecContext implementation (see ExecerContext interface contract).

func (*DB) NamedGet

func (db *DB) NamedGet(dest any, query string, param any) error

NamedGet executes a query with named parameters and scans a single row result.

func (*DB) NamedGetContext

func (db *DB) NamedGetContext(ctx context.Context, dest any, query string, param any) error

NamedGetContext executes a query with named parameters and scans a single row result.

func (*DB) NamedQuery

func (db *DB) NamedQuery(query string, arg any) (*Rows, error)

NamedQuery using this DB. Any named placeholder parameters are replaced with fields from arg.

func (*DB) NamedQueryContext

func (db *DB) NamedQueryContext(ctx context.Context, query string, arg any) (*Rows, error)

NamedQueryContext using this DB. Any named placeholder parameters are replaced with fields from arg.

func (*DB) NamedSelect

func (db *DB) NamedSelect(dest any, query string, param any) error

NamedSelect executes a query with named parameters and scans the result set into a slice.

func (*DB) NamedSelectContext

func (db *DB) NamedSelectContext(ctx context.Context, dest any, query string, param any) error

NamedSelectContext executes a query with named parameters and scans the result set into a slice.

func (*DB) PrepareNamed

func (db *DB) PrepareNamed(query string) (*NamedStmt, error)

PrepareNamed returns an sqlex.NamedStmt

func (*DB) PrepareNamedContext

func (db *DB) PrepareNamedContext(ctx context.Context, query string) (*NamedStmt, error)

PrepareNamedContext returns an sqlex.NamedStmt

func (*DB) Preparex

func (db *DB) Preparex(query string) (*Stmt, error)

Preparex returns an sqlex.Stmt instead of a sql.Stmt

func (*DB) PreparexContext

func (db *DB) PreparexContext(ctx context.Context, query string) (*Stmt, error)

PreparexContext returns an sqlex.Stmt instead of a sql.Stmt.

The provided context is used for the preparation of the statement, not for the execution of the statement.

func (*DB) QueryRowx

func (db *DB) QueryRowx(query string, args ...any) *Row

QueryRowx queries the database and returns an *sqlex.Row. Any placeholder parameters are replaced with supplied args.

func (*DB) QueryRowxContext

func (db *DB) QueryRowxContext(ctx context.Context, query string, args ...any) *Row

QueryRowxContext queries the database and returns an *sqlex.Row. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*DB) Queryx

func (db *DB) Queryx(query string, args ...any) (*Rows, error)

Queryx queries the database and returns an *sqlex.Rows. Any placeholder parameters are replaced with supplied args.

func (*DB) QueryxContext

func (db *DB) QueryxContext(ctx context.Context, query string, args ...any) (*Rows, error)

QueryxContext queries the database and returns an *sqlex.Rows. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*DB) Rebind

func (db *DB) Rebind(query string) string

Rebind transforms a query from QUESTION to the DB driver's bindvar type.

func (*DB) Select

func (db *DB) Select(dest any, query string, args ...any) error

Select using this DB. Any placeholder parameters are replaced with supplied args.

func (*DB) SelectContext

func (db *DB) SelectContext(ctx context.Context, dest any, query string, args ...any) error

SelectContext using this DB. Any placeholder parameters are replaced with supplied args.

func (*DB) SetStrict

func (db *DB) SetStrict(strict bool)

SetStrict enables or disables strict mode. In strict mode (true), an error is returned if the query result contains columns that have no corresponding field in the target struct. In lenient mode (false, default), mismatched columns are silently ignored.

type Execer

type Execer interface {
	Exec(query string, args ...any) (sql.Result, error)
}

Execer is an interface used by MustExec.

Implementer contract (must be upheld, otherwise cross-cutting concerns of MustExec and other high-level methods will be broken):

  1. Exec must perform autoIn internally (automatic IN slice expansion)
  2. Exec must perform Rebind internally (driver bindvar conversion)
  3. Exec must trigger the Hook chain

sqlex.DB / Tx / Conn satisfy all contract requirements.

type ExecerContext

type ExecerContext interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}

ExecerContext is an interface used by MustExecContext and LoadFileContext.

Implementer contract (must be upheld, otherwise cross-cutting concerns of MustExecContext and other high-level methods will be broken):

  1. ExecContext must perform autoIn internally (automatic IN slice expansion)
  2. ExecContext must perform Rebind internally (driver bindvar conversion)
  3. ExecContext must trigger the Hook chain

sqlex.DB / Tx / Conn satisfy all contract requirements.

type Ext

type Ext interface {
	Queryer
	Execer
	// contains filtered or unexported methods
}

Ext is a union interface which can bind, query, and exec, used by NamedQuery and NamedExec.

type ExtContext

type ExtContext interface {
	QueryerContext
	ExecerContext
	// contains filtered or unexported methods
}

ExtContext is a union interface which can bind, query, and exec, with Context used by NamedQueryContext and NamedExecContext.

type Hook

type Hook interface {
	BeforeQuery(ctx context.Context, event *QueryEvent) context.Context
	AfterQuery(ctx context.Context, event *QueryEvent)
}

Hook is the SQL execution aspect interface. BeforeQuery is called before SQL execution and can modify the context. AfterQuery is called after SQL execution. Multiple Hooks execute BeforeQuery in registration order and AfterQuery in reverse order (onion model).

Note: For QueryRowx/QueryRowxContext methods, the Hook fires immediately after QueryContext returns, before the row data is scanned. Therefore, QueryEvent.Error only reflects errors from the query dispatch phase and does not include subsequent row.Scan() errors (e.g., type mismatch, sql.ErrNoRows). For full lifecycle observability, use the QueryxContext + rows.Next() + StructScan pattern.

type NamedExt

type NamedExt interface {
	// --- High-level convenience methods (struct scanning + automatic IN expansion) ---
	NamedGet(dest any, query string, param any) error
	NamedGetContext(ctx context.Context, dest any, query string, param any) error
	NamedSelect(dest any, query string, param any) error
	NamedSelectContext(ctx context.Context, dest any, query string, param any) error

	// --- Execution (INSERT/UPDATE/DELETE, with built-in IN expansion) ---
	NamedExec(query string, arg any) (sql.Result, error)
	NamedExecContext(ctx context.Context, query string, arg any) (sql.Result, error)

	// --- Low-level query primitives (with built-in IN expansion) ---
	NamedQuery(query string, arg any) (*Rows, error)
	NamedQueryContext(ctx context.Context, query string, arg any) (*Rows, error)
}

NamedExt is a unified interface that keeps the named-parameter method signatures of DB, Tx, and Conn consistent, enabling generic data access functions that are agnostic to the execution context (DB / Tx / Conn). Symmetric with BindExt.

Contract guarantees (all implementations must satisfy):

  1. Automatic IN expansion: when a map/struct field contains a slice, IN (:field) is automatically expanded to IN (?,?,...,?), consistent with BindExt behavior; zero overhead when no slices.
  2. Automatic Rebind: callers use unified :name named placeholders; internally converted to the target database's bindvar format (?/$N/:argN/@pN) without worrying about underlying differences.
  3. Hook chain integration: all methods trigger BeforeQuery/AfterQuery hooks; the observable query is the final SQL after IN expansion + Rebind (i.e. the SQL actually sent to the driver).

type NamedStmt

type NamedStmt struct {
	Params      []string
	QueryString string
	Stmt        *Stmt
	// contains filtered or unexported fields
}

NamedStmt is a prepared statement that executes named queries. Prepare it how you would execute a NamedQuery, but pass in a struct or map when executing.

func (*NamedStmt) Close

func (n *NamedStmt) Close() error

Close closes the named statement.

func (*NamedStmt) Exec

func (n *NamedStmt) Exec(arg any) (sql.Result, error)

Exec executes a named statement using the struct passed. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) ExecContext

func (n *NamedStmt) ExecContext(ctx context.Context, arg any) (sql.Result, error)

ExecContext executes a named statement using the struct passed. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) Get

func (n *NamedStmt) Get(dest any, arg any) error

Get using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) GetContext

func (n *NamedStmt) GetContext(ctx context.Context, dest any, arg any) error

GetContext using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) GetMapper

func (n *NamedStmt) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this NamedStmt.

func (*NamedStmt) MustExec

func (n *NamedStmt) MustExec(arg any) sql.Result

MustExec execs a NamedStmt, panicing on error Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) MustExecContext

func (n *NamedStmt) MustExecContext(ctx context.Context, arg any) sql.Result

MustExecContext execs a NamedStmt, panicing on error Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) Query

func (n *NamedStmt) Query(arg any) (*sql.Rows, error)

Query executes a named statement using the struct argument, returning rows. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryContext

func (n *NamedStmt) QueryContext(ctx context.Context, arg any) (*sql.Rows, error)

QueryContext executes a named statement using the struct argument, returning rows. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryRow

func (n *NamedStmt) QueryRow(arg any) *Row

QueryRow executes a named statement against the database. Because sqlex cannot create a *sql.Row with an error condition pre-set for binding errors, sqlex returns a *sqlex.Row instead. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryRowContext

func (n *NamedStmt) QueryRowContext(ctx context.Context, arg any) *Row

QueryRowContext executes a named statement against the database. Because sqlex cannot create a *sql.Row with an error condition pre-set for binding errors, sqlex returns a *sqlex.Row instead. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryRowx

func (n *NamedStmt) QueryRowx(arg any) *Row

QueryRowx this NamedStmt. Because of limitations with QueryRow, this is an alias for QueryRow. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryRowxContext

func (n *NamedStmt) QueryRowxContext(ctx context.Context, arg any) *Row

QueryRowxContext this NamedStmt. Because of limitations with QueryRow, this is an alias for QueryRow. Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) Queryx

func (n *NamedStmt) Queryx(arg any) (*Rows, error)

Queryx using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) QueryxContext

func (n *NamedStmt) QueryxContext(ctx context.Context, arg any) (*Rows, error)

QueryxContext using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) Select

func (n *NamedStmt) Select(dest any, arg any) error

Select using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

func (*NamedStmt) SelectContext

func (n *NamedStmt) SelectContext(ctx context.Context, dest any, arg any) error

SelectContext using this NamedStmt Any named placeholder parameters are replaced with fields from arg.

type Opt

type Opt func(db *DB)

func WithHooks

func WithHooks(hooks ...Hook) Opt

WithHooks returns an Opt that registers Hook chain(s) when creating a DB.

func WithMapperFunc

func WithMapperFunc(mf func(string) string) Opt

WithMapperFunc returns an Opt that sets a custom field mapping function when creating a DB.

type Preparer

type Preparer interface {
	Prepare(query string) (*sql.Stmt, error)
	// contains filtered or unexported methods
}

Preparer is an interface used by Preparex. It embeds the binder interface, enabling Preparex to automatically Rebind, allowing unified use of ? placeholders without worrying about underlying database differences.

type PreparerContext

type PreparerContext interface {
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
	// contains filtered or unexported methods
}

PreparerContext is an interface used by PreparexContext. It embeds the binder interface, enabling PreparexContext to automatically Rebind, allowing unified use of ? placeholders without worrying about underlying database differences.

type QueryEvent

type QueryEvent struct {
	// Query is the SQL statement.
	Query string
	// Args are the execution parameters.
	Args []any
	// StartTime is the start time (includes Hook chain execution time).
	StartTime time.Time
	// Duration is the total elapsed time from the start of the BeforeQuery chain
	// to the end of the AfterQuery chain. Includes Hook chain execution overhead,
	// suitable for distributed tracing.
	Duration time.Duration
	// Error is the execution error (only set in the AfterQuery phase).
	Error error
	// OperationType is the operation type: query/exec/prepare/commit/rollback.
	OperationType string
}

QueryEvent describes the context of a SQL execution event.

type Queryer

type Queryer interface {
	Query(query string, args ...any) (*sql.Rows, error)
	Queryx(query string, args ...any) (*Rows, error)
	QueryRowx(query string, args ...any) *Row
}

Queryer is an interface used by Get and Select.

Implementer contract (must be upheld, otherwise cross-cutting concerns of Get/Select and other high-level methods will be broken):

  1. Queryx / QueryRowx must perform autoIn internally (automatic IN slice expansion)
  2. Queryx / QueryRowx must perform Rebind internally (driver bindvar conversion)
  3. Queryx / QueryRowx must trigger the Hook chain

sqlex.DB / Tx / Conn satisfy all contract requirements. The top-level Get/Select functions do not perform autoIn / Rebind / Hook themselves — they rely entirely on the Queryer implementation's guarantees. This is the design principle since Phase 2.0.

Note: The standard library's Query method (returning *sql.Rows) is sqlx-compatible legacy and is not covered by this contract.

type QueryerContext

type QueryerContext interface {
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryxContext(ctx context.Context, query string, args ...any) (*Rows, error)
	QueryRowxContext(ctx context.Context, query string, args ...any) *Row
}

QueryerContext is an interface used by GetContext and SelectContext.

Implementer contract (must be upheld, otherwise cross-cutting concerns of GetContext/SelectContext and other high-level methods will be broken):

  1. QueryxContext / QueryRowxContext must perform autoIn internally (automatic IN slice expansion)
  2. QueryxContext / QueryRowxContext must perform Rebind internally (driver bindvar conversion)
  3. QueryxContext / QueryRowxContext must trigger the Hook chain

sqlex.DB / Tx / Conn satisfy all contract requirements.

Note: The standard library's QueryContext method (returning *sql.Rows) is sqlx-compatible legacy and is not covered by this contract.

type Row

type Row struct {
	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

Row is a reimplementation of sql.Row in order to gain access to the underlying sql.Rows.Columns() data, necessary for StructScan.

func (*Row) ColumnTypes

func (r *Row) ColumnTypes() ([]*sql.ColumnType, error)

ColumnTypes returns the underlying sql.Rows.ColumnTypes(), or the deferred error

func (*Row) Columns

func (r *Row) Columns() ([]string, error)

Columns returns the underlying sql.Rows.Columns(), or the deferred error usually returned by Row.Scan()

func (*Row) Err

func (r *Row) Err() error

Err returns the error encountered while scanning.

func (*Row) GetMapper

func (r *Row) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this Row.

func (*Row) MapScan

func (r *Row) MapScan(dest map[string]any) error

MapScan using this Row.

func (*Row) Scan

func (r *Row) Scan(dest ...any) error

Scan is a fixed implementation of sql.Row.Scan, which does not discard the underlying error from the internal rows object if it exists.

func (*Row) SliceScan

func (r *Row) SliceScan() ([]any, error)

SliceScan using this Row.

func (*Row) StructScan

func (r *Row) StructScan(dest any) error

StructScan a single Row into dest.

type Rows

type Rows struct {
	*sql.Rows
	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

Rows is a wrapper around sql.Rows which caches costly reflect operations during a looped StructScan

func NamedQuery

func NamedQuery(e Ext, query string, arg any) (*Rows, error)

NamedQuery binds a named query and then runs Query on the result using the provided Ext (sqlex.Tx, sqlex.Db). It works with both structs and with map[string]any types.

func NamedQueryContext

func NamedQueryContext(ctx context.Context, e ExtContext, query string, arg any) (*Rows, error)

NamedQueryContext binds a named query and then runs Query on the result using the provided Ext (sqlex.Tx, sqlex.Db). It works with both structs and with map[string]any types.

func (*Rows) GetMapper

func (r *Rows) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this Rows.

func (*Rows) MapScan

func (r *Rows) MapScan(dest map[string]any) error

MapScan using this Rows.

func (*Rows) SliceScan

func (r *Rows) SliceScan() ([]any, error)

SliceScan using this Rows.

func (*Rows) StructScan

func (r *Rows) StructScan(dest any) error

StructScan is like sql.Rows.Scan, but scans a single Row into a single Struct. Use this and iterate over Rows manually when the memory load of Select() might be prohibitive. *Rows.StructScan caches the reflect work of matching up column positions to fields to avoid that overhead per scan, which means it is not safe to run StructScan on the same Rows instance with different struct types.

Note: Rows, like database/sql.Rows, is not safe for concurrent use. Do not call StructScan on the same Rows instance from multiple goroutines.

type Stmt

type Stmt struct {
	*sql.Stmt
	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

Stmt is an sqlex wrapper around sql.Stmt with extra functionality

func Preparex

func Preparex(p Preparer, query string) (*Stmt, error)

Preparex prepares a statement. Automatically converts ? placeholders to the target database's bindvar format (e.g. PostgreSQL's $N), consistent with other query methods, enabling unified ?-style SQL authoring.

func PreparexContext

func PreparexContext(ctx context.Context, p PreparerContext, query string) (*Stmt, error)

PreparexContext prepares a statement. Automatically converts ? placeholders to the target database's bindvar format (e.g. PostgreSQL's $N), consistent with other query methods, enabling unified ?-style SQL authoring.

The provided context is used for the preparation of the statement, not for the execution of the statement.

func (*Stmt) Exec

func (s *Stmt) Exec(args ...any) (sql.Result, error)

Exec executes the prepared statement with the given arguments.

func (*Stmt) ExecContext

func (s *Stmt) ExecContext(ctx context.Context, args ...any) (sql.Result, error)

ExecContext executes the prepared statement with the given arguments.

func (*Stmt) Get

func (s *Stmt) Get(dest any, args ...any) error

Get using the prepared statement. An error is returned if the result set is empty.

func (*Stmt) GetContext

func (s *Stmt) GetContext(ctx context.Context, dest any, args ...any) error

GetContext using the prepared statement. An error is returned if the result set is empty.

func (*Stmt) GetMapper

func (s *Stmt) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this Stmt.

func (*Stmt) MustExec

func (s *Stmt) MustExec(args ...any) sql.Result

MustExec (panic) using this statement.

func (*Stmt) MustExecContext

func (s *Stmt) MustExecContext(ctx context.Context, args ...any) sql.Result

MustExecContext (panic) using this statement.

func (*Stmt) Query

func (s *Stmt) Query(args ...any) (*sql.Rows, error)

Query executes the prepared statement with the given arguments.

func (*Stmt) QueryContext

func (s *Stmt) QueryContext(ctx context.Context, args ...any) (*sql.Rows, error)

QueryContext executes the prepared statement with the given arguments.

func (*Stmt) QueryRowx

func (s *Stmt) QueryRowx(args ...any) *Row

QueryRowx using the prepared statement.

func (*Stmt) QueryRowxContext

func (s *Stmt) QueryRowxContext(ctx context.Context, args ...any) *Row

QueryRowxContext using the prepared statement.

func (*Stmt) Queryx

func (s *Stmt) Queryx(args ...any) (*Rows, error)

Queryx using the prepared statement.

func (*Stmt) QueryxContext

func (s *Stmt) QueryxContext(ctx context.Context, args ...any) (*Rows, error)

QueryxContext using the prepared statement.

func (*Stmt) Select

func (s *Stmt) Select(dest any, args ...any) error

Select using the prepared statement.

func (*Stmt) SelectContext

func (s *Stmt) SelectContext(ctx context.Context, dest any, args ...any) error

SelectContext using the prepared statement.

type Tx

type Tx struct {
	*sql.Tx

	Mapper *reflectx.Mapper
	// contains filtered or unexported fields
}

Tx is sqlex's enhanced wrapper around sql.Tx. Note: Tx, like database/sql.Tx, is not safe for concurrent use. Do not share the same Tx instance across goroutines for concurrent queries. If you need serialized operations within a transaction, use the ExecFunc method, which is the only method that provides internal locking.

func (*Tx) BindNamed

func (tx *Tx) BindNamed(query string, arg any) (string, []any, error)

BindNamed binds a query within a transaction's bindvar type.

func (*Tx) CloseWithErr

func (tx *Tx) CloseWithErr(err error)

CloseWithErr automatically commits or rolls back the transaction based on the error value. If err is nil, the transaction is committed; otherwise, it is rolled back. Commit/Rollback failures are reported via Hook.

func (*Tx) DriverName

func (tx *Tx) DriverName() string

DriverName returns the driverName used by the DB which began this transaction.

func (*Tx) Exec

func (tx *Tx) Exec(query string, args ...any) (sql.Result, error)

Exec executes a query within a transaction without returning any rows. Any placeholder parameters are replaced with supplied args. Overrides sql.Tx's Exec, delegating to ExecContext to integrate Hook logic.

func (*Tx) ExecContext

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

ExecContext executes a query without returning any rows. Overrides sql.Tx's ExecContext to integrate Hook logic. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Tx) ExecFunc

func (tx *Tx) ExecFunc(fn func(tx *Tx))

ExecFunc executes a function within a transaction, using a mutex to ensure concurrency safety. This is the only method in Tx that provides internal locking, suitable for scenarios requiring serialized transaction operations. All other Tx methods are unlocked; callers must ensure that the same Tx is not used concurrently across goroutines.

func (*Tx) Get

func (tx *Tx) Get(dest any, query string, args ...any) error

Get within a transaction. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*Tx) GetContext

func (tx *Tx) GetContext(ctx context.Context, dest any, query string, args ...any) error

GetContext within a transaction and context. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty.

func (*Tx) GetMapper

func (tx *Tx) GetMapper() *reflectx.Mapper

GetMapper returns the Mapper for this Tx.

func (*Tx) IsStrict

func (tx *Tx) IsStrict() bool

IsStrict returns whether strict mode is currently enabled.

func (*Tx) MustExec

func (tx *Tx) MustExec(query string, args ...any) sql.Result

MustExec runs MustExec within a transaction. Any placeholder parameters are replaced with supplied args.

func (*Tx) MustExecContext

func (tx *Tx) MustExecContext(ctx context.Context, query string, args ...any) sql.Result

MustExecContext runs MustExecContext within a transaction. Any placeholder parameters are replaced with supplied args.

func (*Tx) NamedExec

func (tx *Tx) NamedExec(query string, arg any) (sql.Result, error)

NamedExec a named query within a transaction. Any named placeholder parameters are replaced with fields from arg. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Tx) NamedExecContext

func (tx *Tx) NamedExecContext(ctx context.Context, query string, arg any) (sql.Result, error)

NamedExecContext using this Tx. Any named placeholder parameters are replaced with fields from arg.

Note: This method only does named-to-? conversion and forwarding; autoIn / Rebind / Hook are guaranteed by the downstream tx.ExecContext implementation (see ExecerContext interface contract).

func (*Tx) NamedGet

func (tx *Tx) NamedGet(dest any, query string, param any) error

NamedGet executes a query with named parameters and scans a single row result.

func (*Tx) NamedGetContext

func (tx *Tx) NamedGetContext(ctx context.Context, dest any, query string, param any) error

NamedGetContext executes a query with named parameters and scans a single row result.

func (*Tx) NamedQuery

func (tx *Tx) NamedQuery(query string, arg any) (*Rows, error)

NamedQuery within a transaction. Any named placeholder parameters are replaced with fields from arg.

func (*Tx) NamedQueryContext

func (tx *Tx) NamedQueryContext(ctx context.Context, query string, arg any) (*Rows, error)

NamedQueryContext within a transaction and context. Any named placeholder parameters are replaced with fields from arg.

func (*Tx) NamedSelect

func (tx *Tx) NamedSelect(dest any, query string, param any) error

NamedSelect executes a query with named parameters and scans the result set into a slice.

func (*Tx) NamedSelectContext

func (tx *Tx) NamedSelectContext(ctx context.Context, dest any, query string, param any) error

NamedSelectContext executes a query with named parameters and scans the result set into a slice.

func (*Tx) NamedStmt

func (tx *Tx) NamedStmt(stmt *NamedStmt) *NamedStmt

NamedStmt returns a version of the prepared statement which runs within a transaction.

func (*Tx) NamedStmtContext

func (tx *Tx) NamedStmtContext(ctx context.Context, stmt *NamedStmt) *NamedStmt

NamedStmtContext returns a version of the prepared statement which runs within a transaction.

func (*Tx) PrepareNamed

func (tx *Tx) PrepareNamed(query string) (*NamedStmt, error)

PrepareNamed returns an sqlex.NamedStmt

func (*Tx) PrepareNamedContext

func (tx *Tx) PrepareNamedContext(ctx context.Context, query string) (*NamedStmt, error)

PrepareNamedContext returns an sqlex.NamedStmt

func (*Tx) Preparex

func (tx *Tx) Preparex(query string) (*Stmt, error)

Preparex a statement within a transaction.

func (*Tx) PreparexContext

func (tx *Tx) PreparexContext(ctx context.Context, query string) (*Stmt, error)

PreparexContext returns an sqlex.Stmt instead of a sql.Stmt.

func (*Tx) QueryRowx

func (tx *Tx) QueryRowx(query string, args ...any) *Row

QueryRowx within a transaction. Any placeholder parameters are replaced with supplied args.

func (*Tx) QueryRowxContext

func (tx *Tx) QueryRowxContext(ctx context.Context, query string, args ...any) *Row

QueryRowxContext within a transaction and context. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Tx) Queryx

func (tx *Tx) Queryx(query string, args ...any) (*Rows, error)

Queryx within a transaction. Any placeholder parameters are replaced with supplied args.

func (*Tx) QueryxContext

func (tx *Tx) QueryxContext(ctx context.Context, query string, args ...any) (*Rows, error)

QueryxContext within a transaction and context. Any placeholder parameters are replaced with supplied args. Automatically detects slice args and expands IN clauses; zero overhead when no slices are present.

func (*Tx) Rebind

func (tx *Tx) Rebind(query string) string

Rebind a query within a transaction's bindvar type.

func (*Tx) Select

func (tx *Tx) Select(dest any, query string, args ...any) error

Select within a transaction. Any placeholder parameters are replaced with supplied args.

func (*Tx) SelectContext

func (tx *Tx) SelectContext(ctx context.Context, dest any, query string, args ...any) error

SelectContext within a transaction and context. Any placeholder parameters are replaced with supplied args.

func (*Tx) SetStrict

func (tx *Tx) SetStrict(strict bool)

SetStrict enables or disables strict mode. In strict mode (true), an error is returned if the query result contains columns that have no corresponding field in the target struct. In lenient mode (false), mismatched columns are silently ignored.

func (*Tx) Stmtx

func (tx *Tx) Stmtx(stmt any) *Stmt

Stmtx returns a version of the prepared statement which runs within a transaction. Provided stmt can be either *sql.Stmt or *sqlex.Stmt.

WARNING: Stmtx will panic if stmt is not a valid type. Use TryStmtx for a safe version.

func (*Tx) StmtxContext

func (tx *Tx) StmtxContext(ctx context.Context, stmt any) *Stmt

StmtxContext returns a version of the prepared statement which runs within a transaction. Provided stmt can be either *sql.Stmt or *sqlex.Stmt.

WARNING: StmtxContext will panic if stmt is not a valid type. Use TryStmtxContext for a safe version.

func (*Tx) TryStmtx

func (tx *Tx) TryStmtx(stmt any) (*Stmt, error)

TryStmtx is a safe version of Stmtx that returns an error instead of panicking.

func (*Tx) TryStmtxContext

func (tx *Tx) TryStmtxContext(ctx context.Context, stmt any) (*Stmt, error)

TryStmtxContext is a safe version of StmtxContext that returns an error instead of panicking.

Directories

Path Synopsis
Package reflectx implements extensions to the standard reflect lib suitable for implementing marshalling and unmarshalling packages.
Package reflectx implements extensions to the standard reflect lib suitable for implementing marshalling and unmarshalling packages.
Package types provides some useful types which implement the `sql.Scanner` and `driver.Valuer` interfaces, suitable for use as scan and value targets with database/sql.
Package types provides some useful types which implement the `sql.Scanner` and `driver.Valuer` interfaces, suitable for use as scan and value targets with database/sql.

Jump to

Keyboard shortcuts

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