Documentation
¶
Overview ¶
Package database provides cross-database query building utilities
Package database provides performance tracking for database operations
Index ¶
- Constants
- Variables
- func ConstraintName(err error) (string, bool)
- func ExecuteInsert(ctx context.Context, exec Executor, q SQLProvider, op string) error
- func ExecuteQueryMany(ctx context.Context, exec Executor, q SQLProvider, op string, ...) error
- func ExecuteQuerySingle(ctx context.Context, exec Executor, q SQLProvider, op string, dest ...any) error
- func ExecuteUpdate(ctx context.Context, exec Executor, q SQLProvider, op string) (int64, error)
- func ExecuteUpdateOne(ctx context.Context, exec Executor, q SQLProvider, op string) error
- func GetSupportedDatabaseTypes() []string
- func IsForeignKeyViolation(err error) bool
- func IsNotFound(err error) bool
- func IsUniqueViolation(err error) bool
- func ValidateDatabaseType(dbType string) error
- func WithTx(ctx context.Context, db Interface, fn func(ctx context.Context, tx Tx) error) error
- func WithTxOptions(ctx context.Context, db Interface, opts *sql.TxOptions, ...) error
- type Connector
- type DBConfigProvider
- type DbManager
- type DbManagerOptions
- type ExecError
- type ExecStage
- type Executor
- type Interface
- type QueryBuilder
- type QueryBuilderInterface
- type ReleaseFunc
- type SQLProvider
- type Statement
- type TrackedConnection
- type TrackedDB
- type TrackedStatement
- type TrackedStmt
- type TrackedTransaction
- type TrackedTx
- type TrackingContext
- type Tx
Constants ¶
const ( DefaultSlowQueryThreshold = tracking.DefaultSlowQueryThreshold DefaultMaxQueryLength = tracking.DefaultMaxQueryLength )
Re-export internal constants
const ( PostgreSQL = types.PostgreSQL Oracle = types.Oracle )
Re-export database vendor identifiers so existing callers using the database package continue to compile while the single source of truth lives in types.
Variables ¶
var ( NewTrackedDB = tracking.NewDB NewTrackedConnection = tracking.NewConnection TrackDBOperation = tracking.TrackDBOperation NewTrackingSettings = tracking.NewSettings RegisterConnectionPoolMetrics = tracking.RegisterConnectionPoolMetrics // SetObservabilityEnabled gates DB-operation OpenTelemetry span/metric emission. // Called once at app bootstrap from the resolved observability.enabled value so // that, when observability is disabled, the tracking layer builds no span/metric // attributes (honoring the no-op provider's zero-overhead contract). SetObservabilityEnabled = tracking.SetObservabilityEnabled // WithRepositoryMethod records the business-operation (repository) method name // on ctx so the tracking layer emits it as the `repository.method` attribute on // the db.client.operation.duration metric. Pass the resulting context to the // database call: // // ctx = database.WithRepositoryMethod(ctx, "GetCustomer") // rows, err := db.Query(ctx, query, args...) // // The method name must be a static, low-cardinality identifier. WithRepositoryMethod = tracking.WithRepositoryMethod // RepositoryMethodFromContext returns the repository method name stored on ctx // by WithRepositoryMethod, and whether one was set. RepositoryMethodFromContext = tracking.RepositoryMethodFromContext )
Re-export internal functions as public API
var ErrNoRows = fmt.Errorf("database: %w", sql.ErrNoRows)
ErrNoRows reports a zero-row outcome: a SELECT that matched nothing (ExecuteQuerySingle) or an UPDATE/DELETE expected to match exactly one row but matched none (ExecuteUpdateOne). It wraps sql.ErrNoRows so errors.Is(err, sql.ErrNoRows) and IsNotFound(err) also match.
Functions ¶
func ConstraintName ¶ added in v0.40.0
ConstraintName returns the violated constraint name when the driver exposes it. PostgreSQL populates this for constraint violations; Oracle does not, so ConstraintName returns ("", false) for Oracle errors.
func ExecuteInsert ¶ added in v0.55.0
ExecuteInsert runs an INSERT. It does not inspect RowsAffected: a successful INSERT is success, and vendor differences around LastInsertId/RETURNING are out of scope here.
func ExecuteQueryMany ¶ added in v0.55.0
func ExecuteQueryMany(ctx context.Context, exec Executor, q SQLProvider, op string, scan func(rows *sql.Rows) error) error
ExecuteQueryMany runs q and invokes scan once per row. Zero rows is not an error (the caller sees an empty result). A scan callback error aborts iteration and is wrapped at StageScan. A nil scan is rejected up front at StageBuild rather than left to panic once a row arrives: unlike a nil q or exec (which fail loudly at the call site, in the caller's own frame), a nil scan is data-dependent — it only panics once the query returns a row, so a zero-row result would let the mistake pass silently in dev and blow up later in prod.
func ExecuteQuerySingle ¶ added in v0.55.0
func ExecuteQuerySingle(ctx context.Context, exec Executor, q SQLProvider, op string, dest ...any) error
ExecuteQuerySingle runs q and scans the first row into dest. It returns an op-labeled ErrNoRows wrap when the query matches no rows, and *ExecError for infrastructure failures. Extra rows beyond the first are ignored (sql.Row parity): after a successful scan, the rows are closed explicitly (mirroring sql.Row.Scan) so a driver error that only surfaces at Close — a truncated result, a connection fault mid-statement — is not silently swallowed; that failure is reported at StageClose. op labels errors only — it does not feed metrics or tracing; use database.WithRepositoryMethod(ctx, ...) for attribution. Typical mapping in an app:
err := database.ExecuteQuerySingle(ctx, tx, q, "ownership", &row.ID, &row.Name)
switch {
case errors.Is(err, database.ErrNoRows):
return domain.ErrResourceNotFound // business 404
case err != nil:
return fmt.Errorf("load ownership: %w", err) // infra 500
}
func ExecuteUpdate ¶ added in v0.55.0
ExecuteUpdate runs an UPDATE/DELETE and returns the affected-row count. It does not interpret zero: a legitimately-zero write (e.g. an UPDATE guarded by a WHERE clause that may match nothing, such as an idempotent state transition, or a bulk statement with no matching rows) returns (0, nil). Use ExecuteUpdateOne when zero affected rows should surface as ErrNoRows.
func ExecuteUpdateOne ¶ added in v0.55.0
ExecuteUpdateOne runs an UPDATE/DELETE expected to match exactly one row and enforces that cardinality: zero rows affected returns an op-labeled ErrNoRows wrap, so "not found" surfaces uniformly with ExecuteQuerySingle; more than one row affected returns *ExecError at StageRowsAffected instead of silently reporting success, because a broader-than-intended WHERE predicate updating several rows is a data-integrity failure, not a "found it" outcome; exactly one row affected returns nil. Use ExecuteUpdate when a non-singular match is legitimate (bulk or idempotent writes), or when only the caller can tell "absent" from "already in the target state".
func GetSupportedDatabaseTypes ¶
func GetSupportedDatabaseTypes() []string
GetSupportedDatabaseTypes returns a list of supported database types
func IsForeignKeyViolation ¶ added in v0.40.0
IsForeignKeyViolation reports whether err is a foreign-key constraint violation (PostgreSQL SQLSTATE 23503, Oracle ORA-02291).
func IsNotFound ¶ added in v0.40.0
IsNotFound reports whether err is a no-rows result (sql.ErrNoRows). Scan paths produce it natively; on the write path, ExecuteUpdateOne surfaces it via ErrNoRows (which wraps sql.ErrNoRows) when an UPDATE/DELETE expected to match exactly one row matches none, so IsNotFound matches that too. ExecuteUpdate does not produce it — zero rows affected is (0, nil) there.
func IsUniqueViolation ¶ added in v0.40.0
IsUniqueViolation reports whether err is a unique or primary-key constraint violation (PostgreSQL SQLSTATE 23505, Oracle ORA-00001). It traverses the framework's error wrap chain via errors.As, so callers must wrap driver errors with %w (not %v) for it to work.
func ValidateDatabaseType ¶
ValidateDatabaseType reports an error when dbType is not among the supported database types.
func WithTx ¶ added in v0.40.0
WithTx runs fn inside a database transaction. It commits when fn returns nil, rolls back and returns fn's original error when fn returns an error, and rolls back then re-panics if fn panics. After a successful commit the deferred rollback is skipped (a committed flag guards it), so there is no post-commit rollback noise on the happy path.
fn must use the provided tx for all database work; using the outer db handle inside fn escapes the transaction.
func WithTxOptions ¶ added in v0.40.0
func WithTxOptions(ctx context.Context, db Interface, opts *sql.TxOptions, fn func(ctx context.Context, tx Tx) error) error
WithTxOptions behaves like WithTx but begins the transaction with the given options (isolation level, read-only mode) via BeginTx. A nil opts is equivalent to WithTx.
Types ¶
type DBConfigProvider ¶ added in v0.26.0
type DBConfigProvider interface {
// DBConfig returns the database configuration for the given key.
// For single-tenant apps, key will be "". For multi-tenant, key will be the tenant ID.
DBConfig(ctx context.Context, key string) (*config.DatabaseConfig, error)
}
DBConfigProvider provides per-key database configurations. This interface abstracts where tenant-specific database configs come from.
type DbManager ¶ added in v0.9.0
type DbManager struct {
// contains filtered or unexported fields
}
DbManager manages database connections by string keys. It provides lazy initialization, LRU eviction, and cleanup for database connections. The manager is key-agnostic - it doesn't know about tenants, just manages named connections.
It is a thin adapter over internal/resourcepool.Pool, which owns the ADR-032 lease/evict/close protocol (seed leases, LRU eviction, idle cleanup, and the closed-pool guard). The manager keeps only the database-specific config resolution.
func NewDbManager ¶ added in v0.9.0
func NewDbManager(resourceSource DBConfigProvider, log logger.Logger, opts DbManagerOptions, connector Connector) *DbManager
NewDbManager creates a new database manager
func (*DbManager) Get ¶ added in v0.9.0
Get returns a database connection for the given key plus a ReleaseFunc the caller must invoke when finished with it for the current unit of work (typically deferred). For single-tenant, use key "". For multi-tenant, use the tenant ID. Connections are created lazily and cached with LRU eviction; the lease prevents a connection that is evicted while in use from being closed under an active caller (the #606 race). Once Close has run, Get fails closed rather than resurrecting a connection (F22). On error the returned ReleaseFunc is nil — check err first.
func (*DbManager) StartCleanup ¶ added in v0.9.0
StartCleanup starts the background cleanup routine for idle connections. A non-positive interval substitutes the documented 5-minute default.
func (*DbManager) StopCleanup ¶ added in v0.9.0
func (m *DbManager) StopCleanup()
StopCleanup stops the background cleanup routine
type DbManagerOptions ¶ added in v0.9.0
type DbManagerOptions struct {
MaxSize int // Cached-connection cap; <=0 uses a default (not unlimited).
IdleTTL time.Duration // Idle-connection lifetime; <=0 uses a default (not disabled).
}
DbManagerOptions configures the DbManager
type ExecError ¶ added in v0.55.0
ExecError is an infrastructure failure from an Execute* helper: the SQL could not be built, executed, scanned, or iterated. Zero-row outcomes are NOT ExecErrors — they are returned as op-labeled wraps of ErrNoRows.
type ExecStage ¶ added in v0.55.0
type ExecStage string
ExecStage identifies where inside a helper an infrastructure failure occurred.
const ( StageBuild ExecStage = "build" StageExec ExecStage = "exec" StageScan ExecStage = "scan" StageIterate ExecStage = "iterate" StageClose ExecStage = "close" // StageRowsAffected covers two distinct failures at the same point in the // pipeline: the driver's RowsAffected() call itself erroring, or (in // ExecuteUpdateOne only) RowsAffected() succeeding with a count that isn't // the exactly-one cardinality the helper promises. StageRowsAffected ExecStage = "rows_affected" )
type Executor ¶ added in v0.55.0
type Executor interface {
Query(ctx context.Context, query string, args ...any) (*sql.Rows, error)
Exec(ctx context.Context, query string, args ...any) (sql.Result, error)
}
Executor is the minimal query/exec surface shared by a database connection (Interface) and a transaction (Tx), so the Execute* helpers run identically inside or outside a transaction. Executor is a closed 2-method seam: future capabilities (batch, prepared statements) arrive as separate interfaces, never as additional methods here — adding a method to a shipped exported interface is apidiff-INCOMPATIBLE.
type Interface ¶
Interface defines the common database operations supported by the framework. This type alias maintains backward compatibility while the actual interfaces are now defined in the database/types package to avoid import cycles.
func NewConnection ¶
NewConnection creates a tracked database connection based on the provided configuration.
It initializes a concrete driver connection for the configured database type, wraps it with performance/tracing tracking, and attaches server metadata (host, port and an OTel namespace) to the tracking wrapper when available.
Errors are returned if cfg is nil, cfg.Type is not supported (supported: "postgresql", "oracle"), or if the underlying driver initialization fails.
Every unrecognized type is an error here, including the empty string. Classifying a database as intentionally absent is a config-layer verdict, made by the resolver that knows WHICH database it is resolving (config.TenantStore.DBConfig) — the factory sees no key, so it cannot tell a deliberately database-free service from a half-provisioned tenant, where absence is never legitimate. See ADR-047.
type QueryBuilder ¶
type QueryBuilder struct {
*builder.QueryBuilder
}
QueryBuilder provides vendor-specific SQL query building. This is a compatibility wrapper around the internal implementation.
func NewQueryBuilder ¶
func NewQueryBuilder(vendor string) *QueryBuilder
NewQueryBuilder creates a new query builder for the specified database vendor. This function maintains backward compatibility while using the improved internal implementation.
func (*QueryBuilder) Delete ¶
func (qb *QueryBuilder) Delete(table string) types.DeleteQueryBuilder
Delete creates a DELETE query builder that returns the interface type. This method overrides the embedded builder to provide the correct interface.
func (*QueryBuilder) Filter ¶ added in v0.13.0
func (qb *QueryBuilder) Filter() types.FilterFactory
Filter returns a FilterFactory for creating composable WHERE clause filters. This method overrides the embedded builder to provide the correct interface.
func (*QueryBuilder) Select ¶
func (qb *QueryBuilder) Select(columns ...any) types.SelectQueryBuilder
Select creates a SELECT query builder that returns the interface type. This method overrides the embedded builder to provide the correct interface.
func (*QueryBuilder) Update ¶
func (qb *QueryBuilder) Update(table string) types.UpdateQueryBuilder
Update creates an UPDATE query builder that returns the interface type. This method overrides the embedded builder to provide the correct interface.
type QueryBuilderInterface ¶ added in v0.8.1
type QueryBuilderInterface = types.QueryBuilderInterface
QueryBuilderInterface defines the interface for vendor-specific SQL query building. This type alias maintains backward compatibility while enabling dependency injection.
type ReleaseFunc ¶ added in v0.43.0
type ReleaseFunc func()
ReleaseFunc releases a lease obtained from Get. Callers must invoke it (typically deferred) when they are finished with the connection for the current unit of work. It is idempotent: calling it more than once is a safe no-op. The connection itself is a long-lived, shared pool — Release does NOT close it; it only signals that this borrower is done, so a connection evicted while leased can be closed once its last lease is released. See ADR-032.
type SQLProvider ¶ added in v0.55.0
SQLProvider is the contract of a complete, executable statement: anything that can render itself to SQL + args. Every query builder returned by QueryBuilder (Select/Insert/Update/Delete) already satisfies it; Raw adapts hand-written SQL. A types.Filter/types.JoinFilter WHERE fragment also satisfies SQLProvider structurally (it embeds squirrel.Sqlizer, which declares the same ToSQL()-shaped ToSql()) but is NOT a complete statement and must never be passed to the Execute* helpers directly — buildQuery detects and rejects it at StageBuild instead of letting the driver fail on a bare WHERE fragment.
func Raw ¶ added in v0.55.0
func Raw(query string, args ...any) SQLProvider
Raw adapts hand-written SQL (UNION, FOR UPDATE, vendor tricks) to SQLProvider so it reuses the same Execute* helpers as builder output.
SECURITY: Raw is an escape hatch on par with Filter.Raw, and broader — the SQL string replaces the whole statement, bypassing the builder's identifier validation entirely. Never concatenate user input into the SQL; carry values in args. Every call site requires an adjacent "// SECURITY: Manual SQL review completed - <what was verified>" comment, exactly as f.Raw()/jf.Raw() do.
type Statement ¶ added in v0.8.0
Statement defines the interface for prepared statements. This type alias maintains backward compatibility.
type TrackedConnection ¶ added in v0.2.0
type TrackedConnection = tracking.Connection
Re-export the internal tracking implementation as the public API
type TrackedStatement ¶ added in v0.2.0
Re-export the internal tracking implementation as the public API
type TrackedStmt ¶
Re-export the internal tracking implementation as the public API
type TrackedTransaction ¶ added in v0.2.0
type TrackedTransaction = tracking.Transaction
Re-export the internal tracking implementation as the public API
type TrackedTx ¶
type TrackedTx = tracking.Transaction
Re-export the internal tracking implementation as the public API
type TrackingContext ¶ added in v0.5.0
Re-export the internal tracking implementation as the public API
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
builder
Package builder provides cross-database query building utilities.
|
Package builder provides cross-database query building utilities. |
|
dbtestlog
Package dbtestlog provides shared test-logger helpers for the database vendor packages.
|
Package dbtestlog provides shared test-logger helpers for the database vendor packages. |
|
tracking
Package tracking provides performance tracking for database operations.
|
Package tracking provides performance tracking for database operations. |
|
wrapper
Package wrapper provides driver-agnostic wrappers around database/sql.Stmt and database/sql.Tx that implement the types.Statement and types.Tx interfaces.
|
Package wrapper provides driver-agnostic wrappers around database/sql.Stmt and database/sql.Tx that implement the types.Statement and types.Tx interfaces. |
|
Package testing provides utilities for testing database logic in go-bricks applications.
|
Package testing provides utilities for testing database logic in go-bricks applications. |
|
Package types contains the core database interface definitions for go-bricks.
|
Package types contains the core database interface definitions for go-bricks. |