sqel-0.0.1.0: lib/Sqel/Migration/Type.hs
module Sqel.Migration.Type where
import Generics.SOP (NP (Nil, (:*)), SListI, Top, hcfoldMap)
import Sqel.Data.Migration (CompAction, TableAction, TypeAction (AddAction, ModifyAction, RenameAction))
import Sqel.Data.PgType (PgColumn (PgColumn), PgColumns (PgColumns), pgColumnName)
import Sqel.Data.PgTypeName (PgCompName)
import Sqel.Kind (type (++))
import Sqel.SOP.Constraint (symbolText)
import Sqel.Migration.Column (ColIndex (colIndex), ColumnsChanges (columnsChanges))
import Sqel.Migration.Data.Ddl (DdlColumn (DdlColumn), DdlColumnK, DdlType (DdlType), DdlTypeK (DdlTypeK))
data OldK =
OldK {
table :: Bool,
name :: Symbol,
cols :: [DdlColumnK]
}
data NewK =
NewK {
index :: Nat,
table :: Bool,
name :: Symbol,
rename :: Maybe Symbol,
cols :: [DdlColumnK]
}
data ModK =
KeepK
|
AddK
|
RenameK
data ActionK =
ActionK {
mod :: ModK,
index :: Nat
}
|
UnusedK
type family OldKs (index :: Nat) (types :: [DdlTypeK]) :: [OldK] where
OldKs _ '[] = '[]
OldKs index ('DdlTypeK table name _ cols : types) =
'OldK table name cols : OldKs (index + 1) types
type family NewKs (index :: Nat) (types :: [DdlTypeK]) :: [NewK] where
NewKs _ '[] = '[]
NewKs index ('DdlTypeK table name rename cols : types) =
'NewK index table name rename cols : NewKs (index + 1) types
type family MkMigrationAction (old :: OldK) (check :: [NewK]) (other :: [NewK]) :: (ActionK, [NewK]) where
MkMigrationAction _ '[] other =
'( 'UnusedK, other)
MkMigrationAction ('OldK table name _) ('NewK index table name 'Nothing _ : news) other =
'( 'ActionK 'KeepK index, news ++ other)
MkMigrationAction ('OldK table oldName _) ('NewK index table _ ('Just oldName) _ : news) other =
'( 'ActionK 'RenameK index, news ++ other)
MkMigrationAction old (new : news) other =
MkMigrationAction old news (new : other)
type family NewMigrationActions (cols :: [NewK]) :: [ActionK] where
NewMigrationActions '[] = '[]
NewMigrationActions ('NewK index 'False _ 'Nothing _ : news) =
'ActionK 'AddK index : NewMigrationActions news
NewMigrationActions cols = TypeError ("type NewMigrationActions:" % cols)
type family MigrationActionsCont (cur :: (ActionK, [NewK])) (old :: [OldK]) :: [ActionK] where
MigrationActionsCont '(cur, new) old = cur : MigrationActions old new
type family MigrationActions (old :: [OldK]) (new :: [NewK]) :: [ActionK] where
MigrationActions '[] rest =
NewMigrationActions rest
MigrationActions (old : olds) new =
MigrationActionsCont (MkMigrationAction old new '[]) olds
type ReifyKeepAction :: Bool -> DdlTypeK -> DdlTypeK -> Constraint
class ReifyKeepAction table old new where
reifyKeepAction :: DdlType old -> DdlType new -> TypeAction table
instance (
ColumnsChanges colsOld colsNew
) => ReifyKeepAction table ('DdlTypeK table tname renameOld colsOld) ('DdlTypeK table tname renameNew colsNew) where
reifyKeepAction (DdlType name colsOld) (DdlType _ colsNew) =
ModifyAction name (columnsChanges colsOld colsNew)
type family ReifyModResult (table :: Bool) :: Type where
ReifyModResult 'False =
[(PgCompName, CompAction)]
ReifyModResult 'True =
TypeAction 'True
type ReifyModAction :: Bool -> ModK -> DdlTypeK -> DdlTypeK -> Constraint
class ReifyModAction table action old new where
reifyModAction :: DdlType old -> DdlType new -> ReifyModResult table
instance (
ReifyKeepAction 'True old new
) => ReifyModAction 'True 'KeepK old new where
reifyModAction old new =
reifyKeepAction @'True old new
instance (
ReifyKeepAction 'False ('DdlTypeK 'False tname renameOld colsOld) new
) => ReifyModAction 'False 'KeepK ('DdlTypeK 'False tname renameOld colsOld) new where
reifyModAction old@(DdlType name _) new =
[(name, reifyKeepAction @'False old new)]
instance (
ColumnsChanges colsOld colsNew
) => ReifyModAction 'False 'RenameK ('DdlTypeK 'False name renameOld colsOld) ('DdlTypeK 'False nameNew ('Just name) colsNew) where
reifyModAction (DdlType nameOld colsOld) (DdlType nameNew colsNew) =
[(nameOld, RenameAction nameNew (columnsChanges colsOld colsNew))]
type ReifyOldAction :: Bool -> ActionK -> DdlTypeK -> [DdlTypeK] -> Constraint
class ReifyOldAction table action old new where
reifyOldAction :: DdlType old -> NP DdlType new -> ReifyModResult table
instance (
ColIndex index news new,
ReifyModAction table mod old new
) => ReifyOldAction table ('ActionK mod index) old news where
reifyOldAction old news =
reifyModAction @table @mod old (colIndex @index news)
instance ReifyOldAction 'False 'UnusedK ('DdlTypeK 'False name renameOld colsOld) new where
reifyOldAction _ _ = []
-- -- TODO this has to check that new columns in composite types are
-- -- a) at the end of the list
-- -- b) Maybe
type ReifyNewAction :: ActionK -> [DdlTypeK] -> Constraint
class ReifyNewAction action new where
reifyNewAction :: NP DdlType new -> (PgCompName, CompAction)
instance (
SListI cols,
ColIndex index news ('DdlTypeK 'False name rename cols)
) => ReifyNewAction ('ActionK 'AddK index) news where
reifyNewAction news =
let
DdlType name ddlCols = colIndex @index news
cols = hcfoldMap (Proxy @Top) \ (DdlColumn (Proxy :: Proxy n) t _) ->
[PgColumn (pgColumnName (symbolText @n)) t]
in (name, AddAction (PgColumns (cols ddlCols)))
type ReifyActions :: [ActionK] -> [DdlTypeK] -> [DdlTypeK] -> Constraint
class ReifyActions actions old new where
reifyActions :: NP DdlType old -> NP DdlType new -> [(PgCompName, CompAction)]
instance ReifyActions '[] '[] new where
reifyActions _ _ =
mempty
instance (
ReifyNewAction action new,
ReifyActions actions '[] new
) => ReifyActions (action : actions) '[] new where
reifyActions Nil new =
reifyNewAction @action new : reifyActions @actions Nil new
instance (
ReifyOldAction 'False action o new,
ReifyActions actions old new
) => ReifyActions (action : actions) (o : old) new where
reifyActions (o :* old) new =
reifyOldAction @'False @action o new <> reifyActions @actions old new
type TypeChanges :: [DdlTypeK] -> [DdlTypeK] -> Constraint
class TypeChanges old new where
typeChanges :: NP DdlType old -> NP DdlType new -> [(PgCompName, CompAction)]
instance (
actions ~ MigrationActions (OldKs 0 old) (NewKs 0 new),
ReifyActions actions old new
) => TypeChanges old new where
typeChanges = reifyActions @actions
type TableChange :: DdlTypeK -> DdlTypeK -> Constraint
class TableChange old new where
tableChange :: DdlType old -> DdlType new -> TableAction
instance (
'[oldk] ~ OldKs 0 '[old],
'(action, '[]) ~ MkMigrationAction oldk (NewKs 0 '[new]) '[],
ReifyOldAction 'True action old '[new]
) => TableChange old new where
tableChange old new =
reifyOldAction @'True @action old (new :* Nil)