base

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package base provides common functionality for Queen database drivers.

This package contains shared code to reduce duplication across different database drivers (PostgreSQL, MySQL, SQLite, ClickHouse, CockroachDB).

The base package provides:

  • Transaction management (Exec)
  • Connection lifecycle (Close)
  • Common migration operations (GetApplied, Record, Remove)
  • SQL identifier quoting strategies
  • Placeholder formatting strategies

Note: The base driver does not manage dedicated database connections for locking. Some databases (e.g. MySQL with GET_LOCK) require a dedicated connection for locking. In such cases, the concrete driver (e.g., mysql.Driver) is responsible for managing its own *sql.Conn for the lifetime of the lock.

drivers/base/owner.go

drivers/base/quote.go

drivers/base/tablelock.go

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AcquireTableLock added in v0.2.0

func AcquireTableLock(ctx context.Context, db *sql.DB, config TableLockConfig, lockKey, ownerID string, timeout time.Duration) error

AcquireTableLock implements distributed locking using a lock table.

This function is intended for databases without native advisory locks (e.g., ClickHouse, CockroachDB).

The lockKey identifies which lock to acquire (e.g., "migration_lock"). The ownerID uniquely identifies the process acquiring the lock, preventing one process from unlocking another process's lock.

Lock mechanism: 1. Cleans up expired locks using CleanupQuery (best effort, ignores errors) 2. Checks if an active lock exists using CheckQuery and ScanFunc 3. If no active lock exists, attempts to insert a new lock record using InsertQuery 4. Retries with exponential backoff until the lock is acquired or timeout is reached

Query parameters: - CleanupQuery: receives (lockKey, expiresAt) - CheckQuery: receives (lockKey) - InsertQuery: receives (lockKey, expiresAt, ownerID)

Exponential backoff: - Starts at 50ms and doubles after each retry - Maximum backoff is 1 second

If the lock cannot be acquired within the timeout, returns queen.ErrLockTimeout.

func GenerateOwnerID added in v0.2.0

func GenerateOwnerID() (string, error)

GenerateOwnerID generates a unique owner identifier for lock ownership tracking.

This ID is used to prevent race conditions where one process unlocks another process's lock. Each Driver instance gets a unique owner ID when created.

The owner ID is a cryptographically secure random string of 32 hexadecimal characters (16 random bytes encoded as hex). This provides sufficient uniqueness across processes, containers, and hosts without requiring external dependencies.

Returns an error if the system's random number generator fails.

Example usage:

ownerID, err := base.GenerateOwnerID()
if err != nil {
    return fmt.Errorf("failed to generate owner ID: %w", err)
}

func ParseTimeISO8601

func ParseTimeISO8601(src interface{}) (time.Time, error)

ParseTimeISO8601 parses time from ISO8601 string format. Used by SQLite which stores timestamps as TEXT.

SQLite default format: "YYYY-MM-DD HH:MM:SS"

func PlaceholderDollar

func PlaceholderDollar(n int) string

PlaceholderDollar creates placeholders in the format $1, $2, $3... Used by PostgreSQL and CockroachDB.

func PlaceholderQuestion

func PlaceholderQuestion(n int) string

PlaceholderQuestion creates placeholders in the format ?, ?, ?... Used by MySQL, SQLite, and ClickHouse.

func QuoteBackticks

func QuoteBackticks(name string) string

QuoteBackticks is a convenience wrapper for QuoteIdentifier with Backtick.

Used by MySQL.

Examples:

QuoteBackticks("users")      -> `users`
QuoteBackticks("my`table")   -> `my``table`

func QuoteBrackets added in v0.2.0

func QuoteBrackets(name string) string

QuoteBrackets is a convenience wrapper for QuoteIdentifier with Bracket.

Used by MS SQL Server.

Examples:

QuoteBrackets("users")      -> [users]
QuoteBrackets("my]table")   -> [my]]table]

func QuoteDoubleQuotes

func QuoteDoubleQuotes(name string) string

QuoteDoubleQuotes is a convenience wrapper for QuoteIdentifier with DoubleQuote.

Used by PostgreSQL, SQLite, ClickHouse, and CockroachDB.

Examples:

QuoteDoubleQuotes("users")       -> "users"
QuoteDoubleQuotes(`my"table`)   -> "my""table"

func QuoteIdentifier

func QuoteIdentifier(name string, quoteChar QuoteChar) string

QuoteIdentifier escapes and wraps a SQL identifier using the provided quote character.

It prevents SQL injection by doubling any existing quote characters inside the identifier.

Supported quote characters:

  • DoubleQuote (") for PostgreSQL, SQLite, ClickHouse, CockroachDB
  • Backtick (`) for MySQL
  • Bracket ([]) for MS SQL Server

Examples:

QuoteIdentifier("users", DoubleQuote)     -> "users"
QuoteIdentifier(`my"table`, DoubleQuote) -> "my""table"
QuoteIdentifier("users", Backtick)        -> `users`
QuoteIdentifier("users", Bracket)         -> [users]

Use this function whenever constructing SQL queries with dynamic table or column names.

Types

type Config

type Config struct {
	// Placeholder generates SQL placeholders for the n-th argument (1-based).
	// PostgreSQL/CockroachDB: func(n int) string { return fmt.Sprintf("$%d", n) }
	// MySQL/SQLite/ClickHouse: func(n int) string { return "?" }
	Placeholder func(n int) string

	// QuoteIdentifier escapes SQL identifiers (table names, column names).
	// PostgreSQL/SQLite/ClickHouse/CockroachDB: double quotes
	// MySQL: backticks
	QuoteIdentifier func(name string) string

	// ParseTime parses time from query result (optional).
	// Most drivers: nil (use standard scanning)
	// SQLite: parses from ISO8601 string
	ParseTime func(src interface{}) (time.Time, error)
}

Config contains configuration for the base driver. Each concrete driver provides these strategies to customize behavior.

type Driver

type Driver struct {
	DB        *sql.DB
	TableName string
	Config    Config
}

Driver provides a base implementation of common queen.Driver methods.

Concrete drivers should embed this type and implement:

  • Init() - database-specific schema creation
  • Lock()/Unlock() - database-specific locking mechanisms if needed

Note: The base driver works with a standard *sql.DB connection pool and does not manage dedicated connections. Some databases (e.g., MySQL with GET_LOCK) require a dedicated connection for certain operations. In those cases, the concrete driver (e.g., mysql.Driver) should manage its own *sql.Conn for the lifetime of such operations.

func (*Driver) Close

func (d *Driver) Close() error

Close closes the database connection.

Identical implementation for all database drivers.

func (*Driver) Exec

func (d *Driver) Exec(ctx context.Context, isolationLevel sql.IsolationLevel, fn func(*sql.Tx) error) error

Exec executes a function within a transaction with the specified isolation level.

If the function returns an error, the transaction is rolled back. Otherwise, the transaction is committed.

The isolationLevel parameter controls transaction isolation:

  • sql.LevelDefault: use database default
  • sql.LevelReadUncommitted: allow dirty reads
  • sql.LevelReadCommitted: prevent dirty reads
  • sql.LevelRepeatableRead: prevent non-repeatable reads
  • sql.LevelSerializable: full isolation

This provides ACID guarantees for migration execution. Identical implementation for all database drivers.

func (*Driver) GetApplied

func (d *Driver) GetApplied(ctx context.Context) ([]queen.Applied, error)

GetApplied returns all applied migrations sorted by applied_at in ascending order.

Uses the QuoteIdentifier strategy for SQL identifier escaping. Uses the optional ParseTime strategy for SQLite compatibility.

func (*Driver) Record

func (d *Driver) Record(ctx context.Context, m *queen.Migration) error

Record marks a migration as applied in the database.

Uses Placeholder and QuoteIdentifier strategies to generate database-specific SQL queries.

func (*Driver) Remove

func (d *Driver) Remove(ctx context.Context, version string) error

Remove removes a migration record from the database (for rollback).

Uses Placeholder and QuoteIdentifier strategies to generate database-specific SQL queries.

type QuoteChar

type QuoteChar string

QuoteChar represents a SQL identifier quote character.

const (
	DoubleQuote QuoteChar = `"`  // PostgreSQL, SQLite, ClickHouse, CockroachDB
	Backtick    QuoteChar = "`"  // MySQL
	Bracket     QuoteChar = `[]` // MS SQL Server
)

type TableLockConfig added in v0.2.0

type TableLockConfig struct {
	// CleanupQuery removes expired locks from the table.
	// For ClickHouse this can be ALTER TABLE DELETE (async),
	// for CockroachDB DELETE FROM.
	CleanupQuery string

	// CheckQuery checks for the existence of an active lock.
	// For ClickHouse, FINAL must be used with ReplacingMergeTree to ensure
	// deduplicated results, accounting for async merges.
	// For CockroachDB, a simple SELECT with LIMIT 1 is sufficient.
	CheckQuery string

	// InsertQuery inserts a new lock entry.
	InsertQuery string

	// ScanFunc processes the result of CheckQuery and returns true if
	// a lock exists, false otherwise. Returns an error if SQL execution failed.
	ScanFunc func(*sql.Row) (bool, error)
}

TableLockConfig configures table-based distributed locking for databases that do not support native advisory locks.

This is used by ClickHouse, CockroachDB, and other databases where distributed locks are implemented via a dedicated table with keys and expiration times.

Jump to

Keyboard shortcuts

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