module Sqel.Migration.Run where
import Control.Monad (foldM)
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Text as Text
import Exon (exon)
import Generics.SOP (All, NP (Nil, (:*)))
import Lens.Micro ((^.))
import qualified Sqel.Class.MigrationEffect as MigrationEffect
import Sqel.Class.MigrationEffect (MigrationEffect (runMigrationStatements))
import Sqel.Data.Migration (
CompAction,
CustomMigration (customMigration, customTypeKeys),
MigExt,
Migration (Migration),
MigrationActions (AutoActions, CustomActions),
Migrations (Migrations),
TypeAction (AddAction),
)
import Sqel.Data.PgType (
ColumnType (ColumnComp, ColumnPrim),
PgColumns (PgColumns),
PgComposite (PgComposite),
PgTable (PgTable),
)
import Sqel.Data.PgTypeName (
PgCompName,
pattern PgOnlyTableName,
PgTableName,
pattern PgTableName,
pattern PgTypeName,
PgTypeName,
getPgTypeName,
)
import Sqel.Data.Sql (Sql)
import Sqel.Statement (tableColumnsSql, typeColumnsSql)
import Sqel.Migration.Init (initTable)
import Sqel.Migration.Metadata (
DbCols (DbCols),
TypeStatus (Absent, Match, Mismatch),
columnMap,
logType,
typeColumns,
typeStatus,
)
import Sqel.Migration.Statement (typeStatements)
typeMatchWith ::
Monad m =>
MigrationEffect m =>
Text ->
PgTypeName table ->
PgColumns ->
Sql ->
m TypeStatus
typeMatchWith :: forall (m :: * -> *) (table :: Bool).
(Monad m, MigrationEffect m) =>
Text -> PgTypeName table -> PgColumns -> Sql -> m TypeStatus
typeMatchWith Text
desc PgTypeName table
name (PgColumns [PgColumn]
cols) Sql
code = do
DbCols
dbCols <- forall (m :: * -> *) (table :: Bool).
(Monad m, MigrationEffect m) =>
Sql -> PgTypeName table -> m DbCols
typeColumns Sql
code PgTypeName table
name
forall (m :: * -> *).
MigrationEffect m =>
Text -> DbCols -> DbCols -> m ()
logType Text
desc DbCols
dbCols DbCols
colsByName
pure (DbCols -> DbCols -> TypeStatus
typeStatus DbCols
dbCols DbCols
colsByName)
where
colsByName :: DbCols
colsByName = Map PgColumnName (Either PgTypeRef PgPrimName) -> DbCols
DbCols forall a b. (a -> b) -> a -> b
$ [PgColumn] -> Map PgColumnName ColumnType
columnMap [PgColumn]
cols forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
ColumnPrim PgPrimName
n Bool
_ [Sql]
_ -> forall a b. b -> Either a b
Right PgPrimName
n
ColumnComp PgTypeRef
n Bool
_ [Sql]
_ -> forall a b. a -> Either a b
Left PgTypeRef
n
typeMatch ::
Monad m =>
MigrationEffect m =>
PgComposite ->
m TypeStatus
typeMatch :: forall (m :: * -> *).
(Monad m, MigrationEffect m) =>
PgComposite -> m TypeStatus
typeMatch (PgComposite PgCompName
name PgColumns
cols) =
forall (m :: * -> *) (table :: Bool).
(Monad m, MigrationEffect m) =>
Text -> PgTypeName table -> PgColumns -> Sql -> m TypeStatus
typeMatchWith Text
"type" PgCompName
name PgColumns
cols Sql
typeColumnsSql
tableMatch ::
Monad m =>
MigrationEffect m =>
TypeStatus ->
PgTable a ->
m TypeStatus
tableMatch :: forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
TypeStatus -> PgTable a -> m TypeStatus
tableMatch TypeStatus
Absent PgTable a
_ =
forall (f :: * -> *) a. Applicative f => a -> f a
pure TypeStatus
Absent
tableMatch TypeStatus
_ (PgTable PgTableName
name PgColumns
cols Map PgTypeRef PgComposite
_ TableSelectors
_ TableValues
_ PgStructure
_) =
forall (m :: * -> *) (table :: Bool).
(Monad m, MigrationEffect m) =>
Text -> PgTypeName table -> PgColumns -> Sql -> m TypeStatus
typeMatchWith Text
"table" PgTableName
name PgColumns
cols Sql
tableColumnsSql
matches ::
Monad m =>
MigrationEffect m =>
TypeStatus ->
PgTable from ->
m (TypeStatus, Set PgCompName)
matches :: forall {k} (m :: * -> *) (from :: k).
(Monad m, MigrationEffect m) =>
TypeStatus -> PgTable from -> m (TypeStatus, Set PgCompName)
matches TypeStatus
initialStatus PgTable from
table = do
TypeStatus
tbm <- forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
TypeStatus -> PgTable a -> m TypeStatus
tableMatch TypeStatus
initialStatus PgTable from
table
Set PgCompName
tym <- forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM forall {f :: * -> *}.
(MigrationEffect f, Monad f) =>
Set PgCompName -> PgComposite -> f (Set PgCompName)
folder forall a. Set a
Set.empty (PgTable from
table forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "types" a => a
#types)
pure (TypeStatus
tbm, Set PgCompName
tym)
where
folder :: Set PgCompName -> PgComposite -> f (Set PgCompName)
folder Set PgCompName
acc PgComposite
t =
forall (m :: * -> *).
(Monad m, MigrationEffect m) =>
PgComposite -> m TypeStatus
typeMatch PgComposite
t forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
TypeStatus
Match -> forall a. Ord a => a -> Set a -> Set a
Set.insert (PgComposite
t forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "name" a => a
#name) Set PgCompName
acc
TypeStatus
_ -> Set PgCompName
acc
runAction ::
MigrationEffect m =>
PgTypeName table ->
TypeAction table ->
m ()
runAction :: forall (m :: * -> *) (table :: Bool).
MigrationEffect m =>
PgTypeName table -> TypeAction table -> m ()
runAction PgTypeName table
typeName TypeAction table
action =
forall (m :: * -> *).
MigrationEffect m =>
[MigrationStatement] -> m ()
runMigrationStatements (forall (table :: Bool).
PgTypeName table -> TypeAction table -> [MigrationStatement]
typeStatements PgTypeName table
typeName TypeAction table
action)
runTypesMigration ::
Monad m =>
MigrationEffect m =>
Set PgCompName ->
Map PgCompName CompAction ->
m ()
runTypesMigration :: forall (m :: * -> *).
(Monad m, MigrationEffect m) =>
Set PgCompName -> Map PgCompName CompAction -> m ()
runTypesMigration Set PgCompName
eligible Map PgCompName CompAction
actions =
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (forall k a. Map k a -> [(k, a)]
Map.toList (forall k a. Ord k => Map k a -> Set k -> Map k a
Map.restrictKeys Map PgCompName CompAction
actions Set PgCompName
eligible)) \ (PgCompName
name, CompAction
tpe) ->
forall (m :: * -> *) (table :: Bool).
MigrationEffect m =>
PgTypeName table -> TypeAction table -> m ()
runAction PgCompName
name CompAction
tpe
runMigration ::
∀ mig m .
Monad m =>
MigrationEffect m =>
CustomMigration m mig =>
TypeStatus ->
PgTableName ->
Set PgCompName ->
MigrationActions (MigExt mig) ->
m ()
runMigration :: forall (mig :: Mig) (m :: * -> *).
(Monad m, MigrationEffect m, CustomMigration m mig) =>
TypeStatus
-> PgTableName
-> Set PgCompName
-> MigrationActions (MigExt mig)
-> m ()
runMigration TypeStatus
status PgTableName
tableName Set PgCompName
eligible = \case
AutoActions TableAction
tableAction Map PgCompName CompAction
typeActions -> do
forall (m :: * -> *). MigrationEffect m => Text -> m ()
MigrationEffect.log [exon|Starting migration for #{getPgTypeName tableName}|]
forall (m :: * -> *).
(Monad m, MigrationEffect m) =>
Set PgCompName -> Map PgCompName CompAction -> m ()
runTypesMigration Set PgCompName
eligible Map PgCompName CompAction
typeActions
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (TypeStatus
status forall a. Eq a => a -> a -> Bool
== TypeStatus
Match) (forall (m :: * -> *) (table :: Bool).
MigrationEffect m =>
PgTypeName table -> TypeAction table -> m ()
runAction PgTableName
tableName TableAction
tableAction)
CustomActions MigExt mig
actions ->
forall (m :: * -> *) (mig :: Mig).
CustomMigration m mig =>
PgTableName -> Set PgCompName -> MigExt mig -> m ()
customMigration @m @mig PgTableName
tableName Set PgCompName
eligible MigExt mig
actions
tryRunMigration ::
∀ mig m .
Monad m =>
MigrationEffect m =>
CustomMigration m mig =>
TypeStatus ->
PgTableName ->
Set PgCompName ->
MigrationActions (MigExt mig) ->
m ()
tryRunMigration :: forall (mig :: Mig) (m :: * -> *).
(Monad m, MigrationEffect m, CustomMigration m mig) =>
TypeStatus
-> PgTableName
-> Set PgCompName
-> MigrationActions (MigExt mig)
-> m ()
tryRunMigration TypeStatus
Mismatch (PgTableName Text
name) Set PgCompName
_ MigrationActions (MigExt mig)
_ =
forall (m :: * -> *). MigrationEffect m => Text -> m ()
MigrationEffect.error [exon|No migration fits the current table layout for #{name}|]
tryRunMigration TypeStatus
status PgTableName
tableName Set PgCompName
eligible MigrationActions (MigExt mig)
actions =
forall (mig :: Mig) (m :: * -> *).
(Monad m, MigrationEffect m, CustomMigration m mig) =>
TypeStatus
-> PgTableName
-> Set PgCompName
-> MigrationActions (MigExt mig)
-> m ()
runMigration @mig TypeStatus
status PgTableName
tableName Set PgCompName
eligible MigrationActions (MigExt mig)
actions
autoKeys ::
Map PgCompName CompAction ->
Set (PgCompName, Bool)
autoKeys :: Map PgCompName CompAction -> Set (PgCompName, Bool)
autoKeys Map PgCompName CompAction
typeActions =
forall a. Ord a => [a] -> Set a
Set.fromList (forall k a. Map k a -> [a]
Map.elems (forall k a b. (k -> a -> b) -> Map k a -> Map k b
Map.mapWithKey forall {a} {table :: Bool}. a -> TypeAction table -> (a, Bool)
keyAndAddition Map PgCompName CompAction
typeActions))
where
keyAndAddition :: a -> TypeAction table -> (a, Bool)
keyAndAddition a
k = \case
AddAction PgColumns
_ -> (a
k, Bool
True)
TypeAction table
_ -> (a
k, Bool
False)
typeKeys ::
∀ mig m .
Applicative m =>
CustomMigration m mig =>
MigrationActions (MigExt mig) ->
m (Set (PgCompName, Bool))
typeKeys :: forall (mig :: Mig) (m :: * -> *).
(Applicative m, CustomMigration m mig) =>
MigrationActions (MigExt mig) -> m (Set (PgCompName, Bool))
typeKeys = \case
AutoActions TableAction
_ Map PgCompName CompAction
typeActions ->
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map PgCompName CompAction -> Set (PgCompName, Bool)
autoKeys Map PgCompName CompAction
typeActions)
CustomActions MigExt mig
actions ->
forall (m :: * -> *) (mig :: Mig).
CustomMigration m mig =>
MigExt mig -> m (Set (PgCompName, Bool))
customTypeKeys @m @mig MigExt mig
actions
collectDirectMatches :: Set (PgCompName, Bool) -> Set PgCompName -> Set PgCompName
collectDirectMatches :: Set (PgCompName, Bool) -> Set PgCompName -> Set PgCompName
collectDirectMatches Set (PgCompName, Bool)
actions Set PgCompName
curMatches =
forall a. Ord a => [a] -> Set a
Set.fromList (forall a b. (a, b) -> a
fst forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. (a -> Bool) -> [a] -> [a]
filter (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry PgCompName -> Bool -> Bool
matchAction) (forall a. Set a -> [a]
Set.toList Set (PgCompName, Bool)
actions))
where
matchAction :: PgCompName -> Bool -> Bool
matchAction PgCompName
name = \case
Bool
True -> Bool -> Bool
not (forall a. Ord a => a -> Set a -> Bool
Set.member PgCompName
name Set PgCompName
curMatches)
Bool
False -> forall a. Ord a => a -> Set a -> Bool
Set.member PgCompName
name Set PgCompName
curMatches
matchMessage :: PgTypeName table -> TypeStatus -> Set PgCompName -> Set PgCompName -> Set PgCompName -> Text
matchMessage :: forall (table :: Bool).
PgTypeName table
-> TypeStatus
-> Set PgCompName
-> Set PgCompName
-> Set PgCompName
-> Text
matchMessage (PgTypeName Text
tableName) TypeStatus
status Set PgCompName
currentMatches Set PgCompName
directMatches Set PgCompName
allMatches =
[exon|Table #{tableName}: #{show status}
Matching types: #{showNames currentMatches}
Direct action matches: #{showNames directMatches}
All action matches: #{showNames allMatches}
|]
where
showNames :: Set (PgTypeName table) -> Text
showNames =
Text -> [Text] -> Text
Text.intercalate Text
", " forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\ (PgTypeName Text
name) -> Text
name) forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall a. Set a -> [a]
Set.toList
runMigrationSteps ::
∀ m migs a .
Monad m =>
MigrationEffect m =>
All (CustomMigration m) migs =>
TypeStatus ->
Set PgCompName ->
PgTable a ->
NP Migration migs ->
m (TypeStatus, Set PgCompName)
runMigrationSteps :: forall {k} (m :: * -> *) (migs :: [Mig]) (a :: k).
(Monad m, MigrationEffect m, All (CustomMigration m) migs) =>
TypeStatus
-> Set PgCompName
-> PgTable a
-> NP Migration migs
-> m (TypeStatus, Set PgCompName)
runMigrationSteps TypeStatus
initialStatus Set PgCompName
_ PgTable a
_ NP Migration migs
Nil =
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypeStatus
initialStatus, forall a. Monoid a => a
mempty)
runMigrationSteps TypeStatus
initialStatus Set PgCompName
laterMatches PgTable a
table ((Migration PgTable from
currentTable PgTable to
_ MigrationActions ext
actions :: Migration mig) :* NP Migration xs
t) = do
(TypeStatus
status, Set PgCompName
currentTypeMatches) <- forall {k} (m :: * -> *) (from :: k).
(Monad m, MigrationEffect m) =>
TypeStatus -> PgTable from -> m (TypeStatus, Set PgCompName)
matches TypeStatus
initialStatus PgTable from
currentTable
Set (PgCompName, Bool)
actionNamesAndAdditions <- forall (mig :: Mig) (m :: * -> *).
(Applicative m, CustomMigration m mig) =>
MigrationActions (MigExt mig) -> m (Set (PgCompName, Bool))
typeKeys @mig @m MigrationActions ext
actions
let
actionNames :: Set PgCompName
actionNames = forall a. Ord a => [a] -> Set a
Set.fromList (forall a b. (a, b) -> a
fst forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Set a -> [a]
Set.toList Set (PgCompName, Bool)
actionNamesAndAdditions)
mismatchHere :: Bool
mismatchHere = TypeStatus
status forall a. Eq a => a -> a -> Bool
== TypeStatus
Mismatch
directMatches :: Set PgCompName
directMatches = Set (PgCompName, Bool) -> Set PgCompName -> Set PgCompName
collectDirectMatches Set (PgCompName, Bool)
actionNamesAndAdditions Set PgCompName
currentTypeMatches
allMatches :: Set PgCompName
allMatches = forall a. Ord a => Set a -> Set a -> Set a
Set.union Set PgCompName
directMatches Set PgCompName
laterMatches
forall (m :: * -> *). MigrationEffect m => Text -> m ()
MigrationEffect.log (forall (table :: Bool).
PgTypeName table
-> TypeStatus
-> Set PgCompName
-> Set PgCompName
-> Set PgCompName
-> Text
matchMessage (PgTable from
currentTable forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "name" a => a
#name) TypeStatus
status Set PgCompName
currentTypeMatches Set PgCompName
directMatches Set PgCompName
allMatches)
(TypeStatus
newStatus, Set PgCompName
eligible) <-
if Bool -> Bool
not Bool
mismatchHere Bool -> Bool -> Bool
&& forall a. Ord a => Set a -> Set a -> Bool
Set.isSubsetOf Set PgCompName
actionNames Set PgCompName
allMatches
then forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypeStatus
status, Set PgCompName
directMatches)
else do
(TypeStatus
earlierStatus, Set PgCompName
earlierMatches) <- forall {k} (m :: * -> *) (migs :: [Mig]) (a :: k).
(Monad m, MigrationEffect m, All (CustomMigration m) migs) =>
TypeStatus
-> Set PgCompName
-> PgTable a
-> NP Migration migs
-> m (TypeStatus, Set PgCompName)
runMigrationSteps TypeStatus
status Set PgCompName
allMatches PgTable a
table NP Migration xs
t
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypeStatus
earlierStatus, forall a. Ord a => Set a -> Set a -> Set a
Set.union Set PgCompName
earlierMatches Set PgCompName
directMatches)
forall (mig :: Mig) (m :: * -> *).
(Monad m, MigrationEffect m, CustomMigration m mig) =>
TypeStatus
-> PgTableName
-> Set PgCompName
-> MigrationActions (MigExt mig)
-> m ()
runMigration @mig TypeStatus
newStatus (PgTable a
table forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "name" a => a
#name) Set PgCompName
eligible MigrationActions ext
actions
pure (TypeStatus
newStatus, Set PgCompName
eligible)
createAbsent ::
Monad m =>
MigrationEffect m =>
PgTable a ->
TypeStatus ->
m ()
createAbsent :: forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
PgTable a -> TypeStatus -> m ()
createAbsent PgTable a
table = \case
TypeStatus
Absent -> forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
PgTable a -> m ()
initTable PgTable a
table
TypeStatus
_ -> forall (f :: * -> *). Applicative f => f ()
unit
runMigrations ::
∀ m migs a .
Monad m =>
MigrationEffect m =>
All (CustomMigration m) migs =>
PgTable a ->
Migrations m migs ->
m ()
runMigrations :: forall {k} (m :: * -> *) (migs :: [Mig]) (a :: k).
(Monad m, MigrationEffect m, All (CustomMigration m) migs) =>
PgTable a -> Migrations m migs -> m ()
runMigrations PgTable a
table (Migrations NP Migration migs
steps) = do
forall (m :: * -> *). MigrationEffect m => Text -> m ()
MigrationEffect.log [exon|Checking migrations for '#{name}'|]
TypeStatus
initialStatus <- forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
TypeStatus -> PgTable a -> m TypeStatus
tableMatch TypeStatus
Mismatch PgTable a
table
(TypeStatus
status, Set PgCompName
_) <- forall {k} (m :: * -> *) (migs :: [Mig]) (a :: k).
(Monad m, MigrationEffect m, All (CustomMigration m) migs) =>
TypeStatus
-> Set PgCompName
-> PgTable a
-> NP Migration migs
-> m (TypeStatus, Set PgCompName)
runMigrationSteps TypeStatus
initialStatus forall a. Monoid a => a
mempty PgTable a
table NP Migration migs
steps
forall (m :: * -> *). MigrationEffect m => Text -> m ()
MigrationEffect.log [exon|Migrations for '#{name}' concluded with #{show status}|]
forall {k} (m :: * -> *) (a :: k).
(Monad m, MigrationEffect m) =>
PgTable a -> TypeStatus -> m ()
createAbsent PgTable a
table TypeStatus
status
where
PgOnlyTableName Text
name = PgTable a
table forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "name" a => a
#name