Migration.hs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. module Database.Persist.Sql.Migration
  2. ( parseMigration
  3. , parseMigration'
  4. , printMigration
  5. , showMigration
  6. , getMigration
  7. , runMigration
  8. , runMigrationSilent
  9. , runMigrationUnsafe
  10. , migrate
  11. -- * Utilities for constructing migrations
  12. , reportErrors
  13. , reportError
  14. , addMigrations
  15. , addMigration
  16. ) where
  17. import Control.Monad (liftM, unless)
  18. import Control.Monad.IO.Unlift
  19. import Control.Monad.Trans.Class (MonadTrans (..))
  20. import Control.Monad.Trans.Reader (ReaderT (..), ask)
  21. import Control.Monad.Trans.Writer
  22. import Data.Text (Text, unpack, snoc, isPrefixOf, pack)
  23. import qualified Data.Text.IO
  24. import System.IO
  25. import System.IO.Silently (hSilence)
  26. import Database.Persist.Sql.Types
  27. import Database.Persist.Sql.Raw
  28. import Database.Persist.Types
  29. import Database.Persist.Sql.Orphan.PersistStore()
  30. allSql :: CautiousMigration -> [Sql]
  31. allSql = map snd
  32. safeSql :: CautiousMigration -> [Sql]
  33. safeSql = allSql . filter (not . fst)
  34. -- | Given a 'Migration', this parses it and returns either a list of
  35. -- errors associated with the migration or a list of migrations to do.
  36. parseMigration :: MonadIO m => Migration -> ReaderT SqlBackend m (Either [Text] CautiousMigration)
  37. parseMigration =
  38. liftIOReader . liftM go . runWriterT . execWriterT
  39. where
  40. go ([], sql) = Right sql
  41. go (errs, _) = Left errs
  42. liftIOReader (ReaderT m) = ReaderT $ liftIO . m
  43. -- | Like 'parseMigration', but instead of returning the value in an
  44. -- 'Either' value, it calls 'error' on the error values.
  45. parseMigration' :: MonadIO m => Migration -> ReaderT SqlBackend m (CautiousMigration)
  46. parseMigration' m = do
  47. x <- parseMigration m
  48. case x of
  49. Left errs -> error $ unlines $ map unpack errs
  50. Right sql -> return sql
  51. -- | Prints a migration.
  52. printMigration :: MonadIO m => Migration -> ReaderT SqlBackend m ()
  53. printMigration m = showMigration m
  54. >>= mapM_ (liftIO . Data.Text.IO.putStrLn)
  55. -- | Convert a 'Migration' to a list of 'Text' values corresponding to their
  56. -- 'Sql' statements.
  57. showMigration :: MonadIO m => Migration -> ReaderT SqlBackend m [Text]
  58. showMigration m = map (flip snoc ';') `liftM` getMigration m
  59. -- | Return all of the 'Sql' values associated with the given migration.
  60. -- Calls 'error' if there's a parse error on any migration.
  61. getMigration :: MonadIO m => Migration -> ReaderT SqlBackend m [Sql]
  62. getMigration m = do
  63. mig <- parseMigration' m
  64. return $ allSql mig
  65. -- | Runs a migration. If the migration fails to parse or if any of the
  66. -- migrations are unsafe, then this calls 'error' to halt the program.
  67. runMigration :: MonadIO m
  68. => Migration
  69. -> ReaderT SqlBackend m ()
  70. runMigration m = runMigration' m False >> return ()
  71. -- | Same as 'runMigration', but returns a list of the SQL commands executed
  72. -- instead of printing them to stderr.
  73. runMigrationSilent :: MonadUnliftIO m
  74. => Migration
  75. -> ReaderT SqlBackend m [Text]
  76. runMigrationSilent m = withRunInIO $ \run ->
  77. hSilence [stderr] $ run $ runMigration' m True
  78. -- | Run the given migration against the database. If the migration fails
  79. -- to parse, or there are any unsafe migrations, then this will error at
  80. -- runtime. This returns a list of the migrations that were executed.
  81. runMigration'
  82. :: MonadIO m
  83. => Migration
  84. -> Bool -- ^ is silent?
  85. -> ReaderT SqlBackend m [Text]
  86. runMigration' m silent = do
  87. mig <- parseMigration' m
  88. if any fst mig
  89. then error $ concat
  90. [ "\n\nDatabase migration: manual intervention required.\n"
  91. , "The unsafe actions are prefixed by '***' below:\n\n"
  92. , unlines $ map displayMigration mig
  93. ]
  94. else mapM (executeMigrate silent) $ sortMigrations $ safeSql mig
  95. where
  96. displayMigration :: (Bool, Sql) -> String
  97. displayMigration (True, s) = "*** " ++ unpack s ++ ";"
  98. displayMigration (False, s) = " " ++ unpack s ++ ";"
  99. -- | Like 'runMigration', but this will perform the unsafe database
  100. -- migrations instead of erroring out.
  101. runMigrationUnsafe :: MonadIO m
  102. => Migration
  103. -> ReaderT SqlBackend m ()
  104. runMigrationUnsafe m = do
  105. mig <- parseMigration' m
  106. mapM_ (executeMigrate False) $ sortMigrations $ allSql mig
  107. executeMigrate :: MonadIO m => Bool -> Text -> ReaderT SqlBackend m Text
  108. executeMigrate silent s = do
  109. unless silent $ liftIO $ hPutStrLn stderr $ "Migrating: " ++ unpack s
  110. rawExecute s []
  111. return s
  112. -- | Sort the alter DB statements so tables are created before constraints are
  113. -- added.
  114. sortMigrations :: [Sql] -> [Sql]
  115. sortMigrations x =
  116. filter isCreate x ++ filter (not . isCreate) x
  117. where
  118. -- Note the use of lower-case e. This (hack) allows backends to explicitly
  119. -- choose to have this special sorting applied.
  120. isCreate t = pack "CREATe " `isPrefixOf` t
  121. -- | Given a list of old entity definitions and a new 'EntityDef' in
  122. -- @val@, this creates a 'Migration' to update the old list of definitions
  123. -- with the new one.
  124. migrate :: [EntityDef]
  125. -> EntityDef
  126. -> Migration
  127. migrate allDefs val = do
  128. conn <- lift $ lift ask
  129. res <- liftIO $ connMigrateSql conn allDefs (getStmtConn conn) val
  130. either reportErrors addMigrations res
  131. -- | Report a single error in a 'Migration'.
  132. --
  133. -- @since 2.9.2
  134. reportError :: Text -> Migration
  135. reportError = tell . pure
  136. -- | Report multiple errors in a 'Migration'.
  137. --
  138. -- @since 2.9.2
  139. reportErrors :: [Text] -> Migration
  140. reportErrors = tell
  141. -- | Add a migration to the migration plan.
  142. --
  143. -- @since 2.9.2
  144. addMigration
  145. :: Bool
  146. -- ^ Is the migration safe to run? (eg a non-destructive and idempotent
  147. -- update on the schema)
  148. -> Sql
  149. -- ^ A 'Text' value representing the command to run on the database.
  150. -> Migration
  151. addMigration isSafe sql = lift (tell [(isSafe, sql)])
  152. -- | Add a 'CautiousMigration' (aka a @[('Bool', 'Text')]@) to the
  153. -- migration plan.
  154. --
  155. -- @since 2.9.2
  156. addMigrations
  157. :: CautiousMigration
  158. -> Migration
  159. addMigrations = lift . tell