Documentation
¶
Overview ¶
Package postgres provides functionality for interacting with PostgreSQL databases.
The postgres package offers a robust interface for working with PostgreSQL databases, built on top of the standard database/sql package. It includes connection management, query execution, transaction handling, and migration tools.
Core Features:
- Connection pooling and management
- Parameterized query execution
- Transaction support with automatic rollback on errors
- Schema migration tools
- Row scanning utilities
- Basic CRUD operations
Basic Usage:
import (
"github.com/Aleph-Alpha/std/v1/postgres"
"github.com/Aleph-Alpha/std/v1/logger"
)
// Create a logger
log, _ := logger.NewLogger(logger.Config{Level: "info"})
// Create a new database connection
db, err := postgres.New(postgres.Config{
Host: "localhost",
Port: 5432,
Username: "postgres",
Password: "password",
Database: "mydb",
}, log)
if err != nil {
log.Fatal("Failed to connect to database", err, nil)
}
defer db.Close()
// Execute a query
rows, err := db.Query(context.Background(), "SELECT id, name FROM users WHERE age > $1", 18)
if err != nil {
log.Error("Query failed", err, nil)
}
defer rows.Close()
// Scan the results
var users []User
for rows.Next() {
var user User
if err := rows.Scan(&user.ID, &user.Name); err != nil {
log.Error("Scan failed", err, nil)
}
users = append(users, user)
}
Transaction Example:
err = db.WithTransaction(context.Background(), func(tx postgres.Transaction) error {
// Execute multiple queries in a transaction
_, err := tx.Exec("UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID)
if err != nil {
return err // Transaction will be rolled back
}
_, err = tx.Exec("UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID)
if err != nil {
return err // Transaction will be rolled back
}
return nil // Transaction will be committed
})
Basic Operations:
// Create a record id, err := db.Insert(ctx, "INSERT INTO users(name, email) VALUES($1, $2) RETURNING id", "John", "john@example.com") // Check if a record exists exists, err := db.Exists(ctx, "SELECT 1 FROM users WHERE email = $1", "john@example.com") // Get a single record var user User err = db.Get(ctx, &user, "SELECT id, name, email FROM users WHERE id = $1", id)
FX Module Integration:
This package provides an fx module for easy integration:
app := fx.New( logger.Module, postgres.Module, // ... other modules ) app.Run()
Error Handling:
All methods in this package return GORM errors directly. This design provides:
- Consistency: All methods behave the same way
- Flexibility: Consumers can use errors.Is() with GORM error types
- Performance: No translation overhead unless needed
- Transparency: Preserves the full error chain from GORM
Basic error handling with GORM errors:
var user User
err := db.First(ctx, &user, "email = ?", "user@example.com")
if errors.Is(err, gorm.ErrRecordNotFound) {
// Handle not found
}
err = db.Query(ctx).Where("email = ?", "user@example.com").First(&user)
if errors.Is(err, gorm.ErrRecordNotFound) {
// Handle not found
}
For standardized error types, use TranslateError():
err := db.First(ctx, &user, conditions)
if err != nil {
err = db.TranslateError(err)
if errors.Is(err, postgres.ErrRecordNotFound) {
// Handle not found with standardized error
}
}
Recommended pattern - create a helper function for common error checks:
func isRecordNotFound(err error) bool {
return errors.Is(err, gorm.ErrRecordNotFound)
}
// Use consistently throughout your codebase
if isRecordNotFound(err) {
// Handle not found
}
Performance Considerations:
- Connection pooling is automatically handled to optimize performance
- Prepared statements are used internally to reduce parsing overhead
- Consider using batch operations for multiple insertions
- Query timeouts are recommended for all database operations
Thread Safety:
All methods on the DB interface are safe for concurrent use by multiple goroutines.
Index ¶
- Variables
- func RegisterPostgresLifecycle(params PostgresLifeCycleParams)
- type Config
- type Connection
- type ConnectionDetails
- type ErrorCategory
- type Migration
- type MigrationDirection
- type MigrationHistoryRecord
- type MigrationType
- type Postgres
- func (p *Postgres) AutoMigrate(models ...interface{}) error
- func (p *Postgres) Count(ctx context.Context, model interface{}, count *int64, ...) error
- func (p *Postgres) Create(ctx context.Context, value interface{}) error
- func (p *Postgres) CreateMigration(migrationsDir, name string, migrationType MigrationType) (string, error)
- func (p *Postgres) DB() *gorm.DB
- func (p *Postgres) Delete(ctx context.Context, value interface{}, conditions ...interface{}) (int64, error)
- func (p *Postgres) Exec(ctx context.Context, sql string, values ...interface{}) (int64, error)
- func (p *Postgres) Find(ctx context.Context, dest interface{}, conditions ...interface{}) error
- func (p *Postgres) First(ctx context.Context, dest interface{}, conditions ...interface{}) error
- func (p *Postgres) GetErrorCategory(err error) ErrorCategory
- func (p *Postgres) GetMigrationStatus(ctx context.Context, migrationsDir string) ([]map[string]interface{}, error)
- func (p *Postgres) GracefulShutdown() error
- func (p *Postgres) IsCritical(err error) bool
- func (p *Postgres) IsRetryable(err error) bool
- func (p *Postgres) IsTemporary(err error) bool
- func (p *Postgres) MigrateDown(ctx context.Context, migrationsDir string) error
- func (p *Postgres) MigrateUp(ctx context.Context, migrationsDir string) error
- func (p *Postgres) MonitorConnection(ctx context.Context)
- func (p *Postgres) Query(ctx context.Context) *QueryBuilder
- func (p *Postgres) RetryConnection(ctx context.Context)
- func (p *Postgres) Save(ctx context.Context, value interface{}) error
- func (p *Postgres) Transaction(ctx context.Context, fn func(pg *Postgres) error) error
- func (p *Postgres) TranslateError(err error) error
- func (p *Postgres) Update(ctx context.Context, model interface{}, attrs interface{}) (int64, error)
- func (p *Postgres) UpdateColumn(ctx context.Context, model interface{}, columnName string, value interface{}) (int64, error)
- func (p *Postgres) UpdateColumns(ctx context.Context, model interface{}, columnValues map[string]interface{}) (int64, error)
- func (p *Postgres) UpdateWhere(ctx context.Context, model interface{}, attrs interface{}, condition string, ...) (int64, error)
- type PostgresLifeCycleParams
- type PostgresParams
- type QueryBuilder
- func (qb *QueryBuilder) Clauses(conds ...clause.Expression) *QueryBuilder
- func (qb *QueryBuilder) Count(count *int64) error
- func (qb *QueryBuilder) Create(value interface{}) (int64, error)
- func (qb *QueryBuilder) CreateInBatches(value interface{}, batchSize int) (int64, error)
- func (qb *QueryBuilder) Delete(value interface{}) (int64, error)
- func (qb *QueryBuilder) Distinct(args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Done()
- func (qb *QueryBuilder) Find(dest interface{}) error
- func (qb *QueryBuilder) First(dest interface{}) error
- func (qb *QueryBuilder) FirstOrCreate(dest interface{}, conds ...interface{}) error
- func (qb *QueryBuilder) FirstOrInit(dest interface{}, conds ...interface{}) error
- func (qb *QueryBuilder) ForKeyShare() *QueryBuilder
- func (qb *QueryBuilder) ForNoKeyUpdate() *QueryBuilder
- func (qb *QueryBuilder) ForShare() *QueryBuilder
- func (qb *QueryBuilder) ForShareSkipLocked() *QueryBuilder
- func (qb *QueryBuilder) ForUpdate() *QueryBuilder
- func (qb *QueryBuilder) ForUpdateNoWait() *QueryBuilder
- func (qb *QueryBuilder) ForUpdateSkipLocked() *QueryBuilder
- func (qb *QueryBuilder) Group(query string) *QueryBuilder
- func (qb *QueryBuilder) Having(query interface{}, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Joins(query string, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Last(dest interface{}) error
- func (qb *QueryBuilder) LeftJoin(query string, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Limit(limit int) *QueryBuilder
- func (qb *QueryBuilder) MapRows(destSlice interface{}, mapFn func(*gorm.DB) error) error
- func (qb *QueryBuilder) Model(value interface{}) *QueryBuilder
- func (qb *QueryBuilder) Not(query interface{}, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Offset(offset int) *QueryBuilder
- func (qb *QueryBuilder) OnConflict(onConflict clause.OnConflict) *QueryBuilder
- func (qb *QueryBuilder) Or(query interface{}, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Order(value interface{}) *QueryBuilder
- func (qb *QueryBuilder) Pluck(column string, dest interface{}) (int64, error)
- func (qb *QueryBuilder) Preload(query string, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) QueryRow() RowScanner
- func (qb *QueryBuilder) QueryRows() (RowsScanner, error)
- func (qb *QueryBuilder) Raw(sql string, values ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Returning(columns ...string) *QueryBuilder
- func (qb *QueryBuilder) RightJoin(query string, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Scan(dest interface{}) error
- func (qb *QueryBuilder) ScanRow(dest interface{}) error
- func (qb *QueryBuilder) Scopes(funcs ...func(*gorm.DB) *gorm.DB) *QueryBuilder
- func (qb *QueryBuilder) Select(query interface{}, args ...interface{}) *QueryBuilder
- func (qb *QueryBuilder) Table(name string) *QueryBuilder
- func (qb *QueryBuilder) ToSubquery() *gorm.DB
- func (qb *QueryBuilder) Unscoped() *QueryBuilder
- func (qb *QueryBuilder) Updates(values interface{}) (int64, error)
- func (qb *QueryBuilder) Where(query interface{}, args ...interface{}) *QueryBuilder
- type RowScanner
- type RowsScanner
Constants ¶
This section is empty.
Variables ¶
var ( // ErrRecordNotFound is returned when a query doesn't find any matching records ErrRecordNotFound = errors.New("record not found") // ErrDuplicateKey is returned when an insert or update violates a unique constraint ErrDuplicateKey = errors.New("duplicate key violation") // ErrForeignKey is returned when an operation violates a foreign key constraint ErrForeignKey = errors.New("foreign key violation") // ErrInvalidData is returned when the data being saved doesn't meet validation rules ErrInvalidData = errors.New("invalid data") // ErrConnectionFailed is returned when database connection cannot be established ErrConnectionFailed = errors.New("database connection failed") // ErrTransactionFailed is returned when a transaction fails to commit or rollback ErrTransactionFailed = errors.New("transaction failed") // ErrQueryTimeout is returned when a query exceeds the allowed timeout ErrQueryTimeout = errors.New("query timeout exceeded") // ErrInvalidQuery is returned when the SQL query is malformed or invalid ErrInvalidQuery = errors.New("invalid query") // ErrPermissionDenied is returned when the user lacks necessary permissions ErrPermissionDenied = errors.New("permission denied") // ErrTableNotFound is returned when trying to access a non-existent table ErrTableNotFound = errors.New("table not found") // ErrColumnNotFound is returned when trying to access a non-existent column ErrColumnNotFound = errors.New("column not found") // ErrConstraintViolation is returned for general constraint violations ErrConstraintViolation = errors.New("constraint violation") // ErrCheckConstraintViolation is returned when a check constraint is violated ErrCheckConstraintViolation = errors.New("check constraint violation") // ErrNotNullViolation is returned when trying to insert null into a not-null column ErrNotNullViolation = errors.New("not null constraint violation") // ErrDataTooLong is returned when data exceeds column length limits ErrDataTooLong = errors.New("data too long for column") // ErrDeadlock is returned when a deadlock is detected during transaction ErrDeadlock = errors.New("deadlock detected") // ErrLockTimeout is returned when unable to acquire lock within timeout ErrLockTimeout = errors.New("lock acquisition timeout") // ErrInvalidDataType is returned when data type conversion fails ErrInvalidDataType = errors.New("invalid data type") // ErrDivisionByZero is returned for division by zero operations ErrDivisionByZero = errors.New("division by zero") // ErrNumericOverflow is returned when numeric operation causes overflow ErrNumericOverflow = errors.New("numeric value overflow") // ErrDiskFull is returned when database storage is full ErrDiskFull = errors.New("disk full") // ErrTooManyConnections is returned when connection pool is exhausted ErrTooManyConnections = errors.New("too many connections") // ErrInvalidJSON is returned when JSON data is malformed ErrInvalidJSON = errors.New("invalid JSON data") // ErrIndexCorruption is returned when database index is corrupted ErrIndexCorruption = errors.New("index corruption detected") // ErrConfigurationError is returned for database configuration issues ErrConfigurationError = errors.New("database configuration error") // ErrUnsupportedOperation is returned for operations not supported by the database ErrUnsupportedOperation = errors.New("unsupported operation") // ErrMigrationFailed is returned when database migration fails ErrMigrationFailed = errors.New("migration failed") // ErrBackupFailed is returned when database backup operation fails ErrBackupFailed = errors.New("backup operation failed") // ErrRestoreFailed is returned when database restore operation fails ErrRestoreFailed = errors.New("restore operation failed") // ErrSchemaValidation is returned when schema validation fails ErrSchemaValidation = errors.New("schema validation failed") // ErrSerializationFailure is returned when transaction serialization fails ErrSerializationFailure = errors.New("serialization failure") // ErrInsufficientPrivileges is returned when user lacks required privileges ErrInsufficientPrivileges = errors.New("insufficient privileges") // ErrInvalidPassword is returned for authentication failures ErrInvalidPassword = errors.New("invalid password") // ErrAccountLocked is returned when user account is locked ErrAccountLocked = errors.New("account locked") // ErrDatabaseNotFound is returned when specified database doesn't exist ErrDatabaseNotFound = errors.New("database not found") // ErrSchemaNotFound is returned when specified schema doesn't exist ErrSchemaNotFound = errors.New("schema not found") // ErrFunctionNotFound is returned when specified function doesn't exist ErrFunctionNotFound = errors.New("function not found") // ErrTriggerNotFound is returned when specified trigger doesn't exist ErrTriggerNotFound = errors.New("trigger not found") // ErrIndexNotFound is returned when specified index doesn't exist ErrIndexNotFound = errors.New("index not found") // ErrViewNotFound is returned when specified view doesn't exist ErrViewNotFound = errors.New("view not found") // ErrSequenceNotFound is returned when specified sequence doesn't exist ErrSequenceNotFound = errors.New("sequence not found") // ErrInvalidCursor is returned when cursor operation fails ErrInvalidCursor = errors.New("invalid cursor") // ErrCursorNotFound is returned when specified cursor doesn't exist ErrCursorNotFound = errors.New("cursor not found") // ErrStatementTimeout is returned when statement execution exceeds timeout ErrStatementTimeout = errors.New("statement timeout") // ErrIdleInTransaction is returned when transaction is idle too long ErrIdleInTransaction = errors.New("idle in transaction timeout") // ErrConnectionLost is returned when database connection is lost ErrConnectionLost = errors.New("connection lost") // ErrProtocolViolation is returned for database protocol violations ErrProtocolViolation = errors.New("protocol violation") // ErrInternalError is returned for internal database errors ErrInternalError = errors.New("internal database error") // ErrSystemError is returned for system-level database errors ErrSystemError = errors.New("system error") )
Common database error types that can be used by consumers of this package. These provide a standardized set of errors that abstract away the underlying database-specific error details.
var FXModule = fx.Module("postgres", fx.Provide( NewPostgresClientWithDI, ), fx.Invoke(RegisterPostgresLifecycle), )
FXModule is an fx module that provides the Postgres database component. It registers the Postgres constructor for dependency injection and sets up lifecycle hooks to properly initialize and shut down the database connection.
Functions ¶
func RegisterPostgresLifecycle ¶
func RegisterPostgresLifecycle(params PostgresLifeCycleParams)
RegisterPostgresLifecycle registers lifecycle hooks for the Postgres database component. It sets up: 1. Connection monitoring on the application starts 2. Automatic reconnection mechanism on application start 3. Graceful shutdown of database connections on application stop
The function uses a WaitGroup to ensure that all goroutines complete before the application terminates.
Types ¶
type Config ¶
type Config struct {
// Connection contains the essential parameters needed to establish a database connection
Connection Connection
// ConnectionDetails contains configuration for the connection pool behavior
ConnectionDetails ConnectionDetails
}
Config represents the complete configuration for a PostgresSQL database connection. It encapsulates both the basic connection parameters and detailed connection pool settings.
type Connection ¶
type Connection struct {
// Host specifies the database server hostname or IP address
Host string
// Port specifies the TCP port on which the database server is listening to
Port string
// User specifies the database username for authentication
User string
// Password specifies the database user password for authentication
Password string
// DbName specifies the name of the database to connect to
DbName string
// SSLMode specifies the SSL mode for the connection (e.g., "disable", "require", "verify-ca", "verify-full")
// For production environments, it's recommended to use at least "require"
SSLMode string
}
Connection holds the basic parameters required to connect to a PostgresSQL database. These parameters are used to construct the database connection string.
type ConnectionDetails ¶
type ConnectionDetails struct {
// MaxOpenConns controls the maximum number of open connections to the database.
// Setting this appropriately helps prevent overwhelming the database with too many connections.
// Default is 0 (unlimited).
MaxOpenConns int
// MaxIdleConns controls the maximum number of connections in the idle connection pool.
// A higher value can improve performance under a concurrent load but consumes more resources.
// Default is 2.
MaxIdleConns int
// ConnMaxLifetime is the maximum amount of time a connection may be reused.
// Expired connections are closed and removed from the pool during connection acquisition.
// This helps ensure database-enforced timeouts are respected.
// Default is 0 (unlimited).
ConnMaxLifetime time.Duration
}
ConnectionDetails holds configuration settings for the database connection pool. These settings help optimize performance and resource usage by controlling how database connections are created, reused, and expired.
type ErrorCategory ¶
type ErrorCategory int
ErrorCategory represents different categories of database errors
const ( CategoryUnknown ErrorCategory = iota CategoryConnection CategoryQuery CategoryData CategoryConstraint CategoryPermission CategoryTransaction CategoryResource CategorySystem CategorySchema CategoryOperation )
type Migration ¶
type Migration struct {
// ID is a unique identifier for the migration, typically a timestamp (YYYYMMDDHHMMSS)
// that also helps establish the execution order.
ID string
// Name is a descriptive label for the migration, typically describing what it does.
Name string
// Type categorizes the migration as either schema or data.
Type MigrationType
// Direction indicates whether this is an up (apply) or down (rollback) migration.
Direction MigrationDirection
// SQL contains the actual SQL statements to execute for this migration.
SQL string
}
Migration represents a single database migration with all its metadata and content. Each migration contains the SQL to execute and information about its purpose and identity.
type MigrationDirection ¶
type MigrationDirection string
MigrationDirection specifies the direction of the migration, indicating whether it's applying a change or reverting one.
const ( // UpMigration indicates a forward migration that applies a change. UpMigration MigrationDirection = "up" // DownMigration indicates a rollback migration that reverts a change. DownMigration MigrationDirection = "down" )
type MigrationHistoryRecord ¶
type MigrationHistoryRecord struct {
// ID matches the migration ID that was applied.
ID string
// Name is the descriptive name of the migration.
Name string
// Type indicates whether this was a schema or data migration.
Type string
// ExecutedAt records when the migration was applied.
ExecutedAt time.Time
// ExecutedBy tracks who or what system applied the migration.
ExecutedBy string
// Duration measures how long the migration took to execute in milliseconds.
Duration int64
// Status indicates whether the migration completed successfully or failed.
Status string
// ErrorMessage contains details if the migration failed.
ErrorMessage string `gorm:"type:text"`
}
MigrationHistoryRecord represents a record in the migration history table. It tracks when and how each migration was applied, enabling the system to determine which migrations have been run and providing an audit trail.
type MigrationType ¶
type MigrationType string
MigrationType defines the type of migration, categorizing the purpose of the change. This helps track and organize migrations based on their impact on the database.
const ( // SchemaType represents schema changes (tables, columns, indexes, etc.) // These migrations modify the structure of the database. SchemaType MigrationType = "schema" // DataType represents data manipulations (inserts, updates, etc.) // These migrations modify the content within the database. DataType MigrationType = "data" )
type Postgres ¶
Postgres is a thread-safe wrapper around gorm.DB that provides connection monitoring, automatic reconnection, and standardized database operations. It guards all database operations with a mutex to ensure thread safety and includes mechanisms for graceful shutdown and connection health monitoring.
func NewPostgres ¶
NewPostgres creates a new Postgres instance with the provided configuration and Logger. It establishes the initial database connection and sets up the internal state for connection monitoring and recovery. If the initial connection fails, it logs a fatal error and terminates.
func NewPostgresClientWithDI ¶
func NewPostgresClientWithDI(params PostgresParams) (*Postgres, error)
NewPostgresClientWithDI creates a new Postgres Client using dependency injection. This function is designed to be used with Uber's fx dependency injection framework where the Config and Logger dependencies are automatically provided via the PostgresParams struct.
Parameters:
- params: A PostgresParams struct containing the Config and Logger instances required to initialize the Postgres Client. This struct embeds fx.In to enable automatic injection of these dependencies.
Returns:
- *Postgres: A fully initialized Postgres Client ready for use.
Example usage with fx:
app := fx.New(
postgres.FXModule,
fx.Provide(
func() postgres.Config {
return loadPostgresConfig() // Your config loading function
},
func() postgres.Logger {
return initLogger() // Your logger initialization
},
),
)
This function delegates to the standard NewPostgres function, maintaining the same initialization logic while enabling seamless integration with dependency injection.
func (*Postgres) AutoMigrate ¶
AutoMigrate is a wrapper around GORM's AutoMigrate with additional features. It tracks migrations in the migration history table and provides better error handling.
Parameters:
- models: The GORM models to auto-migrate
Returns a GORM error if any part of the migration process fails.
This method is useful during development or for simple applications, but for production systems, explicit migrations are recommended.
func (*Postgres) Count ¶
func (p *Postgres) Count(ctx context.Context, model interface{}, count *int64, conditions ...interface{}) error
Count determines the number of records that match the given conditions. It populates the count parameter with the result.
Parameters:
- ctx: Context for the database operation
- model: The model type to count
- count: Pointer to an int64 where the count will be stored
- conditions: Query conditions to filter the records to count
Returns a GORM error if the query fails or nil on success. Use TranslateError() to convert to standardized error types if needed.
Example:
var count int64
err := db.Count(ctx, &User{}, &count, "age > ?", 18)
func (*Postgres) Create ¶
Create inserts a new record into the database. It processes the value parameter according to GORM conventions, performing hooks and validations defined on the model.
Parameters:
- ctx: Context for the database operation
- value: The struct or slice of structs to be created
Returns a GORM error if the creation fails or nil on success. Use TranslateError() to convert to standardized error types if needed.
Example:
user := User{Name: "John", Email: "john@example.com"}
err := db.Create(ctx, &user)
func (*Postgres) CreateMigration ¶
func (p *Postgres) CreateMigration(migrationsDir, name string, migrationType MigrationType) (string, error)
CreateMigration generates a new migration file. It creates both up and down migration files with appropriate names and timestamps.
Parameters:
- migrationsDir: Directory where migration files should be created
- name: Descriptive name for the migration
- migrationType: Whether this is a schema or data migration
Returns the base filename of the created migration or a wrapped error if creation fails.
Example:
filename, err := db.CreateMigration("./migrations", "create_users_table", postgres.SchemaType)
if err == nil {
fmt.Printf("Created migration: %s\n", filename)
}
func (*Postgres) DB ¶
DB returns the underlying GORM DB Client instance. This method provides direct access to the database connection while maintaining thread safety through a read lock.
Use this method when you need to perform operations not covered by the wrapper methods or when you need to access specific GORM functionality. Note that direct usage bypasses some of the safety mechanisms, so use it with care.
func (*Postgres) Delete ¶
func (p *Postgres) Delete(ctx context.Context, value interface{}, conditions ...interface{}) (int64, error)
Delete removes records that match the given value and conditions from the database. It respects soft delete if implemented on the model.
Parameters:
- ctx: Context for the database operation
- value: The model to delete or a slice for batch delete
- conditions: Additional conditions to filter records to delete
Returns:
- int64: Number of rows affected by the delete operation
- error: GORM error if the deletion fails, nil on success
Example:
// Delete user with ID=1
rowsAffected, err := db.Delete(ctx, &User{}, "id = ?", 1)
if err != nil {
return err
}
fmt.Printf("Deleted %d rows\n", rowsAffected)
// Or with a model instance
user := User{ID: 1}
rowsAffected, err := db.Delete(ctx, &user)
func (*Postgres) Exec ¶
Exec executes raw SQL directly against the database. This is useful for operations not easily expressed through GORM's API or for performance-critical code.
Parameters:
- ctx: Context for the database operation
- sql: The SQL statement to execute
- values: Parameters for the SQL statement
Returns:
- int64: Number of rows affected by the SQL execution
- error: GORM error if the execution fails, nil on success
Example:
rowsAffected, err := db.Exec(ctx, "UPDATE users SET status = ? WHERE last_login < ?",
"inactive", time.Now().AddDate(0, -6, 0))
if err != nil {
return err
}
fmt.Printf("Updated %d users\n", rowsAffected)
func (*Postgres) Find ¶
Find retrieves records from the database that match the given conditions. It populates the dest parameter with the query results.
Parameters:
- ctx: Context for the database operation
- dest: Pointer to a slice where the results will be stored
- conditions: Optional query conditions (follows GORM conventions)
Returns a GORM error if the query fails or nil on success. Use TranslateError() to convert to standardized error types if needed.
Example:
var users []User err := db.Find(ctx, &users, "name LIKE ?", "%john%")
func (*Postgres) First ¶
First retrieves the first record that matches the given conditions. It populates the dest parameter with the result or returns an error if no matching record exists.
Parameters:
- ctx: Context for the database operation
- dest: Pointer to a struct where the result will be stored
- conditions: Optional query conditions (follows GORM conventions)
Returns gorm.ErrRecordNotFound if no matching record exists, or another GORM error if the query fails. Use TranslateError() to convert to standardized error types if needed.
Example:
var user User
err := db.First(ctx, &user, "email = ?", "user@example.com")
if errors.Is(err, gorm.ErrRecordNotFound) {
// Handle not found
}
func (*Postgres) GetErrorCategory ¶
func (p *Postgres) GetErrorCategory(err error) ErrorCategory
GetErrorCategory returns the category of the given error
func (*Postgres) GetMigrationStatus ¶
func (p *Postgres) GetMigrationStatus(ctx context.Context, migrationsDir string) ([]map[string]interface{}, error)
GetMigrationStatus returns the status of all migrations. It compares available migrations with those that have been applied to build a comprehensive status report.
Parameters:
- ctx: Context for database operations
- migrationsDir: Directory containing the migration SQL files
Returns a slice of maps with status information for each migration, or a wrapped error if the status cannot be determined. The error wraps the underlying GORM error with additional context.
Example:
status, err := db.GetMigrationStatus(ctx, "./migrations")
if err == nil {
for _, m := range status {
fmt.Printf("Migration %s: %v\n", m["id"], m["applied"])
}
}
func (*Postgres) GracefulShutdown ¶
func (*Postgres) IsCritical ¶
IsCritical returns true if the error indicates a serious system problem
func (*Postgres) IsRetryable ¶
IsRetryable returns true if the error might be resolved by retrying the operation
func (*Postgres) IsTemporary ¶
IsTemporary returns true if the error is likely temporary and might resolve itself
func (*Postgres) MigrateDown ¶
MigrateDown rolls back the last applied migration. It finds the most recently applied migration and executes its corresponding down migration to revert the changes.
Parameters:
- ctx: Context for database operations
- migrationsDir: Directory containing the migration SQL files
Returns a wrapped error if the rollback fails or if the down migration can't be found. The error wraps the underlying GORM error with additional context.
Example:
err := db.MigrateDown(ctx, "./migrations")
func (*Postgres) MigrateUp ¶
MigrateUp applies all pending migrations from the specified directory. It identifies which migrations haven't been applied yet, sorts them by ID, and applies them in order within transactions.
Parameters:
- ctx: Context for database operations
- migrationsDir: Directory containing the migration SQL files
Returns a wrapped error if any migration fails or if there are issues accessing the migrations. The error wraps the underlying GORM error with additional context.
Example:
err := db.MigrateUp(ctx, "./migrations")
func (*Postgres) MonitorConnection ¶
MonitorConnection periodically checks the health of the database connection and triggers reconnection attempts when necessary. It runs as a goroutine that performs health checks at regular intervals (10 seconds) and signals the RetryConnection goroutine when a failure is detected.
The function respects context cancellation and shutdown signals, ensuring proper resource cleanup and graceful termination when requested.
func (*Postgres) Query ¶
func (p *Postgres) Query(ctx context.Context) *QueryBuilder
Query provides a flexible way to build complex queries. It returns a QueryBuilder which can be used to chain query methods in a fluent interface. The method acquires a read lock on the database connection that will be automatically released when a terminal method is called or Done() is invoked.
Parameters:
- ctx: Context for the database operation
Returns a QueryBuilder instance that can be used to construct the query.
Note: QueryBuilder methods return GORM errors directly. Use Postgres.TranslateError() to convert them to standardized error types if needed.
Example:
users := []User{}
err := db.Query(ctx).
Where("age > ?", 18).
Order("created_at DESC").
Limit(10).
Find(&users)
if err != nil {
err = db.TranslateError(err) // Optional: translate to standardized error
}
func (*Postgres) RetryConnection ¶
RetryConnection continuously attempts to reconnect to the PostgresSQL database when notified of a connection failure. It operates as a goroutine that waits for signals on retryChanSignal before attempting reconnection. The function respects context cancellation and shutdown signals, ensuring graceful termination when requested.
It implements two nested loops: - The outer loop waits for retry signals - The inner loop attempts reconnection until successful
func (*Postgres) Save ¶
Save updates the database record if the primary key exists, otherwise it creates a new record. It performs a full update of all fields, not just changed fields.
Parameters:
- ctx: Context for the database operation
- value: The struct to be saved
Returns a GORM error if the operation fails or nil on success. Use TranslateError() to convert to standardized error types if needed.
Example:
user.Name = "Updated Name" err := db.Save(ctx, &user)
func (*Postgres) Transaction ¶
Transaction executes the given function within a database transaction. It creates a transaction-specific Postgres instance and passes it to the provided function. If the function returns an error, the transaction is rolled back; otherwise, it's committed.
This method provides a clean way to execute multiple database operations as a single atomic unit, with automatic handling of commit/rollback based on the execution result.
Returns a GORM error if the transaction fails or the error returned by the callback function.
Example usage:
err := pg.Transaction(ctx, func(txPg *Postgres) error {
if err := txPg.Create(ctx, user); err != nil {
return err
}
return txPg.Create(ctx, userProfile)
})
func (*Postgres) TranslateError ¶
TranslateError converts GORM/database-specific errors into standardized application errors. This function provides abstraction from the underlying database implementation details, allowing application code to handle errors in a database-agnostic way.
It maps common database errors to the standardized error types defined above. If an error doesn't match any known type, it's returned unchanged.
func (*Postgres) Update ¶
Update updates records that match the given model's non-zero fields or primary key. It only updates the fields provided in attrs and only affects records that match the model's primary key or query conditions.
Parameters:
- ctx: Context for the database operation
- model: The model instance with primary key set, or struct with query conditions
- attrs: Map, struct, or individual field values to update
Returns:
- int64: Number of rows affected by the update operation
- error: GORM error if the update fails, nil on success
Note: The current implementation has a bug where it executes the query twice. This should be fixed to execute only once and return both values properly.
Example:
// Update user with ID=1
rowsAffected, err := db.Update(ctx, &User{ID: 1}, map[string]interface{}{
"name": "New Name",
"age": 30,
})
if err != nil {
return err
}
fmt.Printf("Updated %d rows\n", rowsAffected)
func (*Postgres) UpdateColumn ¶
func (p *Postgres) UpdateColumn(ctx context.Context, model interface{}, columnName string, value interface{}) (int64, error)
UpdateColumn updates a single column's value for records that match the given model. Unlike Update, it doesn't run hooks and can be used to update fields that are zero values (like setting a string to empty or a number to zero).
Parameters:
- ctx: Context for the database operation
- model: The model instance with primary key set, or struct with query conditions
- columnName: Name of the column to update
- value: New value for the column
Returns:
- int64: Number of rows affected by the update operation
- error: GORM error if the update fails, nil on success
Example:
// Set status to "inactive" for user with ID=1
rowsAffected, err := db.UpdateColumn(ctx, &User{ID: 1}, "status", "inactive")
if err != nil {
return err
}
fmt.Printf("Updated %d rows\n", rowsAffected)
func (*Postgres) UpdateColumns ¶
func (p *Postgres) UpdateColumns(ctx context.Context, model interface{}, columnValues map[string]interface{}) (int64, error)
UpdateColumns updates multiple columns with name/value pairs for records that match the given model. Like UpdateColumn, it doesn't run hooks and can update zero-value fields.
Parameters:
- ctx: Context for the database operation
- model: The model instance with primary key set, or struct with query conditions
- columnValues: Map of column names to their new values
Returns:
- int64: Number of rows affected by the update operation
- error: GORM error if the update fails, nil on success
Example:
// Update multiple fields for user with ID=1
rowsAffected, err := db.UpdateColumns(ctx, &User{ID: 1}, map[string]interface{}{
"status": "inactive",
"last_login": time.Now(),
})
if err != nil {
return err
}
fmt.Printf("Updated %d rows\n", rowsAffected)
func (*Postgres) UpdateWhere ¶
func (p *Postgres) UpdateWhere(ctx context.Context, model interface{}, attrs interface{}, condition string, args ...interface{}) (int64, error)
UpdateWhere updates records that match the specified WHERE condition. This method provides more flexibility than Update for complex conditions.
Parameters:
- ctx: Context for the database operation
- model: The model type to update
- attrs: Fields to update (map, struct, or name/value pairs)
- condition: WHERE condition as a string
- args: Arguments for the WHERE condition
Returns:
- int64: Number of rows affected by the update operation
- error: GORM error if the update fails, nil on success
Example:
// Update all users who haven't logged in for 6 months
rowsAffected, err := db.UpdateWhere(ctx, &User{},
map[string]interface{}{"status": "inactive"},
"last_login < ?",
time.Now().AddDate(0, -6, 0))
if err != nil {
return err
}
fmt.Printf("Updated %d users to inactive status\n", rowsAffected)
type PostgresLifeCycleParams ¶
PostgresLifeCycleParams groups the dependencies needed for Postgres lifecycle management. This struct combines all the components required to properly manage the lifecycle of a Postgres Client within an fx application, including startup, monitoring, and graceful shutdown.
The embedded fx.In marker enables automatic injection of the struct fields from the dependency container when this struct is used as a parameter in lifecycle registration functions.
type PostgresParams ¶
PostgresParams groups the dependencies needed to create a Postgres Client via dependency injection. This struct is designed to work with Uber's fx dependency injection framework and provides the necessary parameters for initializing a Postgres database connection.
The embedded fx.In marker enables automatic injection of the struct fields from the dependency container when this struct is used as a parameter in provider functions.
type QueryBuilder ¶
type QueryBuilder struct {
// contains filtered or unexported fields
}
QueryBuilder provides a fluent interface for building complex database queries. It wraps GORM's query building capabilities with thread-safety and automatic resource cleanup. The builder maintains a chain of query modifiers that are applied when a terminal method is called.
Note: All terminal methods (First, Find, Create, etc.) return GORM errors directly. This preserves the error chain and allows consumers to use errors.Is() with GORM error types. Use Postgres.TranslateError() to convert errors to standardized types if needed.
func (*QueryBuilder) Clauses ¶
func (qb *QueryBuilder) Clauses(conds ...clause.Expression) *QueryBuilder
Clauses adds custom clauses to the query. This is a generic method for adding any GORM clause type.
Parameters:
- conds: One or more clause expressions
Returns the QueryBuilder for method chaining.
Example:
qb.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "RANDOM()"},
}).Find(&users) // Random order
qb.Clauses(clause.GroupBy{
Columns: []clause.Column{{Name: "department"}},
}).Find(&users)
func (*QueryBuilder) Count ¶
func (qb *QueryBuilder) Count(count *int64) error
Count counts records that match the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- count: Pointer to an int64 where the count will be stored
Returns a GORM error if the query fails or nil on success.
Example:
var count int64
err := qb.Where("active = ?", true).Count(&count)
func (*QueryBuilder) Create ¶ added in v0.9.0
func (qb *QueryBuilder) Create(value interface{}) (int64, error)
Create inserts a new record into the database. This is a terminal method that executes the operation and releases the mutex lock. It can be combined with OnConflict() for UPSERT operations, Returning() for PostgreSQL RETURNING clause, and other query builder methods.
Parameters:
- value: Pointer to the struct or slice of structs to create
Returns:
- int64: Number of rows affected (records created)
- error: GORM error if the operation fails, nil on success
Example:
user := User{Name: "John", Email: "john@example.com"}
rowsAffected, err := qb.Create(&user)
if err != nil {
return err
}
fmt.Printf("Created %d record, ID: %d\n", rowsAffected, user.ID)
With OnConflict for idempotent create:
rowsAffected, err := qb.OnConflict(clause.OnConflict{DoNothing: true}).Create(&stage)
if rowsAffected == 0 {
// Record already exists
}
func (*QueryBuilder) CreateInBatches ¶
func (qb *QueryBuilder) CreateInBatches(value interface{}, batchSize int) (int64, error)
CreateInBatches creates records in batches to avoid memory issues with large datasets. This is a terminal method that executes the operation and releases the mutex lock.
Parameters:
- value: Slice of records to create
- batchSize: Number of records to process in each batch
Returns:
- int64: Number of rows affected (records created)
- error: GORM error if the operation fails, nil on success
Example:
users := []User{{Name: "John"}, {Name: "Jane"}, {Name: "Bob"}}
rowsAffected, err := qb.CreateInBatches(&users, 100)
if err != nil {
return err
}
fmt.Printf("Created %d records\n", rowsAffected)
func (*QueryBuilder) Delete ¶
func (qb *QueryBuilder) Delete(value interface{}) (int64, error)
Delete deletes records that match the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- value: Model value or pointer to specify what to delete
Returns:
- int64: Number of rows affected by the delete operation
- error: GORM error if the deletion fails, nil on success
Example:
rowsAffected, err := qb.Where("created_at < ?", time.Now().AddDate(-1, 0, 0)).Delete(&User{})
if err != nil {
return err
}
fmt.Printf("Deleted %d rows\n", rowsAffected)
func (*QueryBuilder) Distinct ¶
func (qb *QueryBuilder) Distinct(args ...interface{}) *QueryBuilder
Distinct specifies that the query should return distinct results. It eliminates duplicate rows from the result set.
Parameters:
- args: Optional columns to apply DISTINCT to
Returns the QueryBuilder for method chaining.
Example:
qb.Distinct("department").Find(&departments)
qb.Distinct().Where("age > ?", 18).Find(&users) // SELECT DISTINCT * FROM users WHERE age > 18
func (*QueryBuilder) Done ¶
func (qb *QueryBuilder) Done()
Done releases the mutex lock without executing the query. This method should be called when you want to cancel a query building chain without executing any terminal operation.
Example:
qb := db.Query(ctx)
if someCondition {
err := qb.Where(...).Find(&results)
} else {
qb.Done() // Release the lock without executing
}
func (*QueryBuilder) Find ¶
func (qb *QueryBuilder) Find(dest interface{}) error
Find finds records that match the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- dest: Pointer to a slice where results will be stored
Returns a GORM error if the query fails or nil on success. Use Postgres.TranslateError() to convert to standardized error types.
Example:
var users []User
err := qb.Where("active = ?", true).Find(&users)
func (*QueryBuilder) First ¶
func (qb *QueryBuilder) First(dest interface{}) error
First finds the first record that matches the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- dest: Pointer to a struct where the result will be stored
Returns gorm.ErrRecordNotFound if no record is found, or another GORM error if the query fails, nil on success. Use Postgres.TranslateError() to convert to standardized error types if needed.
Example:
var user User
err := qb.Where("email = ?", "user@example.com").First(&user)
if err != nil {
err = db.TranslateError(err) // Optional: convert to standardized error
}
func (*QueryBuilder) FirstOrCreate ¶
func (qb *QueryBuilder) FirstOrCreate(dest interface{}, conds ...interface{}) error
FirstOrCreate finds the first record matching the conditions, or creates a new one if not found. This is a terminal method.
Parameters:
- dest: Pointer to the struct where the result will be stored
- conds: Optional conditions for the query
Returns a GORM error if the operation fails or nil on success.
Example:
var user User
err := qb.Where("email = ?", "user@example.com").FirstOrCreate(&user)
func (*QueryBuilder) FirstOrInit ¶
func (qb *QueryBuilder) FirstOrInit(dest interface{}, conds ...interface{}) error
FirstOrInit finds the first record matching the conditions, or initializes a new one if not found. This is a terminal method.
Parameters:
- dest: Pointer to the struct where the result will be stored
- conds: Optional conditions for the query
Returns a GORM error if the operation fails or nil on success.
Example:
var user User
err := qb.Where("email = ?", "user@example.com").FirstOrInit(&user)
func (*QueryBuilder) ForKeyShare ¶
func (qb *QueryBuilder) ForKeyShare() *QueryBuilder
ForKeyShare adds a FOR KEY SHARE clause to the query (PostgreSQL specific). This acquires a shared lock that blocks other transactions from acquiring exclusive locks but allows other key share and share locks.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("id = ?", userID).ForKeyShare().First(&user) // PostgreSQL only
func (*QueryBuilder) ForNoKeyUpdate ¶
func (qb *QueryBuilder) ForNoKeyUpdate() *QueryBuilder
ForNoKeyUpdate adds a FOR NO KEY UPDATE clause to the query (PostgreSQL specific). This acquires a weaker exclusive lock that doesn't block other transactions from acquiring key share locks on the same rows.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("id = ?", userID).ForNoKeyUpdate().First(&user) // PostgreSQL only
func (*QueryBuilder) ForShare ¶
func (qb *QueryBuilder) ForShare() *QueryBuilder
ForShare adds a FOR SHARE clause to the query for shared row-level locking. This allows other transactions to read the rows but prevents them from updating or deleting until the current transaction commits or rolls back.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("id = ?", userID).ForShare().First(&user) // Shared lock for reading
qb.ForShare().Where("status = ?", "active").Find(&users) // Prevents updates but allows reads
func (*QueryBuilder) ForShareSkipLocked ¶
func (qb *QueryBuilder) ForShareSkipLocked() *QueryBuilder
ForShareSkipLocked adds a FOR SHARE SKIP LOCKED clause to the query. This acquires shared locks but skips any rows that are already exclusively locked.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("category = ?", "news").ForShareSkipLocked().Find(&articles)
func (*QueryBuilder) ForUpdate ¶
func (qb *QueryBuilder) ForUpdate() *QueryBuilder
ForUpdate adds a FOR UPDATE clause to the query for exclusive row-level locking. This prevents other transactions from modifying the selected rows until the current transaction commits or rolls back.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("id = ?", userID).ForUpdate().First(&user) // Locks the row for update
qb.ForUpdate().Where("status = ?", "pending").Find(&orders) // Locks all matching rows
func (*QueryBuilder) ForUpdateNoWait ¶
func (qb *QueryBuilder) ForUpdateNoWait() *QueryBuilder
ForUpdateNoWait adds a FOR UPDATE NOWAIT clause to the query. This attempts to acquire exclusive locks but immediately fails if any target rows are already locked, instead of waiting.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("id = ?", accountID).ForUpdateNoWait().First(&account)
func (*QueryBuilder) ForUpdateSkipLocked ¶
func (qb *QueryBuilder) ForUpdateSkipLocked() *QueryBuilder
ForUpdateSkipLocked adds a FOR UPDATE SKIP LOCKED clause to the query. This acquires exclusive locks but skips any rows that are already locked, making it ideal for job queue processing where you want to avoid blocking.
Returns the QueryBuilder for method chaining.
Example:
qb.Where("status = ?", "pending").ForUpdateSkipLocked().Limit(10).Find(&jobs)
qb.ForUpdateSkipLocked().Where("processed = ?", false).First(&task)
func (*QueryBuilder) Group ¶
func (qb *QueryBuilder) Group(query string) *QueryBuilder
Group adds a GROUP BY clause to the query. It is used to group rows with the same values into summary rows.
Parameters:
- query: GROUP BY expression
Returns the QueryBuilder for method chaining.
Example:
qb.Group("status")
qb.Group("department, location")
func (*QueryBuilder) Having ¶
func (qb *QueryBuilder) Having(query interface{}, args ...interface{}) *QueryBuilder
Having added a HAVING clause to the query. It is used to filter groups created by the GROUP BY clause.
Parameters:
- query: HAVING condition with optional placeholders
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Group("department").Having("COUNT(*) > ?", 3)
func (*QueryBuilder) Joins ¶
func (qb *QueryBuilder) Joins(query string, args ...interface{}) *QueryBuilder
Joins add a JOIN clause to the query. It performs an INNER JOIN by default.
Parameters:
- query: JOIN condition string
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Joins("JOIN orders ON orders.user_id = users.id")
func (*QueryBuilder) Last ¶
func (qb *QueryBuilder) Last(dest interface{}) error
Last finds the last record that matches the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- dest: Pointer to a struct where the result will be stored
Returns gorm.ErrRecordNotFound if no record is found, or another GORM error if the query fails, nil on success. Use Postgres.TranslateError() to convert to standardized error types if needed.
Example:
var user User
err := qb.Where("department = ?", "Engineering").Order("joined_at ASC").Last(&user)
func (*QueryBuilder) LeftJoin ¶
func (qb *QueryBuilder) LeftJoin(query string, args ...interface{}) *QueryBuilder
LeftJoin adds a LEFT JOIN clause to the query. It retrieves all records from the left table and matching records from the right table.
Parameters:
- query: JOIN condition string without the "LEFT JOIN" prefix
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.LeftJoin("orders ON orders.user_id = users.id")
func (*QueryBuilder) Limit ¶
func (qb *QueryBuilder) Limit(limit int) *QueryBuilder
Limit sets the maximum number of records to return.
Parameters:
- limit: Maximum number of records
Returns the QueryBuilder for method chaining.
Example:
qb.Limit(10) // Return at most 10 records
func (*QueryBuilder) MapRows ¶
func (qb *QueryBuilder) MapRows(destSlice interface{}, mapFn func(*gorm.DB) error) error
MapRows executes a query and maps all rows into a destination slice using the provided mapping function. This provides a higher-level abstraction than working with raw rows, allowing for custom mapping logic while still handling the query execution and resource management automatically.
Parameters:
- destSlice: The slice to populate with mapped rows (should be a pointer to a slice)
- mapFn: A function that defines how to map rows from the database to your slice items
Returns a GORM error if the mapping fails or nil on success.
func (*QueryBuilder) Model ¶
func (qb *QueryBuilder) Model(value interface{}) *QueryBuilder
Model specifies the model to use for the query. This is useful when the model can't be inferred from other methods.
Parameters:
- value: Pointer to the model struct or its instance
Returns the QueryBuilder for method chaining.
Example:
qb.Model(&User{}).Where("active = ?", true).Count(&count)
func (*QueryBuilder) Not ¶
func (qb *QueryBuilder) Not(query interface{}, args ...interface{}) *QueryBuilder
Not adds a NOT condition to the query. It negates the specified condition.
Parameters:
- query: Condition string with optional placeholders or a map of conditions
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Not("status = ?", "deleted")
func (*QueryBuilder) Offset ¶
func (qb *QueryBuilder) Offset(offset int) *QueryBuilder
Offset sets the number of records to skip. It is typically used with Limit for pagination.
Parameters:
- offset: Number of records to skip
Returns the QueryBuilder for method chaining.
Example:
qb.Offset(20).Limit(10) // Skip 20 records and return the next 10
func (*QueryBuilder) OnConflict ¶
func (qb *QueryBuilder) OnConflict(onConflict clause.OnConflict) *QueryBuilder
OnConflict adds an ON CONFLICT clause for UPSERT operations. This handles conflicts during INSERT operations by either updating existing records or ignoring conflicts.
Parameters:
- onConflict: ON CONFLICT clause configuration
Returns the QueryBuilder for method chaining.
Example:
qb.OnConflict(clause.OnConflict{
Columns: []clause.Column{{Name: "email"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "updated_at"}),
}).Create(&user)
func (*QueryBuilder) Or ¶
func (qb *QueryBuilder) Or(query interface{}, args ...interface{}) *QueryBuilder
Or adds an OR condition to the query. It combines with previous conditions using OR logic.
Parameters:
- query: Condition string with optional placeholders or a map of conditions
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Where("status = ?", "active").Or("status = ?", "pending")
func (*QueryBuilder) Order ¶
func (qb *QueryBuilder) Order(value interface{}) *QueryBuilder
Order adds an ORDER BY clause to the query. It is used to sort the result set.
Parameters:
- value: ORDER BY expression
Returns the QueryBuilder for method chaining.
Example:
qb.Order("created_at DESC")
qb.Order("age ASC, name DESC")
func (*QueryBuilder) Pluck ¶
func (qb *QueryBuilder) Pluck(column string, dest interface{}) (int64, error)
Pluck queries a single column and scans the results into a slice. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- column: Name of the column to query
- dest: Pointer to a slice where results will be stored
Returns:
- int64: Number of rows found and processed
- error: GORM error if the query fails, nil on success
Example:
var emails []string
rowsFound, err := qb.Where("department = ?", "Engineering").Pluck("email", &emails)
if err != nil {
return err
}
fmt.Printf("Found %d email addresses\n", rowsFound)
func (*QueryBuilder) Preload ¶
func (qb *QueryBuilder) Preload(query string, args ...interface{}) *QueryBuilder
Preload preloads associations for the query results. This is used to eagerly load related models to avoid N+1 query problems.
Parameters:
- query: Name of the association to preload
- args: Optional conditions for the preloaded association
Return the QueryBuilder for method chaining.
Example:
qb.Preload("Orders")
qb.Preload("Orders", "state = ?", "paid")
func (*QueryBuilder) QueryRow ¶
func (qb *QueryBuilder) QueryRow() RowScanner
QueryRow executes a query expected to return a single row and returns a RowScanner for it. This method is optimized for queries that return exactly one row of data and provides a simplified interface for scanning the values from that row.
func (*QueryBuilder) QueryRows ¶
func (qb *QueryBuilder) QueryRows() (RowsScanner, error)
QueryRows executes a query that returns multiple rows and returns a RowsScanner for them. This method provides an iterator-style interface for processing multiple rows returned by a query, allowing for efficient traversal of large result sets.
Returns a RowsScanner and a GORM error if the query fails.
func (*QueryBuilder) Raw ¶
func (qb *QueryBuilder) Raw(sql string, values ...interface{}) *QueryBuilder
Raw executes raw SQL as part of the query. It provides full SQL flexibility when needed.
Parameters:
- SQL: Raw SQL statement with optional placeholders
- values: Arguments for any placeholders in the SQL
Returns the QueryBuilder for method chaining.
Example:
qb.Raw("SELECT * FROM users WHERE created_at > ?", time.Now().AddDate(0, -1, 0))
func (*QueryBuilder) Returning ¶
func (qb *QueryBuilder) Returning(columns ...string) *QueryBuilder
Returning adds a RETURNING clause to the query (PostgreSQL specific). This returns the specified columns from the modified rows in INSERT, UPDATE, or DELETE operations.
Parameters:
- columns: Column names to return
Returns the QueryBuilder for method chaining.
Example:
qb.Returning("id", "created_at").Create(&user)
qb.Where("status = ?", "pending").Returning("*").Updates(map[string]interface{}{"status": "processed"})
func (*QueryBuilder) RightJoin ¶
func (qb *QueryBuilder) RightJoin(query string, args ...interface{}) *QueryBuilder
RightJoin adds a RIGHT JOIN clause to the query. It retrieves all records from the right table and matching records from the left table.
Parameters:
- query: JOIN condition string without the "RIGHT JOIN" prefix
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.RightJoin("orders ON orders.user_id = users.id")
func (*QueryBuilder) Scan ¶
func (qb *QueryBuilder) Scan(dest interface{}) error
Scan scans the result into the destination struct or slice. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- dest: Pointer to the struct or slice where results will be stored
Returns a GORM error if the query fails or nil on success. Use Postgres.TranslateError() to convert to standardized error types.
Example:
var result struct{ Count int }
err := qb.Raw("SELECT COUNT(*) as count FROM users").Scan(&result)
func (*QueryBuilder) ScanRow ¶
func (qb *QueryBuilder) ScanRow(dest interface{}) error
ScanRow is a convenience method to scan a single row directly into a struct. This is a higher-level alternative to QueryRow that automatically maps column values to struct fields based on naming conventions or field tags. It's useful when you need to map a row to a predefined data structure.
Returns a GORM error if the scan fails or nil on success.
func (*QueryBuilder) Scopes ¶
func (qb *QueryBuilder) Scopes(funcs ...func(*gorm.DB) *gorm.DB) *QueryBuilder
Scopes applies one or more scopes to the query. Scopes are reusable query conditions that can be applied to multiple queries.
Parameters:
- funcs: One or more scope functions that modify the query
Returns the QueryBuilder for method chaining.
Example:
// Define scopes
func ActiveUsers(db *gorm.DB) *gorm.DB {
return db.Where("active = ?", true)
}
func AdultUsers(db *gorm.DB) *gorm.DB {
return db.Where("age >= ?", 18)
}
// Use scopes
qb.Scopes(ActiveUsers, AdultUsers).Find(&users)
qb.Scopes(ActiveUsers).Count(&count)
func (*QueryBuilder) Select ¶
func (qb *QueryBuilder) Select(query interface{}, args ...interface{}) *QueryBuilder
Select specifies fields to be selected in the query. It corresponds to the SQL SELECT clause.
Parameters:
- query: Field selection string or raw SQL expression
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Select("id, name, email")
qb.Select("COUNT(*) as user_count")
func (*QueryBuilder) Table ¶
func (qb *QueryBuilder) Table(name string) *QueryBuilder
Table specifies the table name for the query. This overrides the default table name derived from the model.
Parameters:
- name: Table name to use for the query
Returns the QueryBuilder for method chaining.
Example:
qb.Table("users_archive").Where("deleted_at IS NOT NULL").Find(&users)
qb.Table("custom_table_name").Count(&count)
qb.Table("user_stats").Select("department, COUNT(*) as count").Group("department").Scan(&stats)
func (*QueryBuilder) ToSubquery ¶ added in v0.9.0
func (qb *QueryBuilder) ToSubquery() *gorm.DB
ToSubquery returns the underlying GORM DB for use as a subquery and releases the lock. This method is specifically designed for creating subqueries that can be passed to Where(), Having(), or other clauses that accept subqueries.
Important: This method releases the lock immediately, so the returned *gorm.DB should be used as a subquery argument right away.
Returns:
- *gorm.DB: The underlying GORM DB instance configured with the query chain
Example:
// Find users whose email is in a subquery of active accounts
activeEmails := pg.Query(ctx).
Model(&Account{}).
Select("email").
Where("status = ?", "active").
ToSubquery()
var users []User
err := pg.Query(ctx).
Where("email IN (?)", activeEmails).
Find(&users)
Complex example with multiple subqueries:
// Find stages with no files
stageIDsWithFiles := pg.Query(ctx).
Model(&File{}).
Select("DISTINCT stage_id").
ToSubquery()
err := pg.Query(ctx).
Model(&Stage{}).
Where("stage_id NOT IN (?)", stageIDsWithFiles).
Find(&stages)
func (*QueryBuilder) Unscoped ¶
func (qb *QueryBuilder) Unscoped() *QueryBuilder
Unscoped disables the default scope for the query. This allows querying soft-deleted records or bypassing other default scopes. Commonly used with GORM's soft delete feature.
Returns the QueryBuilder for method chaining.
Example:
qb.Unscoped().Where("name = ?", "John").Find(&users) // Includes soft-deleted records
qb.Unscoped().Delete(&user) // Permanently deletes the record
qb.Unscoped().Count(&count) // Counts all records including soft-deleted
func (*QueryBuilder) Updates ¶
func (qb *QueryBuilder) Updates(values interface{}) (int64, error)
Updates updates records that match the query conditions. This is a terminal method that executes the query and releases the mutex lock.
Parameters:
- values: Map or struct with the fields to update
Returns:
- int64: Number of rows affected by the update operation
- error: GORM error if the update fails, nil on success
Example:
rowsAffected, err := qb.Where("expired = ?", true).Updates(map[string]interface{}{"active": false})
if err != nil {
return err
}
fmt.Printf("Updated %d rows\n", rowsAffected)
func (*QueryBuilder) Where ¶
func (qb *QueryBuilder) Where(query interface{}, args ...interface{}) *QueryBuilder
Where adds a WHERE condition to the query. Multiple Where calls are combined with AND logic.
Parameters:
- query: Condition string with optional placeholders or a map of conditions
- args: Arguments for any placeholders in the query
Returns the QueryBuilder for method chaining.
Example:
qb.Where("age > ?", 18)
qb.Where("status = ?", "active")
type RowScanner ¶
type RowScanner interface {
// Scan copies the column values from the current row into the values pointed to by dest.
// The number of values in dest must match the number of columns in the row.
Scan(dest ...interface{}) error
}
RowScanner provides an interface for scanning a single row of data. It abstracts the process of parsing column values into Go variables, allowing for efficient handling of individual rows returned from a query.
type RowsScanner ¶
type RowsScanner interface {
// Next prepares the next row for reading. It returns false when there are no more rows.
Next() bool
// Scan copies column values from the current row into the provided destination variables.
Scan(dest ...interface{}) error
// Close closes the rows iterator, releasing any associated resources.
Close() error
// Err returns any error encountered during iteration.
Err() error
}
RowsScanner provides an interface for iterating through rows of data returned by a query. It extends RowScanner functionality with methods for navigation and error handling, allowing for efficient processing of result sets with multiple rows.