packages feed

postgresql-migration-persistent (empty) → 1.0.0

raw patch · 5 files changed

+303/−0 lines, 5 filesdep +basedep +mtldep +persistent

Dependencies added: base, mtl, persistent, persistent-postgresql, postgresql-migration, postgresql-simple, resource-pool, text

Files

+ Changelog.md view
@@ -0,0 +1,10 @@+# Change log for postgresql-migration-persistent project++## Version 1.0.0+import the code+++## Version 0.0.0 ++import [template](https://github.com/jappeace/template).+
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2025 Jappie Klooster++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Readme.md view
@@ -0,0 +1,83 @@++> Don't you *ever* trust computers.+> +> \- first rule of programist bible.+++This library combines [`postgresql-migration`](https://hackage.haskell.org/package/postgresql-migration) and [`persistent`](https://hackage.haskell.org/package/persistent),+for the common use case of:+1. Run my manually defined migrations.+2. Check if the schema defined in persistent aligns with the database.+3. If not, rollback and error with the migration plan persistent wants to do.++## Usage++It could for example look something like this with katip logging:+```haskell+import PostgreSQL.Migration.Persistent+import Database.Schema.User()+import Database.Schema.Company()++migrateAll :: Migration+migrateAll = migrateModels $(discoverEntities)++main :: IO ()+main = do+  katipConfig <- mkKatipConfig "server" environment+  let migDir = "migrations/up"+  let migrationOptions = defaultOptions migDir $ \case+        Left errmsg -> runContext katipConfig $ $logTM AlertS $ logStr errmsg+        Right infoMsg -> runContext katipConfig $ $logTM InfoS $ logStr infoMsg++  result <- runMigrations migrationOptions migrateAll rawPool+  runContext katipConfig $ case result of+    MigrationConsistent -> $logTM InfoS "migration consistent"+    MigrationRollbackDueTo rollback -> do+      $logTM ErrorS $ logStr $ errorMessage rollback+      error "invalid migrations"+    MigrationLibraryError err' -> do+      $logTM ErrorS $ logStr err'+      error "migration library error"+    MigrationNotBackedByPg ->+      error "app expects pg backing for migrations to work"+      +  runMyApp+```++By default the migrations are applied in a large transaction,+but you can modify this behavior by overriding options record,+for example:++```haskell+import Database.PostgreSQL.Simple.Migration qualified as Migration++main = do+  ...+  let migrationOptions = defaultOptions migDir $ \case+        Left errmsg -> runContext katipConfig $ $logTM AlertS $ logStr errmsg+        Right infoMsg -> runContext katipConfig $ $logTM InfoS $ logStr infoMsg+  let overridenOptions = migrationOptions { pmoMigrationOptions = (pmoMigrationOptions migrationOptions ) {Migration.optTransactionControl = Migration.TransactionPerStep }}+```++pretty much all options are exposed from the underlying postgresql-migration+library.++### Tools+Enter the nix shell.+```+nix develop+```+You can checkout the makefile to see what's available:+```+cat makefile+```++### Running+```+make run+```++### Fast filewatch which runs tests+```+make ghcid+```
+ postgresql-migration-persistent.cabal view
@@ -0,0 +1,79 @@+cabal-version:      3.0++name:           postgresql-migration-persistent+version:        1.0.0+homepage:       https://github.com/jappeace/postgresql-migration-persistent#readme+bug-reports:    https://github.com/jappeace/postgresql-migration-persistent/issues+author:         Jappie Klooster, Jean-Paul Calderone +maintainer:     hi@jappie.me+synopsis:       A PostgreSQL persistent schema migration utility+description:    Wraps postgresql migration and persistent to make sure the persistent schema+                aligns with what's in the database.+                If not, it returns a list of suggested manual migrations+                to be put in postgresql-migration.+copyright:      2025 Jappie Klooster+license:        MIT+license-file:   LICENSE+build-type:     Simple+category:       Database+extra-source-files:+    Readme.md+    LICENSE+extra-doc-files:+    Changelog.md++source-repository head+  type: git+  location: https://github.com/jappeace/postgresql-migration-persistent++common common-options+  default-extensions: +      EmptyCase+      FlexibleContexts+      FlexibleInstances+      InstanceSigs+      MultiParamTypeClasses+      LambdaCase+      MultiWayIf+      NamedFieldPuns+      TupleSections+      DeriveFoldable+      DeriveFunctor+      DeriveGeneric+      DeriveLift+      DeriveTraversable+      DerivingStrategies+      GeneralizedNewtypeDeriving+      StandaloneDeriving+      OverloadedStrings+      TypeApplications+      NumericUnderscores+      ImportQualifiedPost++  ghc-options:+    -Wall -Wincomplete-uni-patterns+    -Wincomplete-record-updates -Widentities -Wredundant-constraints+    -Wcpp-undef -fwarn-tabs -Wpartial-fields+    -fdefer-diagnostics -Wunused-packages+    -fenable-th-splice-warnings+    -fno-omit-yields+    -threaded ++  build-depends:+      base >=4.9.1.0 && <5+    , postgresql-migration < 0.3+    , persistent-postgresql < 3+    , persistent < 3+    , postgresql-simple < 1+    , text < 3+    , resource-pool < 0.5+    , mtl < 3++  default-language: Haskell2010++library+  import: common-options+  exposed-modules:+      PostgreSQL.Migration.Persistent+  hs-source-dirs:+      src
+ src/PostgreSQL/Migration/Persistent.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE DataKinds #-}++-- | uses Postgres migration to run all found+--   migrations in a transaction.+--   then use persistent to check if the persistent+--   model aligns with what's in the database.+module PostgreSQL.Migration.Persistent+  ( runMigrations,+    PMMigrationResult (..),++    -- * options+    defaultOptions,+    PersistentMigrationOptions (..),+  )+where++import Control.Monad.IO.Class (liftIO)+import Control.Monad.Reader (ReaderT (..), asks)+import Data.Pool (Pool)+import Data.Text (Text)+import Database.Persist.Postgresql (getSimpleConn, runSqlPool)+import Database.Persist.Sql (Migration, SqlBackend, showMigration)+import Database.PostgreSQL.Simple (Connection, rollback)+import Database.PostgreSQL.Simple.Migration qualified as Migration+import Database.PostgreSQL.Simple.Util qualified as Migration+import Prelude++-- | recommended default options for production settnigs+defaultOptions ::+  -- | migrations folder, for exmaple "migrations/up"+  FilePath ->+  -- | the logging options, for example+  --+  -- @+  --    (\case+  --       Left errmsg -> runInIO $ $logTM AlertS $ logStr errmsg+  --       Right infoMsg -> runInIO $ $logTM InfoS $ logStr infoMsg)+  -- @+  (Either Text Text -> IO ()) ->+  PersistentMigrationOptions+defaultOptions filepath logMsgs =+  PersistentMigrationOptions+    { pmoMigrationOptions =+        Migration.defaultOptions+          { Migration.optVerbose = Migration.Verbose,+            Migration.optLogWriter = logMsgs,+            -- NB we do the transaction around the entire thing+            Migration.optTransactionControl = Migration.NoNewTransaction+          },+      pmoMigrationSource = Migration.MigrationDirectory filepath,+      pmoSchemaMigrationTableName = "schema_migrations"+    }++-- | The result of the postgresql-migration operation.+data PMMigrationResult+  = MigrationConsistent+  | -- | rollback due to persistent inconsistencies+    MigrationRollbackDueTo [Text]+  | -- | some error from the postgres migration libraries+    MigrationLibraryError String+  | -- | caused by 'getSimpleConn' returning 'Nothing'+    MigrationNotBackedByPg+  deriving stock (Show, Eq)++data PersistentMigrationOptions = PersistentMigrationOptions+  { pmoMigrationOptions :: Migration.MigrationOptions,+    pmoMigrationSource :: Migration.MigrationCommand,+    pmoSchemaMigrationTableName :: String+  }++-- | Run the given migrations in a single transaction.  If the migration fails+-- somehow the transaction is rolled back.+runMigrations ::+  -- | eg 'defaultOptions'+  PersistentMigrationOptions ->+  -- | the Automatic migration. eg 'migrateAll', or migrateModels $(discoverEntities). note this is+  Migration ->+  -- | sql pool, created with for example 'withPostgresqlPool'.+  Pool SqlBackend ->+  IO PMMigrationResult+runMigrations config migrateAll pool =+  flip runSqlPool pool $ do+    asks getSimpleConn >>= \case+      Nothing -> pure $ MigrationNotBackedByPg+      Just conn -> do+        result <- runMigrationCommands config conn+        case result of+          Migration.MigrationError err ->+            pure $ MigrationLibraryError err+          Migration.MigrationSuccess ->+            assertPersistentConsistency migrateAll >>= \case+              MigrationConsistent -> pure MigrationConsistent+              failure -> liftIO $ failure <$ rollback conn++runMigrationCommands :: PersistentMigrationOptions -> Connection -> ReaderT SqlBackend IO (Migration.MigrationResult String)+runMigrationCommands options conn = do+  initialized <- liftIO $ Migration.existsTable conn $ pmoSchemaMigrationTableName options+  let migrations =+        if initialized+          then [pmoMigrationSource options]+          else [Migration.MigrationInitialization, pmoMigrationSource options]+  liftIO $ Migration.runMigrations conn (pmoMigrationOptions options) migrations++assertPersistentConsistency :: Migration -> ReaderT SqlBackend IO PMMigrationResult+assertPersistentConsistency migrateAll = do+  persistentAutoMig <- showMigration migrateAll+  if null persistentAutoMig+    then pure MigrationConsistent+    else do+      pure $ MigrationRollbackDueTo persistentAutoMig