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 ¶
- func AcquireTableLock(ctx context.Context, db *sql.DB, config TableLockConfig, ...) error
- func GenerateOwnerID() (string, error)
- func ParseTimeISO8601(src interface{}) (time.Time, error)
- func PlaceholderDollar(n int) string
- func PlaceholderQuestion(n int) string
- func QuoteBackticks(name string) string
- func QuoteBrackets(name string) string
- func QuoteDoubleQuotes(name string) string
- func QuoteIdentifier(name string, quoteChar QuoteChar) string
- type Config
- type Driver
- func (d *Driver) Close() error
- func (d *Driver) Exec(ctx context.Context, isolationLevel sql.IsolationLevel, fn func(*sql.Tx) error) error
- func (d *Driver) GetApplied(ctx context.Context) ([]queen.Applied, error)
- func (d *Driver) Record(ctx context.Context, m *queen.Migration) error
- func (d *Driver) Remove(ctx context.Context, version string) error
- type QuoteChar
- type TableLockConfig
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
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 ¶
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 ¶
PlaceholderDollar creates placeholders in the format $1, $2, $3... Used by PostgreSQL and CockroachDB.
func PlaceholderQuestion ¶
PlaceholderQuestion creates placeholders in the format ?, ?, ?... Used by MySQL, SQLite, and ClickHouse.
func QuoteBackticks ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
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.