// Package db holds the embedded golang-migrate migrations and the sqlc // generated query code (internal/db/sqlcgen). package db import ( "embed" "errors" "fmt" "net/url" "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/pgx/v5" "github.com/golang-migrate/migrate/v4/source/iofs" ) //go:embed migrations/*.sql var migrationsFS embed.FS // newMigrate builds a *migrate.Migrate over the embedded migration files and // the given database connection string. func newMigrate(databaseURL string) (*migrate.Migrate, error) { sourceDriver, err := iofs.New(migrationsFS, "migrations") if err != nil { return nil, fmt.Errorf("db: loading embedded migrations: %w", err) } m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, wrapDatabaseURL(databaseURL)) if err != nil { return nil, fmt.Errorf("db: initializing migrate: %w", err) } return m, nil } // wrapDatabaseURL swaps the postgres(ql):// scheme for pgx5:// as required // by golang-migrate's pgx/v5 database driver registration. func wrapDatabaseURL(databaseURL string) string { u, err := url.Parse(databaseURL) if err != nil { return databaseURL } u.Scheme = "pgx5" return u.String() } // MigrateUp applies all pending migrations. It returns nil if there is // nothing to do. func MigrateUp(databaseURL string) error { m, err := newMigrate(databaseURL) if err != nil { return err } defer func() { _, _ = m.Close() }() if err := m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) { return fmt.Errorf("db: migrate up: %w", err) } return nil } // MigrateDown rolls back all migrations. It returns nil if there is nothing // to do. func MigrateDown(databaseURL string) error { m, err := newMigrate(databaseURL) if err != nil { return err } defer func() { _, _ = m.Close() }() if err := m.Down(); err != nil && !errors.Is(err, migrate.ErrNoChange) { return fmt.Errorf("db: migrate down: %w", err) } return nil }