package postgres import ( "errors" "fmt" "net/url" "drive.local/drivev2/db/migrations" "github.com/golang-migrate/migrate/v4" pgx5migrate "github.com/golang-migrate/migrate/v4/database/pgx/v5" "github.com/golang-migrate/migrate/v4/source/iofs" ) // Migrate applies every pending forward migration using an advisory lock. func Migrate(databaseURL string) (returnErr error) { sourceDriver, err := iofs.New(migrations.Files, ".") if err != nil { return fmt.Errorf("open embedded migrations: %w", err) } migrationURL, err := url.Parse(databaseURL) if err != nil { return fmt.Errorf("parse database URL for migrations: %w", err) } migrationURL.Scheme = "pgx5" query := migrationURL.Query() query.Set("x-multi-statement", "true") query.Set("x-statement-timeout", "30000") if query.Get("connect_timeout") == "" { query.Set("connect_timeout", "30") } migrationURL.RawQuery = query.Encode() databaseDriver, err := (&pgx5migrate.Postgres{}).Open(migrationURL.String()) if err != nil { return fmt.Errorf("open migration database: %w", err) } runner, err := migrate.NewWithInstance("iofs", sourceDriver, "pgx5", databaseDriver) if err != nil { return errors.Join( fmt.Errorf("create migration runner: %w", err), sourceDriver.Close(), databaseDriver.Close(), ) } defer func() { sourceErr, databaseErr := runner.Close() returnErr = errors.Join(returnErr, sourceErr, databaseErr) }() if err := runner.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) { return fmt.Errorf("apply database migrations: %w", err) } return nil }