| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- package database
- import (
- "database/sql"
- "embed"
- "fmt"
- "log/slog"
- "github.com/golang-migrate/migrate/v4"
- "github.com/golang-migrate/migrate/v4/database/sqlite3"
- "github.com/golang-migrate/migrate/v4/source/iofs"
- _ "github.com/mattn/go-sqlite3"
- )
- //go:embed migrations/*.sql
- var migrations embed.FS
- func Open(dbPath string) (*sql.DB, error) {
- connStr := dbPath + "?_journal_mode=WAL&_foreign_keys=1"
- migrationDb, err := sql.Open("sqlite3", connStr)
- if err != nil {
- return nil, fmt.Errorf("open db for migrations: %w", err)
- }
- if err := migrationDb.Ping(); err != nil {
- migrationDb.Close()
- return nil, fmt.Errorf("ping db: %w", err)
- }
- if err := runMigrations(migrationDb); err != nil {
- migrationDb.Close()
- return nil, fmt.Errorf("run migrations: %w", err)
- }
- migrationDb.Close()
- db, err := sql.Open("sqlite3", connStr)
- if err != nil {
- return nil, fmt.Errorf("open db: %w", err)
- }
- if err := db.Ping(); err != nil {
- db.Close()
- return nil, fmt.Errorf("ping db: %w", err)
- }
- return db, nil
- }
- func runMigrations(db *sql.DB) error {
- sourceDriver, err := iofs.New(migrations, "migrations")
- if err != nil {
- return fmt.Errorf("create migration source: %w", err)
- }
- driver, err := sqlite3.WithInstance(db, &sqlite3.Config{})
- if err != nil {
- return fmt.Errorf("create database driver: %w", err)
- }
- m, err := migrate.NewWithInstance("iofs", sourceDriver, "sqlite3", driver)
- if err != nil {
- return fmt.Errorf("create migrate instance: %w", err)
- }
- defer m.Close()
- currentVersion, _, err := m.Version()
- if err != nil && err != migrate.ErrNilVersion {
- return fmt.Errorf("get current migration version: %w", err)
- }
- if err == migrate.ErrNilVersion {
- slog.Info("running database migrations", "currentVersion", "none")
- } else {
- slog.Info("running database migrations", "currentVersion", currentVersion)
- }
- upErr := m.Up()
- if upErr != nil && upErr != migrate.ErrNoChange {
- return fmt.Errorf("run migrations: %w", upErr)
- }
- newVersion, dirty, versionErr := m.Version()
- if versionErr != nil && versionErr != migrate.ErrNilVersion {
- return fmt.Errorf("get migration version after execution: %w", versionErr)
- }
- if upErr == migrate.ErrNoChange {
- slog.Info("no pending migrations", "currentVersion", currentVersion)
- } else if dirty {
- slog.Warn("migration completed but database is in dirty state", "version", newVersion)
- } else {
- if err == migrate.ErrNilVersion {
- slog.Info("migrations completed", "appliedVersion", newVersion)
- } else if currentVersion != newVersion {
- slog.Info("migrations completed", "fromVersion", currentVersion, "toVersion", newVersion)
- } else {
- slog.Info("migrations completed", "version", newVersion)
- }
- }
- return nil
- }
|