{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
module Database.Beam.Migrate.Types
(
CheckedDatabaseSettings
, IsCheckedDatabaseEntity(..)
, CheckedDatabaseEntityDescriptor(..)
, CheckedDatabaseEntity(..)
, unCheckDatabase, collectChecks
, CheckedFieldModification
, checkedFieldNamed
, modifyCheckedTable
, checkedTableModification
, DatabasePredicate(..)
, SomeDatabasePredicate(..)
, PredicateSpecificity(..)
, p
, TableCheck(..), DomainCheck(..)
, FieldCheck(..)
, MigrationStep(..), MigrationSteps(..)
, Migration, MigrationF(..)
, MigrationCommand(..), MigrationDataLoss(..)
, runMigrationSteps, runMigrationSilenced
, executeMigration, eraseMigrationType, migrationStep
, upDown, migrationDataLoss
, migrateScript, evaluateDatabase, stepNames ) where
import Database.Beam.Migrate.Types.CheckedEntities
import Database.Beam.Migrate.Types.Predicates
import Control.Monad.Free.Church
import Control.Arrow
import Control.Category (Category)
#if !MIN_VERSION_base(4, 11, 0)
import Data.Semigroup
#endif
import Data.Text (Text)
data MigrationStep syntax next where
MigrationStep :: Text -> Migration syntax a -> (a -> next) -> MigrationStep syntax next
deriving instance Functor (MigrationStep syntax)
newtype MigrationSteps syntax from to = MigrationSteps (Kleisli (F (MigrationStep syntax)) from to)
deriving (Category, Arrow)
data MigrationF syntax next where
MigrationRunCommand
:: { _migrationUpCommand :: syntax
, _migrationDownCommand :: Maybe syntax
, _migrationNext :: next }
-> MigrationF syntax next
deriving instance Functor (MigrationF syntax)
type Migration syntax = F (MigrationF syntax)
data MigrationDataLoss
= MigrationLosesData
| MigrationKeepsData
deriving Show
instance Semigroup MigrationDataLoss where
(<>) = mappend
instance Monoid MigrationDataLoss where
mempty = MigrationKeepsData
mappend MigrationLosesData _ = MigrationLosesData
mappend _ MigrationLosesData = MigrationLosesData
mappend MigrationKeepsData MigrationKeepsData = MigrationKeepsData
data MigrationCommand cmd
= MigrationCommand
{ migrationCommand :: cmd
, migrationCommandDataLossPossible :: MigrationDataLoss
} deriving Show
runMigrationSteps :: Monad m
=> Int
-> Maybe Int
-> MigrationSteps syntax () a
-> (forall a'. Int -> Text -> Migration syntax a' -> m a')
-> m a
runMigrationSteps firstIdx lastIdx (MigrationSteps steps) runMigration =
runF (runKleisli steps ()) finish step 0
where finish x _ = pure x
step (MigrationStep nm doStep next) i =
if i >= firstIdx && maybe True (i <) lastIdx
then runMigration i nm doStep >>= \x -> next x (i + 1)
else next (runMigrationSilenced doStep) (i + 1)
runMigrationSilenced :: Migration syntax a -> a
runMigrationSilenced m = runF m id step
where
step (MigrationRunCommand _ _ next) = next
eraseMigrationType :: a -> MigrationSteps syntax a a' -> MigrationSteps syntax () ()
eraseMigrationType a (MigrationSteps steps) = MigrationSteps (arr (const a) >>> steps >>> arr (const ()))
migrationStep :: Text -> (a -> Migration syntax a') -> MigrationSteps syntax a a'
migrationStep stepName migration =
MigrationSteps (Kleisli (\a -> liftF (MigrationStep stepName (migration a) id)))
upDown :: syntax -> Maybe syntax -> Migration syntax ()
upDown up down = liftF (MigrationRunCommand up down ())
migrateScript :: forall syntax m a. (Monoid m, Semigroup m)
=> (Text -> m)
-> (syntax -> m)
-> MigrationSteps syntax () a
-> m
migrateScript renderMigrationHeader renderMigrationSyntax (MigrationSteps steps) =
runF (runKleisli steps ()) (\_ x -> x)
(\(MigrationStep header migration next) x ->
let (res, script) = renderMigration migration mempty
in next res (x <> renderMigrationHeader header <> script)) mempty
where
renderMigration :: forall a'. Migration syntax a' -> m -> (a', m)
renderMigration migrationSteps =
runF migrationSteps (,)
(\(MigrationRunCommand a _ next) x -> next (x <> renderMigrationSyntax a))
executeMigration :: Applicative m => (syntax -> m ()) -> Migration syntax a -> m a
executeMigration runSyntax go = runF go pure doStep
where
doStep (MigrationRunCommand cmd _ next) =
runSyntax cmd *> next
migrationDataLoss :: Migration syntax a -> MigrationDataLoss
migrationDataLoss go = runF go (\_ -> MigrationKeepsData)
(\(MigrationRunCommand _ x next) ->
case x of
Nothing -> MigrationLosesData
_ -> next)
evaluateDatabase :: forall syntax a. MigrationSteps syntax () a -> a
evaluateDatabase (MigrationSteps f) = runF (runKleisli f ()) id (\(MigrationStep _ migration next) -> next (runMigration migration))
where
runMigration :: forall a'. Migration syntax a' -> a'
runMigration migration = runF migration id (\(MigrationRunCommand _ _ next) -> next)
stepNames :: forall syntax a. MigrationSteps syntax () a -> [Text]
stepNames (MigrationSteps f) = runF (runKleisli f ()) (\_ x -> x) (\(MigrationStep nm migration next) x -> next (runMigration migration) (x ++ [nm])) []
where
runMigration :: forall a'. Migration syntax a' -> a'
runMigration migration = runF migration id (\(MigrationRunCommand _ _ next) -> next)