@@ -22,6 +22,8 @@ module Database.PostgreSQL.Simple.Migration
22
22
(
23
23
-- * Migration actions
24
24
runMigration
25
+ , runMigrations
26
+ , sequenceMigrations
25
27
26
28
-- * Migration types
27
29
, MigrationContext (.. )
@@ -48,7 +50,7 @@ import Data.Foldable (Foldable)
48
50
import Data.Traversable (Traversable )
49
51
import Data.List (isPrefixOf , sort )
50
52
#if __GLASGOW_HASKELL__ < 710
51
- import Data.Monoid (mconcat )
53
+ import Data.Monoid (Monoid ( .. ) )
52
54
#endif
53
55
import Data.Time (LocalTime )
54
56
import Database.PostgreSQL.Simple (Connection , Only (.. ),
@@ -73,28 +75,52 @@ runMigration (MigrationContext cmd verbose con) = case cmd of
73
75
MigrationInitialization ->
74
76
initializeSchema con verbose >> return MigrationSuccess
75
77
MigrationDirectory path ->
76
- executeDirectoryMigration con verbose path
78
+ executeDirectoryMigration con verbose path
77
79
MigrationScript name contents ->
78
80
executeMigration con verbose name contents
79
81
MigrationFile name path ->
80
82
executeMigration con verbose name =<< BS. readFile path
81
83
MigrationValidation validationCmd ->
82
84
executeValidation con verbose validationCmd
85
+ MigrationCommands commands ->
86
+ runMigrations verbose con commands
87
+
88
+ -- | Execute a sequence of migrations
89
+ --
90
+ -- Returns 'MigrationSuccess' if all of the provided 'MigrationCommand's
91
+ -- execute without error. If an error occurs, execution is stopped and the
92
+ -- 'MigrationError' is returned.
93
+ --
94
+ -- It is recommended to wrap 'runMigrations' inside a database transaction.
95
+ runMigrations
96
+ :: Bool
97
+ -- ^ Run in verbose mode
98
+ -> Connection
99
+ -- ^ The postgres connection to use
100
+ -> [MigrationCommand ]
101
+ -- ^ The commands to run
102
+ -> IO (MigrationResult String )
103
+ runMigrations verbose con commands =
104
+ sequenceMigrations [runMigration (MigrationContext c verbose con) | c <- commands]
105
+
106
+ -- | Run a sequence of contexts, stopping on the first failure
107
+ sequenceMigrations :: Monad m => [m (MigrationResult e )] -> m (MigrationResult e )
108
+ sequenceMigrations = \ case
109
+ [] -> return MigrationSuccess
110
+ c: cs -> do
111
+ r <- c
112
+ case r of
113
+ MigrationError s -> return (MigrationError s)
114
+ MigrationSuccess -> sequenceMigrations cs
83
115
84
116
-- | Executes all SQL-file based migrations located in the provided 'dir'
85
117
-- in alphabetical order.
86
118
executeDirectoryMigration :: Connection -> Bool -> FilePath -> IO (MigrationResult String )
87
119
executeDirectoryMigration con verbose dir =
88
120
scriptsInDirectory dir >>= go
89
121
where
90
- go [] = return MigrationSuccess
91
- go (f: fs) = do
92
- r <- executeMigration con verbose f =<< BS. readFile (dir ++ " /" ++ f)
93
- case r of
94
- MigrationError _ ->
95
- return r
96
- MigrationSuccess ->
97
- go fs
122
+ go fs = sequenceMigrations (executeMigrationFile <$> fs)
123
+ executeMigrationFile f = executeMigration con verbose f =<< BS. readFile (dir ++ " /" ++ f)
98
124
99
125
-- | Lists all files in the given 'FilePath' 'dir' in alphabetical order.
100
126
scriptsInDirectory :: FilePath -> IO [String ]
@@ -145,6 +171,7 @@ initializeSchema con verbose = do
145
171
-- * 'MigrationScript': validate the presence and checksum of the given script.
146
172
-- * 'MigrationFile': validate the presence and checksum of the given file.
147
173
-- * 'MigrationValidation': always succeeds.
174
+ -- * 'MigrationCommands': validates all the sub-commands stopping at the first failure.
148
175
executeValidation :: Connection -> Bool -> MigrationCommand -> IO (MigrationResult String )
149
176
executeValidation con verbose cmd = case cmd of
150
177
MigrationInitialization ->
@@ -159,6 +186,8 @@ executeValidation con verbose cmd = case cmd of
159
186
validate name =<< BS. readFile path
160
187
MigrationValidation _ ->
161
188
return MigrationSuccess
189
+ MigrationCommands cs ->
190
+ sequenceMigrations (executeValidation con verbose <$> cs)
162
191
where
163
192
validate name contents =
164
193
checkScript con name (md5Hash contents) >>= \ case
@@ -172,13 +201,8 @@ executeValidation con verbose cmd = case cmd of
172
201
when verbose $ putStrLn $ " Checksum mismatch:\t " ++ name
173
202
return (MigrationError $ " Checksum mismatch: " ++ name)
174
203
175
- goScripts _ [] = return MigrationSuccess
176
- goScripts path (x: xs) =
177
- (validate x =<< BS. readFile (path ++ " /" ++ x)) >>= \ case
178
- e@ (MigrationError _) ->
179
- return e
180
- MigrationSuccess ->
181
- goScripts path xs
204
+ goScripts path xs = sequenceMigrations (goScript path <$> xs)
205
+ goScript path x = validate x =<< BS. readFile (path ++ " /" ++ x)
182
206
183
207
-- | Checks the status of the script with the given name 'name'.
184
208
-- If the script has already been executed, the checksum of the script
@@ -227,8 +251,17 @@ data MigrationCommand
227
251
-- ^ Executes a migration based on the provided bytestring.
228
252
| MigrationValidation MigrationCommand
229
253
-- ^ Validates the provided MigrationCommand.
254
+ | MigrationCommands [MigrationCommand ]
255
+ -- ^ Performs a series of 'MigrationCommand's in sequence.
230
256
deriving (Show , Eq , Read , Ord )
231
257
258
+ instance Monoid MigrationCommand where
259
+ mempty = MigrationCommands []
260
+ mappend (MigrationCommands xs) (MigrationCommands ys) = MigrationCommands (xs ++ ys)
261
+ mappend (MigrationCommands xs) y = MigrationCommands (xs ++ [y])
262
+ mappend x (MigrationCommands ys) = MigrationCommands (x : ys)
263
+ mappend x y = MigrationCommands [x, y]
264
+
232
265
-- | A sum-type denoting the result of a single migration.
233
266
data CheckScriptResult
234
267
= ScriptOk
0 commit comments