pgmq-migration-0.1.2.0: src/Pgmq/Migration.hs
-- | PGMQ schema migration support
--
-- This module provides functions to install the PGMQ schema into PostgreSQL
-- without requiring the pgmq extension. It uses hasql-migration for tracking
-- applied migrations.
--
-- == Fresh Installation
--
-- For new projects, use 'migrate' to install the complete PGMQ schema:
--
-- @
-- import Hasql.Connection (acquire)
-- import Hasql.Session (run)
-- import Pgmq.Migration (migrate)
--
-- main :: IO ()
-- main = do
-- Right conn <- acquire "host=localhost dbname=mydb"
-- result <- run migrate conn
-- case result of
-- Right (Right ()) -> putStrLn "Migration successful"
-- Right (Left err) -> print err
-- Left sessionErr -> print sessionErr
-- @
--
-- == Upgrading Existing Installations
--
-- For projects that previously installed PGMQ via this package (e.g., at v1.10.0),
-- use 'upgrade' to apply only the incremental changes:
--
-- @
-- import Hasql.Connection (acquire)
-- import Hasql.Session (run)
-- import Pgmq.Migration (upgrade)
--
-- main :: IO ()
-- main = do
-- Right conn <- acquire "host=localhost dbname=mydb"
-- result <- run upgrade conn
-- case result of
-- Right (Right ()) -> putStrLn "Upgrade successful"
-- Right (Left err) -> print err
-- Left sessionErr -> print sessionErr
-- @
--
-- The hasql-migration library tracks which migrations have been applied,
-- so 'upgrade' will only apply migrations that haven't run yet.
--
-- == Which Function Should I Use?
--
-- * __New project, fresh database__: Use 'migrate'
-- * __Existing project using pgmq-migration__: Use 'upgrade'
-- * __Not sure__: Use 'upgrade' - it's safe on fresh databases too
-- (it will apply all needed migrations)
module Pgmq.Migration
( -- * Migration Operations
migrate,
upgrade,
validate,
getMigrations,
-- * Migration Info
version,
migrations,
upgradeMigrations,
-- * Re-exports
MigrationCommand,
MigrationError (..),
SchemaMigration (..),
)
where
import Control.Monad (foldM)
import Hasql.Migration (MigrationCommand, MigrationError (..), SchemaMigration (..))
import Hasql.Migration qualified as Migration
import Hasql.Session (Session)
import Pgmq.Migration.Migrations qualified as Migrations
import Pgmq.Migration.Sessions qualified as Sessions
-- | Current version of the PGMQ schema
version :: String
version = Migrations.version
-- | Full migration commands for the current version.
--
-- Use this for fresh installations. Applies the complete schema.
migrations :: [MigrationCommand]
migrations = Migrations.migrations
-- | Incremental upgrade migrations.
--
-- Use this to upgrade existing installations that were set up via this package.
-- Contains only the delta migrations (e.g., v1.10.0 -> v1.10.1 -> v1.11.0).
upgradeMigrations :: [MigrationCommand]
upgradeMigrations = Migrations.upgradeMigrations
-- | Run all PGMQ migrations for a fresh installation.
--
-- This function is idempotent - it will only run migrations that haven't
-- been applied yet. Returns 'Left' 'MigrationError' if a migration fails.
--
-- Use this for new projects with a fresh database.
migrate :: Session (Either MigrationError ())
migrate = runMigrations migrations
-- | Run upgrade migrations for existing installations.
--
-- This function applies only the incremental migrations needed to upgrade
-- from a previous version installed via this package (e.g., v1.10.0 -> v1.10.1 -> v1.11.0).
--
-- It is idempotent and safe to run on any database - migrations that have
-- already been applied will be skipped.
--
-- Use this for existing projects that need to upgrade their PGMQ schema.
upgrade :: Session (Either MigrationError ())
upgrade = runMigrations upgradeMigrations
-- | Run a list of migrations
runMigrations :: [MigrationCommand] -> Session (Either MigrationError ())
runMigrations cmds = foldM runIfOk (Right ()) cmds
where
runIfOk :: Either MigrationError () -> MigrationCommand -> Session (Either MigrationError ())
runIfOk (Left err) _ = pure (Left err)
runIfOk (Right ()) cmd = Sessions.runMigrationSession cmd
-- | Validate all migrations without applying them
--
-- Checks that previously applied migrations haven't changed.
-- Returns a list of validation errors, or an empty list if validation passes.
validate :: Session [MigrationError]
validate = do
results <- mapM validateOne validationCommands
pure $ concat results
where
validationCommands = map Migration.MigrationValidation migrations
validateOne :: MigrationCommand -> Session [MigrationError]
validateOne cmd = do
result <- Sessions.runMigrationSession cmd
pure $ case result of
Left err -> [err]
Right () -> []
-- | Get all applied migrations
getMigrations :: Session [SchemaMigration]
getMigrations = Sessions.getMigrationsSession