Skip to content
This repository was archived by the owner on Sep 20, 2021. It is now read-only.

Commit 9a440d4

Browse files
committed
Support a user defined name for the schema_migrations table
Added runMigration' and runMigrations'. These two function support an extra parameter over the non-prime ones. This param is the name of the scschema_migrations table that the user has selected. Adding two new functions like this means there are no breaking changes for existing users.
1 parent 1229914 commit 9a440d4

File tree

3 files changed

+110
-56
lines changed

3 files changed

+110
-56
lines changed

postgresql-simple-migration.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: postgresql-simple-migration
2-
version: 0.1.14.0
2+
version: 0.1.14.1
33
synopsis: PostgreSQL Schema Migrations
44
homepage: https://github.com/ameingast/postgresql-simple-migration
55
Bug-reports: https://github.com/ameingast/postgresql-simple-migration/issues

src/Database/PostgreSQL/Simple/Migration.hs

+76-35
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@
2121
module Database.PostgreSQL.Simple.Migration
2222
(
2323
-- * Migration actions
24-
runMigration
24+
runMigration'
25+
, runMigration
2526
, runMigrations
27+
, runMigrations'
2628
, sequenceMigrations
2729

2830
-- * Migration types
2931
, MigrationContext(..)
32+
, MigrationContext'(..)
3033
, MigrationCommand(..)
3134
, MigrationResult(..)
3235
, ScriptName
@@ -45,6 +48,7 @@ import Control.Applicative ((<$>), (<*>))
4548
import Control.Monad (void, when)
4649
import qualified Crypto.Hash.MD5 as MD5 (hash)
4750
import qualified Data.ByteString as BS (ByteString, readFile)
51+
import qualified Data.ByteString.Char8 as BS8 (unpack)
4852
import qualified Data.ByteString.Base64 as B64 (encode)
4953
import Data.Foldable (Foldable)
5054
import Data.List (isPrefixOf, sort)
@@ -71,19 +75,23 @@ import System.Directory (getDirectoryContents)
7175
--
7276
-- It is recommended to wrap 'runMigration' inside a database transaction.
7377
runMigration :: MigrationContext -> IO (MigrationResult String)
74-
runMigration (MigrationContext cmd verbose con) = case cmd of
78+
runMigration (MigrationContext cmd verbose con) =
79+
runMigration' (MigrationContext' cmd verbose con "schema_migrations")
80+
81+
runMigration' :: MigrationContext' -> IO (MigrationResult String)
82+
runMigration' (MigrationContext' cmd verbose con tableName) = case cmd of
7583
MigrationInitialization ->
76-
initializeSchema con verbose >> return MigrationSuccess
84+
initializeSchema con tableName verbose >> return MigrationSuccess
7785
MigrationDirectory path ->
78-
executeDirectoryMigration con verbose path
86+
executeDirectoryMigration con tableName verbose path
7987
MigrationScript name contents ->
80-
executeMigration con verbose name contents
88+
executeMigration con tableName verbose name contents
8189
MigrationFile name path ->
82-
executeMigration con verbose name =<< BS.readFile path
90+
executeMigration con tableName verbose name =<< BS.readFile path
8391
MigrationValidation validationCmd ->
84-
executeValidation con verbose validationCmd
92+
executeValidation con tableName verbose validationCmd
8593
MigrationCommands commands ->
86-
runMigrations verbose con commands
94+
runMigrations' verbose con commands tableName
8795

8896
-- | Execute a sequence of migrations
8997
--
@@ -100,8 +108,27 @@ runMigrations
100108
-> [MigrationCommand]
101109
-- ^ The commands to run
102110
-> IO (MigrationResult String)
103-
runMigrations verbose con commands =
104-
sequenceMigrations [runMigration (MigrationContext c verbose con) | c <- commands]
111+
runMigrations verbose con commands = runMigrations' verbose con commands "schema_migrations"
112+
113+
-- | Execute a sequence of migrations
114+
--
115+
-- Returns 'MigrationSuccess' if all of the provided 'MigrationCommand's
116+
-- execute without error. If an error occurs, execution is stopped and the
117+
-- 'MigrationError' is returned.
118+
--
119+
-- It is recommended to wrap 'runMigrations' inside a database transaction.
120+
runMigrations'
121+
:: Bool
122+
-- ^ Run in verbose mode
123+
-> Connection
124+
-- ^ The postgres connection to use
125+
-> [MigrationCommand]
126+
-- ^ The commands to run
127+
-> BS.ByteString
128+
-- ^ The schema_migrations table name
129+
-> IO (MigrationResult String)
130+
runMigrations' verbose con commands tableName =
131+
sequenceMigrations [runMigration' (MigrationContext' c verbose con tableName) | c <- commands]
105132

106133
-- | Run a sequence of contexts, stopping on the first failure
107134
sequenceMigrations :: Monad m => [m (MigrationResult e)] -> m (MigrationResult e)
@@ -115,12 +142,12 @@ sequenceMigrations = \case
115142

116143
-- | Executes all SQL-file based migrations located in the provided 'dir'
117144
-- in alphabetical order.
118-
executeDirectoryMigration :: Connection -> Bool -> FilePath -> IO (MigrationResult String)
119-
executeDirectoryMigration con verbose dir =
145+
executeDirectoryMigration :: Connection -> BS.ByteString -> Bool -> FilePath -> IO (MigrationResult String)
146+
executeDirectoryMigration con tableName verbose dir =
120147
scriptsInDirectory dir >>= go
121148
where
122149
go fs = sequenceMigrations (executeMigrationFile <$> fs)
123-
executeMigrationFile f = executeMigration con verbose f =<< BS.readFile (dir ++ "/" ++ f)
150+
executeMigrationFile f = executeMigration con tableName verbose f =<< BS.readFile (dir ++ "/" ++ f)
124151

125152
-- | Lists all files in the given 'FilePath' 'dir' in alphabetical order.
126153
scriptsInDirectory :: FilePath -> IO [String]
@@ -130,10 +157,10 @@ scriptsInDirectory dir =
130157

131158
-- | Executes a generic SQL migration for the provided script 'name' with
132159
-- content 'contents'.
133-
executeMigration :: Connection -> Bool -> ScriptName -> BS.ByteString -> IO (MigrationResult String)
134-
executeMigration con verbose name contents = do
160+
executeMigration :: Connection -> BS.ByteString -> Bool -> ScriptName -> BS.ByteString -> IO (MigrationResult String)
161+
executeMigration con tableName verbose name contents = do
135162
let checksum = md5Hash contents
136-
checkScript con name checksum >>= \case
163+
checkScript con tableName name checksum >>= \case
137164
ScriptOk -> do
138165
when verbose $ putStrLn $ "Ok:\t" ++ name
139166
return MigrationSuccess
@@ -146,15 +173,15 @@ executeMigration con verbose name contents = do
146173
when verbose $ putStrLn $ "Fail:\t" ++ name
147174
return (MigrationError name)
148175
where
149-
q = "insert into schema_migrations(filename, checksum) values(?, ?)"
176+
q = "insert into " <> Query tableName <> "(filename, checksum) values(?, ?)"
150177

151178
-- | Initializes the database schema with a helper table containing
152179
-- meta-information about executed migrations.
153-
initializeSchema :: Connection -> Bool -> IO ()
154-
initializeSchema con verbose = do
180+
initializeSchema :: Connection -> BS.ByteString -> Bool -> IO ()
181+
initializeSchema con tableName verbose = do
155182
when verbose $ putStrLn "Initializing schema"
156183
void $ execute_ con $ mconcat
157-
[ "create table if not exists schema_migrations "
184+
[ "create table if not exists " <> Query tableName <> " "
158185
, "( filename varchar(512) not null"
159186
, ", checksum varchar(32) not null"
160187
, ", executed_at timestamp without time zone not null default now() "
@@ -172,12 +199,14 @@ initializeSchema con verbose = do
172199
-- * 'MigrationFile': validate the presence and checksum of the given file.
173200
-- * 'MigrationValidation': always succeeds.
174201
-- * 'MigrationCommands': validates all the sub-commands stopping at the first failure.
175-
executeValidation :: Connection -> Bool -> MigrationCommand -> IO (MigrationResult String)
176-
executeValidation con verbose cmd = case cmd of
202+
executeValidation :: Connection -> BS.ByteString -> Bool -> MigrationCommand -> IO (MigrationResult String)
203+
executeValidation con tableName' verbose cmd =
204+
let tableName = BS8.unpack tableName' in
205+
case cmd of
177206
MigrationInitialization ->
178-
existsTable con "schema_migrations" >>= \r -> return $ if r
207+
existsTable con tableName >>= \r -> return $ if r
179208
then MigrationSuccess
180-
else MigrationError "No such table: schema_migrations"
209+
else MigrationError $ "No such table: " <> tableName
181210
MigrationDirectory path ->
182211
scriptsInDirectory path >>= goScripts path
183212
MigrationScript name contents ->
@@ -187,10 +216,10 @@ executeValidation con verbose cmd = case cmd of
187216
MigrationValidation _ ->
188217
return MigrationSuccess
189218
MigrationCommands cs ->
190-
sequenceMigrations (executeValidation con verbose <$> cs)
219+
sequenceMigrations (executeValidation con tableName' verbose <$> cs)
191220
where
192221
validate name contents =
193-
checkScript con name (md5Hash contents) >>= \case
222+
checkScript con tableName' name (md5Hash contents) >>= \case
194223
ScriptOk -> do
195224
when verbose $ putStrLn $ "Ok:\t" ++ name
196225
return MigrationSuccess
@@ -209,8 +238,8 @@ executeValidation con verbose cmd = case cmd of
209238
-- is compared against the one that was executed.
210239
-- If there is no matching script entry in the database, the script
211240
-- will be executed and its meta-information will be recorded.
212-
checkScript :: Connection -> ScriptName -> Checksum -> IO CheckScriptResult
213-
checkScript con name checksum =
241+
checkScript :: Connection -> BS.ByteString -> ScriptName -> Checksum -> IO CheckScriptResult
242+
checkScript con tableName name checksum =
214243
query con q (Only name) >>= \case
215244
[] ->
216245
return ScriptNotExecuted
@@ -220,7 +249,7 @@ checkScript con name checksum =
220249
return (ScriptModified actualChecksum)
221250
where
222251
q = mconcat
223-
[ "select checksum from schema_migrations "
252+
[ "select checksum from " <> Query tableName <> " "
224253
, "where filename = ? limit 1"
225254
]
226255

@@ -289,20 +318,32 @@ data MigrationResult a
289318

290319
-- | The 'MigrationContext' provides an execution context for migrations.
291320
data MigrationContext = MigrationContext
292-
{ migrationContextCommand :: MigrationCommand
321+
{ migrationContextCommand :: !MigrationCommand
322+
-- ^ The action that will be performed by 'runMigration'
323+
, migrationContextVerbose :: !Bool
324+
-- ^ Verbosity of the library.
325+
, migrationContextConnection :: !Connection
326+
-- ^ The PostgreSQL connection to use for migrations.
327+
}
328+
329+
-- | The 'MigrationContext'' provides an execution context for migrations, with additional options to MigrationContext
330+
data MigrationContext' = MigrationContext'
331+
{ migrationContextCommand' :: !MigrationCommand
293332
-- ^ The action that will be performed by 'runMigration'
294-
, migrationContextVerbose :: Bool
333+
, migrationContextVerbose' :: !Bool
295334
-- ^ Verbosity of the library.
296-
, migrationContextConnection :: Connection
335+
, migrationContextConnection' :: !Connection
297336
-- ^ The PostgreSQL connection to use for migrations.
337+
, migrationTableName :: !BS.ByteString
338+
-- ^ The name of the table that stores the migrations
298339
}
299340

300341
-- | Produces a list of all executed 'SchemaMigration's.
301-
getMigrations :: Connection -> IO [SchemaMigration]
302-
getMigrations = flip query_ q
342+
getMigrations :: Connection -> BS.ByteString -> IO [SchemaMigration]
343+
getMigrations con tableName = query_ con q
303344
where q = mconcat
304345
[ "select filename, checksum, executed_at "
305-
, "from schema_migrations order by executed_at asc"
346+
, "from " <> Query tableName <> " order by executed_at asc"
306347
]
307348

308349
-- | A product type representing a single, executed 'SchemaMigration'.

src/Main.hs

+33-20
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ module Main (
2121
import Control.Applicative
2222
#endif
2323
import Control.Exception
24+
import qualified Data.ByteString as BS (ByteString)
2425
import qualified Data.ByteString.Char8 as BS8 (pack)
2526
import Database.PostgreSQL.Simple (SqlError (..),
2627
connectPostgreSQL,
2728
withTransaction)
2829
import Database.PostgreSQL.Simple.Migration (MigrationCommand (..),
29-
MigrationContext (..),
30+
MigrationContext' (..),
3031
MigrationResult (..),
31-
runMigration)
32+
runMigration')
3233
import System.Environment (getArgs)
3334
import System.Exit (exitFailure, exitSuccess)
3435

@@ -69,26 +70,29 @@ run :: Maybe Command -> Bool-> IO ()
6970
run Nothing _ = printUsage >> exitFailure
7071
run (Just cmd) verbose =
7172
handleResult =<< case cmd of
72-
Initialize url -> do
73+
Initialize url tableName -> do
7374
con <- connectPostgreSQL (BS8.pack url)
74-
withTransaction con $ runMigration $ MigrationContext
75-
MigrationInitialization verbose con
76-
Migrate url dir -> do
75+
withTransaction con $ runMigration' $ MigrationContext'
76+
MigrationInitialization verbose con tableName
77+
Migrate url dir tableName -> do
7778
con <- connectPostgreSQL (BS8.pack url)
78-
withTransaction con $ runMigration $ MigrationContext
79-
(MigrationDirectory dir) verbose con
80-
Validate url dir -> do
79+
withTransaction con $ runMigration' $ MigrationContext'
80+
(MigrationDirectory dir) verbose con tableName
81+
Validate url dir tableName -> do
8182
con <- connectPostgreSQL (BS8.pack url)
82-
withTransaction con $ runMigration $ MigrationContext
83-
(MigrationValidation (MigrationDirectory dir)) verbose con
83+
withTransaction con $ runMigration' $ MigrationContext'
84+
(MigrationValidation (MigrationDirectory dir)) verbose con tableName
8485
where
8586
handleResult MigrationSuccess = exitSuccess
8687
handleResult (MigrationError _) = exitFailure
8788

8889
parseCommand :: [String] -> Maybe Command
89-
parseCommand ("init":url:_) = Just (Initialize url)
90-
parseCommand ("migrate":url:dir:_) = Just (Migrate url dir)
91-
parseCommand ("validate":url:dir:_) = Just (Validate url dir)
90+
parseCommand ("init":url:tableName:_) = Just (Initialize url (BS8.pack tableName))
91+
parseCommand ("migrate":url:dir:tableName:_) = Just (Migrate url dir (BS8.pack tableName))
92+
parseCommand ("validate":url:dir:tableName:_) = Just (Validate url dir (BS8.pack tableName))
93+
parseCommand ("init":url:_) = Just (Initialize url "schema_migrations")
94+
parseCommand ("migrate":url:dir:_) = Just (Migrate url dir "schema_migrations")
95+
parseCommand ("validate":url:dir:_) = Just (Validate url dir "schema_migrations")
9296
parseCommand _ = Nothing
9397

9498
printUsage :: IO ()
@@ -98,25 +102,34 @@ printUsage = do
98102
putStrLn " -h Print help text"
99103
putStrLn " -q Enable quiet mode"
100104
putStrLn " Commands:"
101-
putStrLn " init <con>"
105+
putStrLn " init <con> {migrations table name}"
102106
putStrLn " Initialize the database. Required to be run"
103107
putStrLn " at least once."
104-
putStrLn " migrate <con> <directory>"
108+
putStrLn " {migrations table name} is the optiona name."
109+
putStrLn " for the migrations table. This defaults to"
110+
putStrLn " `schema_migrations`."
111+
putStrLn " migrate <con> <directory> {migrations table name}"
105112
putStrLn " Execute all SQL scripts in the provided"
106113
putStrLn " directory in alphabetical order."
107114
putStrLn " Scripts that have already been executed are"
108115
putStrLn " ignored. If a script was changed since the"
109116
putStrLn " time of its last execution, an error is"
110117
putStrLn " raised."
111-
putStrLn " validate <con> <directory>"
118+
putStrLn " {migrations table name} is the optiona name."
119+
putStrLn " for the migrations table. This defaults to"
120+
putStrLn " `schema_migrations`."
121+
putStrLn " validate <con> <directory> {migrations table name}"
112122
putStrLn " Validate all SQL scripts in the provided"
113123
putStrLn " directory."
124+
putStrLn " {migrations table name} is the optiona name."
125+
putStrLn " for the migrations table. This defaults to"
126+
putStrLn " `schema_migrations`."
114127
putStrLn " The <con> parameter is based on libpq connection string"
115128
putStrLn " syntax. Detailled information is available here:"
116129
putStrLn " <http://www.postgresql.org/docs/9.3/static/libpq-connect.html>"
117130

118131
data Command
119-
= Initialize String
120-
| Migrate String FilePath
121-
| Validate String FilePath
132+
= Initialize String BS.ByteString
133+
| Migrate String FilePath BS.ByteString
134+
| Validate String FilePath BS.ByteString
122135
deriving (Show, Eq, Read, Ord)

0 commit comments

Comments
 (0)