{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
module Database.Beam.Postgres.Extensions where
import Database.Beam
import Database.Beam.Schema.Tables
import Database.Beam.Postgres.Types
import Database.Beam.Postgres.Syntax
import Database.Beam.Migrate
import Control.Monad
import Data.Aeson
import qualified Data.HashSet as HS
import Data.Hashable (Hashable)
import Data.Proxy
import Data.Text (Text)
#if !MIN_VERSION_base(4, 11, 0)
import Data.Semigroup
#endif
data PgExtensionEntity extension
class IsPgExtension extension where
pgExtensionName :: Proxy extension -> Text
pgExtensionBuild :: extension
instance RenamableWithRule (FieldRenamer (DatabaseEntityDescriptor Postgres (PgExtensionEntity e))) where
renamingFields _ = FieldRenamer id
instance IsDatabaseEntity Postgres (PgExtensionEntity extension) where
data DatabaseEntityDescriptor Postgres (PgExtensionEntity extension) where
PgDatabaseExtension :: IsPgExtension extension
=> Text
-> extension
-> DatabaseEntityDescriptor Postgres (PgExtensionEntity extension)
type DatabaseEntityDefaultRequirements Postgres (PgExtensionEntity extension) =
( IsPgExtension extension )
type DatabaseEntityRegularRequirements Postgres (PgExtensionEntity extension) =
( IsPgExtension extension )
dbEntityName f (PgDatabaseExtension nm ext) = fmap (\nm' -> PgDatabaseExtension nm' ext) (f nm)
dbEntitySchema _ n = pure n
dbEntityAuto _ = PgDatabaseExtension (pgExtensionName (Proxy @extension)) pgExtensionBuild
instance IsCheckedDatabaseEntity Postgres (PgExtensionEntity extension) where
newtype CheckedDatabaseEntityDescriptor Postgres (PgExtensionEntity extension) =
CheckedPgExtension (DatabaseEntityDescriptor Postgres (PgExtensionEntity extension))
type CheckedDatabaseEntityDefaultRequirements Postgres (PgExtensionEntity extension) =
DatabaseEntityRegularRequirements Postgres (PgExtensionEntity extension)
unChecked f (CheckedPgExtension ext) = CheckedPgExtension <$> f ext
collectEntityChecks (CheckedPgExtension (PgDatabaseExtension {})) =
[ SomeDatabasePredicate (PgHasExtension (pgExtensionName (Proxy @extension))) ]
checkedDbEntityAuto = CheckedPgExtension . dbEntityAuto
getPgExtension :: DatabaseEntity Postgres db (PgExtensionEntity extension)
-> extension
getPgExtension (DatabaseEntity (PgDatabaseExtension _ ext)) = ext
pgCreateExtension :: forall extension db
. IsPgExtension extension
=> Migration Postgres (CheckedDatabaseEntity Postgres db (PgExtensionEntity extension))
pgCreateExtension =
let entity = checkedDbEntityAuto ""
extName = pgExtensionName (Proxy @extension)
in upDown (pgCreateExtensionSyntax extName) Nothing >>
pure (CheckedDatabaseEntity entity (collectEntityChecks entity))
pgDropExtension :: forall extension
. CheckedDatabaseEntityDescriptor Postgres (PgExtensionEntity extension)
-> Migration Postgres ()
pgDropExtension (CheckedPgExtension (PgDatabaseExtension {})) =
upDown (pgDropExtensionSyntax (pgExtensionName (Proxy @extension))) Nothing
newtype PgHasExtension = PgHasExtension Text
deriving (Show, Eq, Generic, Hashable)
instance DatabasePredicate PgHasExtension where
englishDescription (PgHasExtension extName) =
"Postgres extension " ++ show extName ++ " is loaded"
predicateSpecificity _ = PredicateSpecificityOnlyBackend "postgres"
serializePredicate (PgHasExtension nm) =
object [ "has-postgres-extension" .= nm ]
pgExtensionActionProvider :: ActionProvider Postgres
pgExtensionActionProvider = pgCreateExtensionProvider <> pgDropExtensionProvider
pgCreateExtensionProvider, pgDropExtensionProvider :: ActionProvider Postgres
pgCreateExtensionProvider =
ActionProvider $ \findPre findPost ->
do extP@(PgHasExtension ext) <- findPost
ensuringNot_ $
do PgHasExtension ext' <- findPre
guard (ext == ext')
let cmd = pgCreateExtensionSyntax ext
pure (PotentialAction mempty (HS.fromList [p extP])
(pure (MigrationCommand cmd MigrationKeepsData))
("Load the postgres extension " <> ext) 1)
pgDropExtensionProvider =
ActionProvider $ \findPre findPost ->
do extP@(PgHasExtension ext) <- findPre
ensuringNot_ $
do PgHasExtension ext' <- findPost
guard (ext == ext')
let cmd = pgDropExtensionSyntax ext
pure (PotentialAction (HS.fromList [p extP]) mempty
(pure (MigrationCommand cmd MigrationKeepsData))
("Unload the postgres extension " <> ext) 1)