{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
module Database.Beam.Migrate.Types
(
CheckedDatabaseSettings
, IsCheckedDatabaseEntity(..)
, CheckedDatabaseEntityDescriptor(..)
, CheckedDatabaseEntity(..)
, unCheckDatabase, collectChecks
, renameCheckedEntity
, CheckedFieldModification
, checkedFieldNamed
, modifyCheckedTable
, checkedTableModification
, DatabasePredicate(..)
, SomeDatabasePredicate(..)
, PredicateSpecificity(..)
, QualifiedName(..)
, p
, TableCheck(..), DomainCheck(..)
, FieldCheck(..)
, MigrationStep(..), MigrationSteps(..)
, Migration, MigrationF(..)
, MigrationCommand(..), MigrationDataLoss(..)
, runMigrationSteps, runMigrationSilenced
, executeMigration, eraseMigrationType, migrationStep
, upDown, migrationDataLoss
, migrateScript, evaluateDatabase, stepNames ) where
import Database.Beam.Backend.SQL
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 be next where
MigrationStep :: Text -> Migration be a -> (a -> next) -> MigrationStep be next
deriving instance Functor (MigrationStep be)
newtype MigrationSteps be from to = MigrationSteps (Kleisli (F (MigrationStep be)) from to)
deriving (Category, Arrow)
data MigrationF be next where
MigrationRunCommand
:: { _migrationUpCommand :: BeamSqlBackendSyntax be
, _migrationDownCommand :: Maybe (BeamSqlBackendSyntax be)
, _migrationNext :: next }
-> MigrationF be next
deriving instance Functor (MigrationF be)
type Migration be = F (MigrationF be)
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 be
= MigrationCommand
{ migrationCommand :: BeamSqlBackendSyntax be
, migrationCommandDataLossPossible :: MigrationDataLoss
}
deriving instance Show (BeamSqlBackendSyntax be) => Show (MigrationCommand be)
runMigrationSteps :: Monad m
=> Int
-> Maybe Int
-> MigrationSteps be () a
-> (forall a'. Int -> Text -> Migration be 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 be a -> a
runMigrationSilenced m = runF m id step
where
step (MigrationRunCommand _ _ next) = next
eraseMigrationType :: a -> MigrationSteps be a a' -> MigrationSteps be () ()
eraseMigrationType a (MigrationSteps steps) = MigrationSteps (arr (const a) >>> steps >>> arr (const ()))
migrationStep :: Text -> (a -> Migration be a') -> MigrationSteps be a a'
migrationStep stepName migration =
MigrationSteps (Kleisli (\a -> liftF (MigrationStep stepName (migration a) id)))
upDown :: BeamSqlBackendSyntax be -> Maybe (BeamSqlBackendSyntax be) -> Migration be ()
upDown up down = liftF (MigrationRunCommand up down ())
migrateScript :: forall be m a. (Monoid m, Semigroup m, BeamSqlBackend be)
=> (Text -> m)
-> (BeamSqlBackendSyntax be -> m)
-> MigrationSteps be () 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 be a' -> m -> (a', m)
renderMigration migrationSteps =
runF migrationSteps (,)
(\(MigrationRunCommand a _ next) x -> next (x <> renderMigrationSyntax a))
executeMigration :: Applicative m => (BeamSqlBackendSyntax be -> m ()) -> Migration be a -> m a
executeMigration runSyntax go = runF go pure doStep
where
doStep (MigrationRunCommand cmd _ next) =
runSyntax cmd *> next
migrationDataLoss :: Migration be a -> MigrationDataLoss
migrationDataLoss go = runF go (\_ -> MigrationKeepsData)
(\(MigrationRunCommand _ x next) ->
case x of
Nothing -> MigrationLosesData
_ -> next)
evaluateDatabase :: forall be a. MigrationSteps be () a -> a
evaluateDatabase (MigrationSteps f) = runF (runKleisli f ()) id (\(MigrationStep _ migration next) -> next (runMigration migration))
where
runMigration :: forall a'. Migration be a' -> a'
runMigration migration = runF migration id (\(MigrationRunCommand _ _ next) -> next)
stepNames :: forall be a. MigrationSteps be () 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 be a' -> a'
runMigration migration = runF migration id (\(MigrationRunCommand _ _ next) -> next)