Documentation
¶
Overview ¶
Package libdb provides an interface for interacting with a SQL database, currently with a specific implementation for PostgreSQL using lib/pq.
Key Features:
Abstraction: Defines interfaces (`DBManager`, `Exec`, `QueryRower`) to decouple application code from specific database driver details.
Simplified Transaction Management: The `DBManager.WithTransaction` method provides a clear pattern for handling database transactions, returning separate functions for committing (`CommitTx`) and releasing/rolling back (`ReleaseTx`). The `ReleaseTx` function is designed for use with `defer` to ensure transactions are always finalized and connections are released, even in cases of errors or panics.
Centralized Error Translation: Maps common low-level database errors (like sql.ErrNoRows or PostgreSQL-specific pq.Error codes) to a consistent set of exported package errors (e.g., ErrNotFound, ErrUniqueViolation, ErrDeadlockDetected). This simplifies error handling in application code.
Usage Example (Transaction):
func handleRequest(ctx context.Context, mgr libdb.DBManager) error { // Start transaction, get executor and commit/release functions exec, commit, release, err := mgr.WithTransaction(ctx) if err != nil { return fmt.Errorf("failed to start transaction: %w", err) } // Always defer release() to ensure cleanup (rollback on error/panic, no-op after commit) defer release() // --- Do work using exec --- _, err = exec.ExecContext(ctx, "UPDATE settings SET value = $1 WHERE key = $2", "new_value", "setting_key") if err != nil { // Error occurred - no need to call release explicitly, defer handles it. return fmt.Errorf("failed to update setting: %w", err) } // --- Success --- // Attempt to commit; if it fails, the deferred release() still runs. if err = commit(ctx); err != nil { return fmt.Errorf("transaction commit failed: %w", err) } // Commit successful. The deferred release() will run but do nothing (idempotent). return nil }
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotFound is returned by Scan when sql.ErrNoRows is encountered. ErrNotFound = errors.New("libdb: not found") // ErrTxFailed indicates a failure during transaction finalization (Commit or Rollback). ErrTxFailed = errors.New("libdb: transaction failed") // ErrMaxRowsReached indicates that the maximum number of rows on a given table has been reached. // This error should be thrown when attempting to create a new entry would lead to exceeding the maximum capacity. // It implies that enforcing a reasonable maximum row count is necessary to prevent operations like bulk update from failing. ErrMaxRowsReached = errors.New("max row count reached") // ErrUniqueViolation corresponds to unique key constraint errors (e.g., PostgreSQL code 23505). ErrUniqueViolation = errors.New("libdb: unique constraint violation") // ErrForeignKeyViolation corresponds to foreign key constraint errors (e.g., PostgreSQL code 23503). ErrForeignKeyViolation = errors.New("libdb: foreign key violation") // ErrNotNullViolation corresponds to not-null constraint errors (e.g., PostgreSQL code 23502). ErrNotNullViolation = errors.New("libdb: not null constraint violation") // ErrCheckViolation corresponds to check constraint errors (e.g., PostgreSQL code 23514). ErrCheckViolation = errors.New("libdb: check constraint violation") // ErrConstraintViolation is a generic error for constraint violations not specifically mapped. ErrConstraintViolation = errors.New("libdb: constraint violation") // ErrDeadlockDetected corresponds to deadlock errors (e.g., PostgreSQL code 40P01). ErrDeadlockDetected = errors.New("libdb: deadlock detected") // ErrSerializationFailure corresponds to serialization failures (e.g., PostgreSQL code 40001). ErrSerializationFailure = errors.New("libdb: serialization failure") // ErrLockNotAvailable corresponds to lock acquisition failures (e.g., PostgreSQL code 55P03). ErrLockNotAvailable = errors.New("libdb: lock not available") // ErrQueryCanceled corresponds to query cancellation (e.g., PostgreSQL code 57014 or context cancellation). ErrQueryCanceled = errors.New("libdb: query canceled") // ErrDataTruncation corresponds to data truncation errors (e.g., PostgreSQL code 22001). ErrDataTruncation = errors.New("libdb: data truncation error") // ErrNumericOutOfRange corresponds to numeric overflow errors (e.g., PostgreSQL code 22003). ErrNumericOutOfRange = errors.New("libdb: numeric value out of range") // ErrInvalidInputSyntax corresponds to syntax errors in data representation (e.g., PostgreSQL code 22P02). ErrInvalidInputSyntax = errors.New("libdb: invalid input syntax") // ErrUndefinedColumn corresponds to referencing an unknown column (e.g., PostgreSQL code 42703). ErrUndefinedColumn = errors.New("libdb: undefined column") // ErrUndefinedTable corresponds to referencing an unknown table (e.g., PostgreSQL code 42P01). ErrUndefinedTable = errors.New("libdb: undefined table") )
Predefined errors for common database interaction scenarios. Using these allows application code to check for specific conditions using errors.Is without relying on driver-specific error types or codes.
Functions ¶
func SetupLocalInstance ¶
Types ¶
type CommitTx ¶
CommitTx is a function type responsible for attempting to commit a transaction. It should typically only be called on the success path of a transactional operation. It may check the context before committing and returns ErrTxFailed or a translated database error if the commit fails.
type DBManager ¶
type DBManager interface { // WithoutTransaction returns an executor that operates directly on the underlying // database connection pool (i.e., outside of an explicit transaction). // Each operation may run on a different connection. WithoutTransaction() Exec // WithTransaction starts a new database transaction and returns an executor // bound to that transaction, a function to commit the transaction, and a function // to release (roll back) the transaction. // // The returned ReleaseTx function is designed to be deferred to ensure the transaction // is always finalized (rolled back on error/panic, or a no-op after successful commit) // and the connection is released. See package documentation for usage patterns. WithTransaction(ctx context.Context) (Exec, CommitTx, ReleaseTx, error) // Close terminates the underlying database connection pool. // It should be called when the application is shutting down. Close() error }
DBManager defines the interface for obtaining database executors and managing the database connection lifecycle. It serves as the main entry point for database interactions.
func NewPostgresDBManager ¶
NewPostgresDBManager creates a new DBManager for PostgreSQL. It opens a connection pool using the provided DSN, pings the database to verify connectivity, and optionally executes an initial schema setup query. Note: For production schema management, using dedicated migration tools is recommended over passing a simple schema string here.
type Exec ¶
type Exec interface { // ExecContext executes a query without returning any rows. // The args are for any placeholder parameters in the query. ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) // QueryContext executes a query that returns rows, typically a SELECT. // The args are for any placeholder parameters in the query. // Callers should check rows.Err() after iterating. QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) // QueryRowContext executes a query that is expected to return at most one row. // QueryRowContext always returns a non-nil value. Errors are deferred until // QueryRower's Scan method is called. QueryRowContext(ctx context.Context, query string, args ...any) QueryRower }
Exec defines the common interface for executing database operations, whether within a transaction or directly on the connection pool. Errors returned by methods implementing this interface should be translated into the package's predefined Err* variables where applicable.
type QueryRower ¶
type QueryRower interface { // Scan copies the columns from the matched row into the values pointed at by dest. // If no rows were found, it returns ErrNotFound. Other scan errors are translated. Scan(dest ...any) error }
QueryRower provides the Scan method, typically implemented by wrapping *sql.Row. Using this interface allows Scan errors (like sql.ErrNoRows) to be translated consistently by the library.
type ReleaseTx ¶
type ReleaseTx func() error
ReleaseTx is a function type responsible for rolling back a transaction, ensuring its resources are released. It is designed to be idempotent (safe to call multiple times or after a commit) and is ideal for use with `defer` to guarantee cleanup. It returns ErrTxFailed or a translated database error if the rollback fails (and the transaction wasn't already finalized).