sequel

package module
v1.7.2 Latest Latest
Warning

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

Go to latest
Published: Jun 13, 2026 License: Apache-2.0 Imports: 21 Imported by: 1

README

Sequel

A Go library that enhances database/sql with cross-driver SQL, schema migration, ephemeral test databases, and adaptive connection pooling.

Features at a Glance

  • Connection pool management - Prevents database exhaustion when many consumers in one process share a DSN
  • Schema migration - Concurrency-safe, incremental database migrations
  • Cross-driver support - MySQL, PostgreSQL, CockroachDB, SQL Server, and SQLite with unified API
  • Ephemeral test databases - Isolated databases per test with automatic cleanup

Quick Start

import "github.com/microbus-io/sequel"

// Open a database connection with its own pool
db, err := sequel.Open("", "root:root@tcp(127.0.0.1:3306)/mydb")

// Run migrations
err = db.Migrate("myservice@v1", migrationFilesFS)

// Use db.DB for standard sql.DB operations
rows, err := db.Query("SELECT * FROM users WHERE tenant_id=?", tenantID)

Connection Pool Management

Sequel exposes two constructors so the connection-pool strategy is self-documenting at the call site:

  • Open(driver, dsn) returns a fresh *DB with its own pool. Each call returns a distinct instance; sequel does not coalesce by DSN and does not size the pool automatically. The standard database/sql defaults apply (unlimited open, 2 idle) until the caller adjusts them with SetMaxOpenConns / SetMaxIdleConns. Use this for a single heavy consumer (e.g. a long-running worker pool) where you want to size the pool to the workload.

  • OpenSingleton(driver, dsn) returns a coalesced *DB: multiple calls with the same (driver, dsn) share one *sql.DB and one connection pool. Sequel automatically sizes that pool based on the number of openers using a sqrt-based formula:

    • maxIdle ≈ sqrt(N) where N is the number of openers
    • maxOpen ≈ (sqrt(N) * 2) + 2

    This is the right choice when many parts of the same process each open the same DSN occasionally — the pool grows gently with the number of openers and no caller has to think about pool sizing.

// Single heavy consumer — caller manages the pool.
db, err := sequel.Open("", dsn)
db.SetMaxOpenConns(32)
db.SetMaxIdleConns(8)

// Multiple consumers sharing a DSN — sequel manages one pool across them.
db, err := sequel.OpenSingleton("", dsn)

Schema Migration

Sequel performs incremental schema migration using numbered SQL files (1.sql, 2.sql, etc.). Migrations are:

  • Concurrency-safe - Distributed locking ensures only one replica executes each migration
  • Tracked - A sequel_migrations table records completed migrations
  • Driver-aware - Use -- DRIVER: drivername comments for driver-specific SQL (list multiple, space-separated, to share a statement across drivers)
// Embed migration files
//go:embed sql/*.sql
var migrationFS embed.FS

// Run migrations (safe to call from multiple replicas)
err := db.Migrate("unique-sequence-name", migrationFS)

Example migration file with driver-specific syntax:

-- DRIVER: mysql
ALTER TABLE users MODIFY COLUMN email VARCHAR(384) NOT NULL;

-- DRIVER: pgx cockroachdb
ALTER TABLE users ALTER COLUMN email TYPE VARCHAR(384);

-- DRIVER: mssql
ALTER TABLE users ALTER COLUMN email NVARCHAR(384) NOT NULL;

-- DRIVER: sqlite
-- SQLite does not support ALTER COLUMN; a table rebuild would be needed

Cross-Driver Support

Sequel supports MySQL, PostgreSQL, CockroachDB, SQL Server, and SQLite through a unified API. Write your SQL once using MySQL-style ? placeholders and virtual functions, and Sequel automatically adapts queries for the active driver.

CockroachDB speaks the PostgreSQL wire protocol and shares the pgx driver, but it is exposed as a distinct driver name (cockroachdb) because callers may need to branch on Cockroach-specific behavior — retry semantics and async schema changes in particular. Internally, every PostgreSQL expansion (placeholders, virtual functions, DSN parsing) applies identically to cockroachdb.

Automatic Placeholder Conversion

All query methods (Exec, Query, QueryRow, Prepare, and their Context variants) automatically convert ? placeholders to the driver's native syntax. For PostgreSQL, ? becomes $1, $2, etc. For MySQL, SQL Server, and SQLite, ? is left as-is. Placeholders inside quoted strings are left untouched.

// Works on all drivers - placeholders are converted automatically
rows, err := db.Query("SELECT * FROM users WHERE tenant_id = ? AND active = ?", tenantID, true)
// PostgreSQL receives: SELECT * FROM users WHERE tenant_id = $1 AND active = $2
Virtual Functions

Virtual functions are driver-agnostic function calls in your SQL that Sequel expands into driver-specific expressions before execution. They are matched case-insensitively and support nesting. Quoted strings inside arguments are handled correctly.

Built-in Virtual Functions

NOW_UTC() returns the current UTC timestamp with millisecond precision.

Driver NOW_UTC() expands to
MySQL UTC_TIMESTAMP(3)
PostgreSQL (NOW() AT TIME ZONE 'UTC')
SQL Server SYSUTCDATETIME()
SQLite STRFTIME('%Y-%m-%d %H:%M:%f', 'now')

REGEXP_TEXT_SEARCH(expr IN col1, col2, ...) performs a case-insensitive regular expression search across one or more columns.

Driver REGEXP_TEXT_SEARCH(? IN name, email) expands to
MySQL CONCAT_WS(' ',name,email) REGEXP ?
PostgreSQL REGEXP_LIKE(CONCAT_WS(' ',name,email), ?, 'i')
SQL Server REGEXP_LIKE(CONCAT_WS(' ',name,email), ?, 'i')
SQLite CONCAT_WS(' ',name,email) LIKE '%' || ? || '%'

DATE_ADD_MILLIS(baseExpr, milliseconds) adds milliseconds to a timestamp expression.

Driver DATE_ADD_MILLIS(created_at, ?) expands to
MySQL DATE_ADD(created_at, INTERVAL (?) * 1000 MICROSECOND)
PostgreSQL created_at + MAKE_INTERVAL(secs => (?) / 1000.0)
SQL Server DATEADD(MILLISECOND, ?, created_at)
SQLite STRFTIME('%Y-%m-%d %H:%M:%f', created_at, '+' || ((?) / 1000.0) || ' seconds')

DATE_DIFF_MILLIS(a, b) returns the difference (a - b) in milliseconds.

Driver DATE_DIFF_MILLIS(updated_at, created_at) expands to
MySQL TIMESTAMPDIFF(MICROSECOND, created_at, updated_at) / 1000.0
PostgreSQL EXTRACT(EPOCH FROM (updated_at - created_at)) * 1000.0
SQL Server DATEDIFF_BIG(MILLISECOND, created_at, updated_at)
SQLite (JULIANDAY(updated_at) - JULIANDAY(created_at)) * 86400000.0

LIMIT_OFFSET(limit, offset) provides cross-driver pagination. Note that SQL Server requires an ORDER BY clause.

Driver LIMIT_OFFSET(10, 0) expands to
MySQL LIMIT 10 OFFSET 0
PostgreSQL LIMIT 10 OFFSET 0
SQL Server OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
SQLite LIMIT 10 OFFSET 0
db.Query("SELECT * FROM users ORDER BY id LIMIT_OFFSET(?, ?)", limit, offset)
Nesting

Virtual functions can be nested. Inner functions are expanded first across multiple passes:

db.Exec("UPDATE t SET expires_at = DATE_ADD_MILLIS(NOW_UTC(), ?) WHERE id = ?", ttlMs, id)
// MySQL:      UPDATE t SET expires_at = DATE_ADD(UTC_TIMESTAMP(3), INTERVAL (?) * 1000 MICROSECOND) WHERE id = ?
// PostgreSQL: UPDATE t SET expires_at = (NOW() AT TIME ZONE 'UTC') + MAKE_INTERVAL(secs => ($1) / 1000.0) WHERE id = $2
Custom Virtual Functions

Register your own virtual functions with RegisterVirtualFunc:

sequel.RegisterVirtualFunc("BOOL", func(driverName string, args string) (string, error) {
    switch driverName {
    case "mysql", "pgx", "sqlite":
        return args, nil
    case "mssql":
        // SQL Server uses BIT, not BOOL
        return "CAST(" + args + " AS BIT)", nil
    default:
        return "", errors.New("unsupported driver: %s", driverName)
    }
})
UnpackQuery

UnpackQuery is the public method that expands virtual functions and conforms placeholders. It is called automatically by the query shadow methods, but can be used directly if needed:

expanded, err := db.UnpackQuery("SELECT * FROM t WHERE updated_at > DATE_ADD_MILLIS(NOW_UTC(), ?) AND active = ?")
InsertReturnID

InsertReturnID executes an INSERT statement and returns the auto-generated ID for the named ID column. Each driver uses its native mechanism:

Driver Mechanism
MySQL LastInsertId() from the result
PostgreSQL Appends RETURNING <idColumn> to the query
SQL Server Injects OUTPUT INSERTED.<idColumn> before VALUES
SQLite LastInsertId() from the result
id, err := db.InsertReturnID(ctx, "id", "INSERT INTO users (name, email) VALUES (?, ?)", name, email)
DriverName()

DriverName() returns the active driver name ("mysql", "pgx", "mssql", or "sqlite") for cases where you need driver-specific logic in Go code.

Ephemeral Test Databases

Provisioning a per-test database is a separate step from opening a connection. CreateTestingDatabase(driver, baseDSN, uniqueTestID) creates (or reuses) a uniquely-named database and returns its DSN; pass that DSN to Open or OpenSingleton to connect.

// Test fixture
func TestUserService(t *testing.T) {
    dsn, err := sequel.CreateTestingDatabase("", "root:root@tcp(127.0.0.1:3306)/mydb", t.Name())
    if err != nil { t.Fatal(err) }
    db, err := sequel.OpenSingleton("", dsn)
    if err != nil { t.Fatal(err) }
    defer db.Close()  // also drops the testing database
}

The same helper can be invoked from production startup paths that want to swap in a per-test database without rewriting the rest of the wiring:

func startup(cfg Config) (*sequel.DB, error) {
    dsn := cfg.DSN
    if cfg.Testing {
        var err error
        dsn, err = sequel.CreateTestingDatabase("", cfg.DSN, cfg.TestID)
        if err != nil { return nil, err }
    }
    return sequel.OpenSingleton("", dsn)
}

Repeated calls within the same process with the same (driver, baseDSN, uniqueTestID) reuse the same testing database — the DROP+CREATE runs only once. The returned DSN points at a database whose name has the testing_NN_ prefix; sequel inspects this on Close and drops the database automatically when the last referencing *DB is closed. There is no separate cleanup call to remember. If a process exits before Close runs, the leftover-cleanup sweep on the next CreateTestingDatabase call removes stale databases older than 1–2 hours.

Sequel is the copyrighted work of various contributors. It is licensed to you free of charge by Microbus LLC - a Delaware limited liability company formed to hold rights to the combined intellectual property of all contributors - under the Apache License 2.0.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyBindings

func ApplyBindings(args ...any) (err error)

ApplyBindings should be called after scanning values from the result set to perform all late binding.

func CreateTestingDatabase added in v1.6.1

func CreateTestingDatabase(driverName string, baseDataSourceName string, uniqueTestID string) (dsn string, err error)

CreateTestingDatabase provisions a uniquely-named database (or returns a SQLite in-memory DSN) for testing and returns the resolved data source name. Pass the result to Open or OpenSingleton to open a connection.

The returned DSN points at a database whose name has the testing_NN_ prefix. When the last *DB referencing that database is Closed, sequel drops it automatically — no separate cleanup call is required.

uniqueTestID scopes the database so that independent tests don't collide. Pass t.Name() from a test, or an equivalent identifier from production startup code that wants a per-run database:

dsn := cfg.DSN
if cfg.Testing {
    dsn, err = sequel.CreateTestingDatabase("", cfg.DSN, cfg.TestID)
    if err != nil { return err }
}
db, err := sequel.OpenSingleton("", dsn)

Within a single process, repeated calls with the same (driverName, baseDataSourceName, uniqueTestID) reuse the same testing database — the underlying DROP+CREATE only happens on the first call.

If a driver name is not provided, it is inferred from the data source name on a best-effort basis. Drivers currently supported: "mysql" (MySQL), "pgx" (Postgres), "cockroachdb" (CockroachDB), "mssql" (SQL Server) or "sqlite" (SQLite).

If a base data source name is not provided, the following localhost defaults are used based on the driver name:

  • (empty): SQLite in-memory database
  • sqlite: SQLite in-memory database
  • mysql: root:root@tcp(127.0.0.1:3306)/
  • pgx: postgres://postgres:postgres@127.0.0.1:5432/
  • cockroachdb: postgres://root@127.0.0.1:26257/?sslmode=disable
  • mssql: sqlserver://sa:Password123@127.0.0.1:1433

func IsLockContentionError added in v1.5.7

func IsLockContentionError(err error) bool

IsLockContentionError returns true if the error indicates database lock contention or a deadlock. Such errors are transient and the operation can typically be retried. Recognizes lock errors from SQLite, MySQL, PostgreSQL, SQL Server, and CockroachDB.

func Nullify

func Nullify[T comparable](value T) any

Nullify returns nil if the value equals to the zero value of its Go data type, else it returns the value. Use this construct to convert zero values to nil when writing to a nullable database column.

Example:

db.Exec(
	"INSERT INTO my_table (id, desc, modified_time) VALUES (?,?,?)",
	obj.ID,
	sequel.Nullify(obj.Description),
	sequel.Nullify(obj.ModifiedTime),
)

func RegisterVirtualFunc added in v1.2.0

func RegisterVirtualFunc(name string, handler func(driverName string, args string) (string, error))

RegisterVirtualFunc registers a virtual SQL function that will be replaced in queries before execution. The name is matched case-insensitively, e.g. registering "NOW_UTC" matches NOW_UTC(), now_utc(), Now_Utc(), etc. The handler receives the driver name and the string found between the parentheses, and returns the replacement SQL expression, or an error.

Types

type Binder

type Binder[T any] struct {
	sql.Null[T]
	// contains filtered or unexported fields
}

Binder is a thin wrapper over sql.Null that allows for late-binding of its value.

func Bind

func Bind[T any](binder func(value T) (err error)) *Binder[T]

Bind applies a binding function to the scanned value.

Example:

var obj Object
args := []any{
	&obj.ID,
	sequel.Bind(func(tags string) {
		return json.Unmarshal([]byte(tags), &obj.Tags)
	}),
	sequel.Bind(func(modifiedTime time.Time) {
		obj.Year, obj.Month, obj.Day = modifiedTime.Date()
		return nil
	}),
}
db.QueryRow("SELECT id, tags, modified_time FROM my_table WHERE id=?", id).Scan(args...)
sequel.ApplyBindings(args...)

func (*Binder[T]) Apply

func (n *Binder[T]) Apply() (err error)

Apply should be called after scanning the columns from the result set.

type DB

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

DB is an enhanced database connection that

  • Limits the size of the connection pool to each server to approx the sqrt of the number of clients
  • Performs schema migration
  • Automatically creates and connects to a localhost database while testing

func Open

func Open(driverName string, dataSourceName string) (db *DB, err error)

Open returns a database connection to the named data source with a dedicated connection pool. Each call returns a distinct *DB; sequel does not coalesce by DSN. The caller is responsible for sizing the pool via SetMaxOpenConns / SetMaxIdleConns if the database/sql defaults (unlimited open, 2 idle) don't fit.

Use OpenSingleton when multiple consumers in the same process share a DSN and you want sequel to manage one pool across all of them.

If a driver name is not provided, it is inferred from the data source name on a best-effort basis. Drivers currently supported: "mysql" (MySQL), "pgx" (Postgres), "cockroachdb" (CockroachDB), "mssql" (SQL Server) or "sqlite" (SQLite).

Example data source name for each of the supported drivers:

  • mysql: username:password@tcp(hostname:3306)/
  • pgx: postgres://username:password@hostname:5432/
  • cockroachdb: postgres://username:password@hostname:26257/
  • mssql: sqlserver://username:password@hostname:1433
  • sqlite: file:path/to/database.sqlite

func OpenSingleton added in v1.6.1

func OpenSingleton(driverName string, dataSourceName string) (db *DB, err error)

OpenSingleton returns a per-DSN coalesced *DB whose connection pool sequel manages automatically based on the number of openers (sqrt-based growth, see [DB.adjustConnectionLimits]). Multiple OpenSingleton calls with the same (driverName, dataSourceName) return the same *DB and share its connection pool. This is the right choice when many parts of the same process each access the database occasionally.

Use Open when you want a dedicated pool with explicit caller-managed sizing.

Driver inference, DSN defaults, and supported drivers are the same as Open.

func (*DB) Begin added in v1.4.0

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

Begin starts a transaction and returns a sequel.Tx that applies virtual function expansion and placeholder conforming.

func (*DB) BeginTx added in v1.4.0

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

BeginTx starts a transaction with the given options and returns a sequel.Tx that applies virtual function expansion and placeholder conforming.

func (*DB) Close

func (db *DB) Close() (err error)

Close closes the database connection.

When the last reference closes and the underlying database name matches the testing pattern (testing_NN_…), sequel drops the database from the server as a best-effort cleanup. This makes CreateTestingDatabase-provisioned databases self-cleaning on test teardown.

func (*DB) ConformArgPlaceholders deprecated

func (db *DB) ConformArgPlaceholders(stmt string) string

Deprecated: ConformArgPlaceholders is applied automatically by the query shadow methods. Use ? placeholders directly in queries passed to Exec, Query, QueryRow, and Prepare.

func (*DB) DriverName

func (db *DB) DriverName() string

DriverName is the name of the driver: "mysql", "pgx", "cockroachdb", "mssql" or "sqlite".

func (*DB) Exec added in v1.2.0

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

Exec shadows sql.DB.Exec and conforms arg placeholders for the driver.

func (*DB) ExecContext added in v1.2.0

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

ExecContext shadows sql.DB.ExecContext and conforms arg placeholders for the driver.

func (*DB) InsertReturnID added in v1.3.0

func (db *DB) InsertReturnID(ctx context.Context, idColumn string, stmt string, args ...any) (int64, error)

InsertReturnID executes an INSERT statement and returns the auto-generated ID for the named ID column.

func (*DB) Migrate

func (db *DB) Migrate(sequenceName string, fileSys fs.FS) (err error)

Migrate reads all #.sql files from the FS, and executes any new migrations in order of their file name. The order of execution is guaranteed only within the context of a sequence name.

func (*DB) NowUTC deprecated

func (db *DB) NowUTC() string

Deprecated: Use the NOW_UTC() virtual function directly in queries instead.

func (*DB) Prepare added in v1.2.0

func (db *DB) Prepare(query string) (*sql.Stmt, error)

Prepare shadows sql.DB.Prepare and conforms arg placeholders for the driver.

func (*DB) PrepareContext added in v1.2.0

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

PrepareContext shadows sql.DB.PrepareContext and conforms arg placeholders for the driver.

func (*DB) Query added in v1.2.0

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

Query shadows sql.DB.Query and conforms arg placeholders for the driver.

func (*DB) QueryContext added in v1.2.0

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

QueryContext shadows sql.DB.QueryContext and conforms arg placeholders for the driver.

func (*DB) QueryRow added in v1.2.0

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

QueryRow shadows sql.DB.QueryRow and conforms arg placeholders for the driver.

func (*DB) QueryRowContext added in v1.2.0

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

QueryRowContext shadows sql.DB.QueryRowContext and conforms arg placeholders for the driver.

func (*DB) RegexpTextSearch deprecated

func (db *DB) RegexpTextSearch(searchableColumns ...string) string

Deprecated: Use the REGEXP_TEXT_SEARCH() virtual function directly in queries instead.

func (*DB) UnpackQuery added in v1.2.0

func (db *DB) UnpackQuery(query string) (string, error)

UnpackQuery expands virtual functions (e.g. NOW_UTC(), REGEXP_TEXT_SEARCH()) into driver-specific SQL expressions, and conforms arg placeholders to the syntax expected by the driver (e.g. ? to $1, $2 for PostgreSQL).

type Executor added in v1.4.1

type Executor interface {
	Exec(query string, args ...any) (sql.Result, error)
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	Query(query string, args ...any) (*sql.Rows, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRow(query string, args ...any) *sql.Row
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
	Prepare(query string) (*sql.Stmt, error)
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
	InsertReturnID(ctx context.Context, idColumn string, stmt string, args ...any) (int64, error)
	DriverName() string
	UnpackQuery(query string) (string, error)
}

Executor is the interface satisfied by both DB and Tx.

type Null

type Null[T any] struct {
	*Binder[T]
}

Null is a thin wrapper over sql.Null that allows for reading NULL values.

func Nullable

func Nullable[T any](ptr *T) *Null[T]

Nullable is a simple binder that interprets NULL values to be the zero value of their Go data type.

Example:

var obj Object
args := []any{
	&obj.ID,
	sequel.Nullable(&obj.Description),
	sequel.Nullable(&obj.ModifiedTime),
}
db.QueryRow("SELECT id, desc, modified_time FROM my_table WHERE id=?", id).Scan(args...)
sequel.ApplyBindings(args...)

type Tx added in v1.4.0

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

Tx is an in-progress database transaction that shadows sql.Tx methods to apply virtual function expansion and placeholder conforming.

func (*Tx) DriverName added in v1.4.0

func (tx *Tx) DriverName() string

DriverName is the name of the driver: "mysql", "pgx", "cockroachdb", "mssql" or "sqlite".

func (*Tx) Exec added in v1.4.0

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

Exec shadows sql.Tx.Exec and conforms arg placeholders for the driver.

func (*Tx) ExecContext added in v1.4.0

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

ExecContext shadows sql.Tx.ExecContext and conforms arg placeholders for the driver.

func (*Tx) InsertReturnID added in v1.4.0

func (tx *Tx) InsertReturnID(ctx context.Context, idColumn string, stmt string, args ...any) (int64, error)

InsertReturnID executes an INSERT statement and returns the auto-generated ID for the named ID column.

func (*Tx) Prepare added in v1.4.0

func (tx *Tx) Prepare(query string) (*sql.Stmt, error)

Prepare shadows sql.Tx.Prepare and conforms arg placeholders for the driver.

func (*Tx) PrepareContext added in v1.4.0

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

PrepareContext shadows sql.Tx.PrepareContext and conforms arg placeholders for the driver.

func (*Tx) Query added in v1.4.0

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

Query shadows sql.Tx.Query and conforms arg placeholders for the driver.

func (*Tx) QueryContext added in v1.4.0

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

QueryContext shadows sql.Tx.QueryContext and conforms arg placeholders for the driver.

func (*Tx) QueryRow added in v1.4.0

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

QueryRow shadows sql.Tx.QueryRow and conforms arg placeholders for the driver.

func (*Tx) QueryRowContext added in v1.4.0

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

QueryRowContext shadows sql.Tx.QueryRowContext and conforms arg placeholders for the driver.

func (*Tx) UnpackQuery added in v1.4.0

func (tx *Tx) UnpackQuery(query string) (string, error)

UnpackQuery expands virtual functions (e.g. NOW_UTC(), REGEXP_TEXT_SEARCH()) into driver-specific SQL expressions, and conforms arg placeholders to the syntax expected by the driver (e.g. ? to $1, $2 for PostgreSQL).

type UnsafeSQL

type UnsafeSQL string

UnsafeSQL wraps a string to indicate not to use an argument placeholder when inserting it into a SQL statement. It should be used to insert values such as NOW() or calculation of other fields. Use with caution to avoid SQL injection.

Jump to

Keyboard shortcuts

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