Drivers¶
Queen supports multiple databases through a driver interface. All drivers implement the same API.
Supported Databases¶
| Database | Package | Locking | Transactional DDL |
|---|---|---|---|
| PostgreSQL | drivers/postgres |
Advisory locks | Yes |
| MySQL | drivers/mysql |
GET_LOCK | Partial (InnoDB) |
| SQLite | drivers/sqlite |
Exclusive locks | Yes |
| ClickHouse | drivers/clickhouse |
Table-based with TTL | No |
| CockroachDB | drivers/cockroachdb |
Table-based distributed | Yes |
| MS SQL Server | drivers/mssql |
sp_getapplock | Partial |
All drivers also support NewWithTableName(db, tableName) for custom migration table names.
Note
The CLI tool supports only PostgreSQL, MySQL, SQLite, and ClickHouse. CockroachDB and MS SQL Server are available as Go driver packages but cannot be used with the queen CLI directly.
PostgreSQL¶
Driver packages (drivers/postgres, drivers/mysql, etc.) are included with the main github.com/honeynil/queen module — no separate go get needed.
import (
"database/sql"
"github.com/honeynil/queen/drivers/postgres"
_ "github.com/jackc/pgx/v5/stdlib"
)
db, _ := sql.Open("pgx", "postgres://localhost/mydb?sslmode=disable")
driver := postgres.New(db)
q := queen.New(driver)
Custom table name:
Connection String¶
| Option | Values |
|---|---|
sslmode |
disable, require, verify-full |
connect_timeout |
seconds |
application_name |
string |
search_path |
public,other |
Features¶
- Transactional DDL -- All DDL is transactional. Failed index creation rolls back table creation.
- Advisory locks --
pg_advisory_lockprevents concurrent migrations. - Schema support -- Custom schemas via
search_path. - Concurrent indexes --
CREATE INDEX CONCURRENTLYsupported (runs outside transaction).
PostgreSQL-Specific Patterns¶
// JSONB column
UpSQL: `ALTER TABLE users ADD COLUMN metadata JSONB DEFAULT '{}'::jsonb`
// Trigger
UpSQL: `
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
`
// Partitioning
UpSQL: `
CREATE TABLE events (
id BIGSERIAL, created_at TIMESTAMP NOT NULL, data JSONB
) PARTITION BY RANGE (created_at);
`
// Concurrent index (non-transactional)
UpSQL: `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`
MySQL¶
import (
"database/sql"
"github.com/honeynil/queen/drivers/mysql"
_ "github.com/go-sql-driver/mysql"
)
db, _ := sql.Open("mysql", "user:password@tcp(localhost:3306)/mydb?parseTime=true")
driver := mysql.New(db)
q := queen.New(driver)
Connection String¶
| Option | Values |
|---|---|
parseTime |
true (recommended) |
charset |
utf8mb4 (recommended) |
collation |
utf8mb4_unicode_ci |
tls |
true, false, skip-verify |
Features¶
- GET_LOCK -- Named locks for migration coordination.
- JSON columns -- MySQL 5.7+ JSON support.
- InnoDB transactions -- Transactional DDL for some operations.
Limitations¶
Many DDL operations are not transactional. ALTER TABLE commits the current transaction. Queen handles each DDL statement separately.
MySQL-Specific Patterns¶
// Always use InnoDB
UpSQL: `CREATE TABLE users (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
// JSON column
UpSQL: `ALTER TABLE users ADD COLUMN metadata JSON`
// Online index creation (MySQL 8.0+)
UpSQL: `CREATE INDEX idx_posts_user_id ON posts(user_id) ALGORITHM=INPLACE, LOCK=NONE`
// Instant column add (MySQL 8.0+)
UpSQL: `ALTER TABLE users ADD COLUMN status VARCHAR(50) DEFAULT 'active', ALGORITHM=INSTANT`
SQLite¶
import (
"database/sql"
"github.com/honeynil/queen/drivers/sqlite"
_ "github.com/mattn/go-sqlite3"
)
db, _ := sql.Open("sqlite3", "myapp.db")
db.SetMaxOpenConns(1) // SQLite limitation
driver := sqlite.New(db)
q := queen.New(driver)
Connection String¶
myapp.db # File database
:memory: # In-memory (testing)
file:myapp.db?_foreign_keys=on&_journal_mode=WAL # URI format
| Option | Values |
|---|---|
_foreign_keys |
on, off |
_journal_mode |
DELETE, WAL, MEMORY |
_busy_timeout |
milliseconds |
mode |
ro, rw, rwc, memory |
Features¶
- Transactional DDL -- All DDL is transactional.
- WAL mode -- Better concurrency with Write-Ahead Logging.
- FTS5 -- Full-text search support.
- JSON support -- SQLite 3.38+ JSON functions.
Recommended Setup¶
db.Exec("PRAGMA foreign_keys = ON")
db.Exec("PRAGMA journal_mode = WAL")
db.Exec("PRAGMA busy_timeout = 5000")
db.SetMaxOpenConns(1)
Limitations¶
Limited ALTER TABLE: no MODIFY COLUMN, no DROP CONSTRAINT. Workaround: create new table, copy data, drop old, rename new.
Only one writer at a time. Use WAL mode and busy timeout to mitigate.
ClickHouse¶
import (
"database/sql"
"github.com/honeynil/queen/drivers/clickhouse"
_ "github.com/ClickHouse/clickhouse-go/v2"
)
db, _ := sql.Open("clickhouse", "clickhouse://localhost:9000/default")
driver, err := clickhouse.New(db)
if err != nil {
log.Fatal(err)
}
q := queen.New(driver)
Warning
The ClickHouse constructor returns an error. Always check it.
Connection String¶
Features¶
- MergeTree family -- MergeTree, ReplacingMergeTree, SummingMergeTree.
- Distributed tables -- Cluster support with
ON CLUSTER. - Materialized views -- Automatic aggregation.
- TTL -- Automatic data expiration.
- Table-based locking -- Distributed lock with TTL for migration coordination.
Limitations¶
No transactional DDL. No foreign keys. Eventual consistency in distributed setups. Each DDL statement commits immediately.
ClickHouse-Specific Patterns¶
// MergeTree with partitioning
UpSQL: `
CREATE TABLE events (
timestamp DateTime, user_id UInt64, data String
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id)
PARTITION BY toYYYYMM(timestamp)
`
// TTL
UpSQL: `ALTER TABLE events MODIFY TTL timestamp + INTERVAL 90 DAY`
// Distributed table
UpSQL: `
CREATE TABLE events ON CLUSTER '{cluster}' AS events_local
ENGINE = Distributed('{cluster}', default, events_local, rand())
`
CockroachDB¶
CockroachDB uses the same pgx driver as PostgreSQL:
import (
"database/sql"
"github.com/honeynil/queen/drivers/cockroachdb"
_ "github.com/jackc/pgx/v5/stdlib"
)
db, _ := sql.Open("pgx", "postgresql://root@localhost:26257/myapp?sslmode=disable")
driver, err := cockroachdb.New(db)
if err != nil {
log.Fatal(err)
}
q := queen.New(driver)
Warning
The CockroachDB constructor returns an error. Always check it.
Features¶
- Distributed SQL -- Automatic data distribution across nodes.
- Transactional DDL -- Full transaction support.
- Table-based locking -- Distributed lock mechanism (not advisory locks).
- Multi-region -- Regional and global table locality.
- JSONB and arrays -- PostgreSQL type compatibility.
Recommendations¶
- Use
UUIDfor primary keys (notSERIAL-- creates hotspots). - Use
STRINGinstead ofVARCHAR. - Set table locality for multi-region deployments.
UpSQL: `
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email STRING UNIQUE NOT NULL
)
`
MS SQL Server¶
import (
"database/sql"
"github.com/honeynil/queen/drivers/mssql"
_ "github.com/microsoft/go-mssqldb"
)
db, _ := sql.Open("sqlserver", "sqlserver://user:pass@localhost:1433?database=myapp")
driver := mssql.New(db)
q := queen.New(driver)
Connection String¶
Features¶
- sp_getapplock -- Application-level locking.
- Temporal tables -- System-versioned tables.
- Columnstore indexes -- Analytics optimization.
- Full-text search -- Built-in FTS.
- Azure SQL -- Managed Identity support.
Recommendations¶
- Use
NVARCHARfor Unicode text. - Use
DATETIME2instead ofDATETIME. - Use explicit schema names (
dbo.users).
Mock Driver¶
For testing without a real database.
No extra dependencies needed — the mock driver is part of the Queen module.
The mock driver stores migrations in memory. Use it in unit tests to verify migration logic without database dependencies.
func TestMigrations(t *testing.T) {
driver := mock.New()
q := queen.NewTest(t, driver)
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id INT)`,
DownSQL: `DROP TABLE users`,
})
q.TestUpDown()
}
Custom Driver¶
Implement the Driver interface:
type Driver interface {
Init(ctx context.Context) error
GetApplied(ctx context.Context) ([]Applied, error)
Record(ctx context.Context, m *Migration, meta *MigrationMetadata) error
Remove(ctx context.Context, version string) error
Lock(ctx context.Context, timeout time.Duration) error
Unlock(ctx context.Context) error
Exec(ctx context.Context, isolationLevel sql.IsolationLevel, fn func(*sql.Tx) error) error
Close() error
}
Initcreates the migration table if it does not exist.GetAppliedreturns all applied migration records.Recordinserts a migration record after successful execution.Removedeletes a migration record after rollback.Lock/Unlockprovide migration concurrency control.Execruns a function within a transaction at the specified isolation level.
See API Reference for the full Applied and MigrationMetadata types.