pgutil

package
v9.4.0 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2025 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package pgutil provides utilities for PostgreSQL database operations with SQLC integration.

The pgutil package consolidates common database patterns used across rebuy projects, providing unified connection management, migration framework, transaction wrappers, URI construction helpers, and standard SQLC configuration templates.

Features

  • Connection Management: Unified connection creation with optional DataDog tracing
  • Migration Framework: Generic migration execution with embedded filesystems (both normal and repeatable)
  • Transaction Wrappers: Reusable transaction and connection hijacking utilities
  • URI Construction: Helper functions for database URI manipulation
  • SQLC Templates: Standard configuration templates for consistent project setup

Quick Start

Basic usage example:

package main

import (
    "context"
    "embed"

    "github.com/rebuy-de/rebuy-go-sdk/v9/pkg/pgutil"
)

//go:embed migrations/*.sql
var migrationsFS embed.FS

func main() {
    ctx := context.Background()
    uri := "postgres://user:pass@localhost:5432/mydb"

    // Run migrations (both normal and repeatable)
    err := pgutil.MigrateWithEmbeddedFS(ctx, uri, "my_app", migrationsFS, "migrations")
    if err != nil {
        panic(err)
    }

    // Create connection with tracing
    queries, err := pgutil.NewQueriesInterface(ctx, uri, pgutil.ConnectionOptions{
        EnableTracing: true,
        SchemaName:   "my_app",
    }, sqlc.New) // sqlc.New is your SQLC-generated constructor

    if err != nil {
        panic(err)
    }

    // Use queries...
}

Dependency Injection with Dig

Integration with uber-go/dig for dependency injection:

err := errors.Join(
    // Database configuration
    digutil.ProvideValue[pgutil.URI](c, "postgres://user:pass@localhost/mydb"),
    digutil.ProvideValue[pgutil.EnableTracing](c, true),
    digutil.ProvideValue[pgutil.Schema](c, "my_app"),
    digutil.ProvideValue[pgutil.MigrationFS](c, pgutil.MigrationFS(sqlc.MigrationsFS)),

    // Providers
    c.Provide(pgutil.NewPool, dig.As(new(sqlc.DBTX))),
    c.Provide(sqlc.New),

    // Run migrations
    c.Invoke(pgutil.Migrate),
)

Transaction Support

Execute database operations within transactions:

// Execute in transaction using the simple Tx function
err := pgutil.Tx(ctx, pool, func(tx pgx.Tx) error {
    // Create queries instance with transaction
    qtx := queries.WithTx(tx)

    // All operations within this function are transactional
    user, err := qtx.CreateUser(ctx, sqlc.CreateUserParams{...})
    if err != nil {
        return err
    }

    _, err = qtx.CreatePost(ctx, sqlc.CreatePostParams{
        UserID: user.ID,
        // ...
    })
    return err
})

Connection Hijacking

For advanced use cases requiring dedicated connections (advisory locks, prepared statements):

// Get dedicated connection for advisory locks, prepared statements, etc.
conn, closer, err := pgutil.Hijack(ctx, pool)
if err != nil {
    return err
}
defer closer()

// Create queries instance with dedicated connection
dedicatedQueries := sqlc.New(conn)
// Use dedicatedQueries with exclusive connection

Configuration Templates

The package provides standard SQLC configuration templates. Copy the template to your project:

cp pkg/pgutil/templates/sqlc.yaml pkg/dal/sqlc/sqlc.yaml

The template includes:

  • PostgreSQL with pgx/v5 driver
  • JSON tags with camelCase style
  • Proper UUID and timestamp handling
  • Null-safe type generation

Repeatable Migrations

The pgutil package supports repeatable migrations alongside traditional versioned migrations. Repeatable migrations are ideal for views, functions, procedures, and reference data that may need to be recreated or updated when the underlying logic changes.

## File Naming Convention

Repeatable migration files must follow the naming pattern: R_<description>.sql Examples:

  • R_001_user_view.sql
  • R_002_lookup_data.sql
  • R_003_reporting_functions.sql

## Migration Process

1. Normal migrations run first (using golang-migrate/migrate library) 2. Repeatable migrations run after normal migrations complete successfully 3. Each repeatable migration is tracked by filename and SHA256 hash in schema_migrations_repeatable table 4. Files are only re-executed if their content has changed (detected via hash comparison) 5. All repeatable migrations execute within individual transactions for safety

## Example Directory Structure

migrations/
├── 000001_initial_schema.up.sql    # Normal migration
├── 000001_initial_schema.down.sql  # Normal migration
├── 000002_add_users_table.up.sql   # Normal migration
├── 000002_add_users_table.down.sql # Normal migration
├── R_001_user_stats_view.sql       # Repeatable migration
└── R_002_seed_lookup_data.sql      # Repeatable migration

## Example Repeatable Migration Content

-- R_001_user_stats_view.sql
CREATE OR REPLACE VIEW user_stats AS
SELECT
    user_id,
    COUNT(*) as total_orders,
    SUM(amount) as total_spent
FROM orders
GROUP BY user_id;

## Usage with MigrateWithEmbeddedFS

No changes to your existing code are needed. The function automatically handles both types:

//go:embed migrations/*.sql
var migrationsFS embed.FS

err := pgutil.MigrateWithEmbeddedFS(ctx, uri, "my_app", migrationsFS, "migrations")
// This will run normal migrations first, then repeatable migrations

For a complete working example, see the examples/full directory which demonstrates all pgutil features in a real application.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrRepeatableMigrationFailed = errors.New("repeatable migration failed")
	ErrInvalidRepeatableFile     = errors.New("invalid repeatable file")
)

Functions

func BuildURI

func BuildURI(base, username, password string) (string, error)

BuildURI constructs a PostgreSQL connection URI with credentials. This replaces the identical URI() function duplicated across all projects.

Parameters:

  • base: Base connection URI (e.g., "postgres://localhost:5432/mydb")
  • username: Database username
  • password: Database password

Returns a complete URI with embedded credentials.

Example usage:

uri, err := pgutil.BuildURI("postgres://localhost:5432/mydb", "user", "pass")
// Returns: "postgres://user:pass@localhost:5432/mydb"

func Hijack

func Hijack(ctx context.Context, pool *pgxpool.Pool) (*pgx.Conn, func(), error)

Hijack acquires a dedicated connection from the pool for exclusive use. The returned closer function must be called to release the connection back to the pool.

This is useful for operations that require connection-level state, such as:

  • Advisory locks
  • Prepared statements
  • Connection-specific settings

Example usage:

conn, closer, err := pgutil.Hijack(ctx, pool)
if err != nil {
	return err
}
defer closer()
queries := sqlc.New(conn)
// Use queries with dedicated connection

func Migrate

func Migrate(ctx context.Context, uri URI, schema Schema, migrationsFS MigrationFS) error

Migrate runs database migrations using an embedded filesystem.

Parameters:

  • ctx: Context for cancellation
  • uri: PostgreSQL connection URI
  • schemaName: Name of the schema to create (e.g., "llm_gateway", "knowledge_base_bot")
  • migrationsFS: Embedded filesystem containing migration files
  • migrationsDir: Directory path within the embedded FS (e.g., "migrations")

func NewPool

func NewPool(ctx context.Context, uri URI, schema Schema, enableTracing digutil.Optional[EnableTracing]) (*pgxpool.Pool, error)

NewPool creates a new PostgreSQL connection pool using typed parameters

func ParseCredentials

func ParseCredentials(uri string) (username, password string, err error)

ParseCredentials extracts username and password from a PostgreSQL URI. This is useful for separating credentials from connection details.

Returns empty strings if no credentials are present in the URI.

func Tx

func Tx(ctx context.Context, pool *pgxpool.Pool, fn func(tx pgx.Tx) error) error

Tx executes a function within a database transaction. If the function returns an error, the transaction is rolled back. Otherwise, the transaction is committed.

This function is designed to work with any SQLC-generated queries struct that has a WithTx method.

Example usage:

err := pgutil.Tx(ctx, pool, func(tx pgx.Tx) error {
	qtx := queries.WithTx(tx)
	// Use qtx for transactional operations
	return nil
})

Types

type EnableTracing

type EnableTracing bool

EnableTracing represents whether to enable DataDog tracing for dependency injection

type MigrationFS

type MigrationFS embed.FS

MigrationFS represents an embedded filesystem containing migration files for dependency injection

type Schema

type Schema string

Schema represents a database schema name for dependency injection

type URI

type URI string

URI represents a PostgreSQL connection string for dependency injection

Jump to

Keyboard shortcuts

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