@@ -8,8 +8,13 @@ Stability: experimental
8This module defines a `Migration` type to safely
8This module defines a `Migration` type to safely
9change the schema of your database over time. Let's see an example!
9change the schema of your database over time. Let's see an example!
11+First turn on some extensions.
11>>> :set -XDataKinds -XOverloadedLabels
13>>> :set -XDataKinds -XOverloadedLabels
12>>> :set -XOverloadedStrings -XFlexibleContexts -XTypeOperators
14>>> :set -XOverloadedStrings -XFlexibleContexts -XTypeOperators
16+Next, let's define our `TableType`s.
14type UsersTable =
19type UsersTable =
15 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
20 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
@@ -20,7 +25,7 @@ type UsersTable =
22type EmailsTable =
27type EmailsTable =
23− '[ "pk_emails" ::: 'PrimaryKey '["id"]
28+ '[ "pk_emails" ::: 'PrimaryKey '["id"]
24 , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
29 , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
26 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
31 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
@@ -29,6 +34,8 @@ type EmailsTable =
37+Now we can define some `Migration`s to make our tables.
34 makeUsers :: Migration Definition (Public '[]) '["public" ::: '["users" ::: 'Table UsersTable]]
41 makeUsers :: Migration Definition (Public '[]) '["public" ::: '["users" ::: 'Table UsersTable]]
@@ -36,7 +43,7 @@ let
36 { name = "make users table"
43 { name = "make users table"
37 , up = createTable #users
44 , up = createTable #users
38 ( serial `as` #id :*
45 ( serial `as` #id :*
39− (text & notNullable) `as` #name )
46+ notNullable text `as` #name )
40 ( primaryKey #id `as` #pk_users )
47 ( primaryKey #id `as` #pk_users )
41 , down = dropTable #users
48 , down = dropTable #users
@@ -50,8 +57,8 @@ let
50 { name = "make emails table"
57 { name = "make emails table"
51 , up = createTable #emails
58 , up = createTable #emails
52 ( serial `as` #id :*
59 ( serial `as` #id :*
53− (int & notNullable) `as` #user_id :*
60+ notNullable int `as` #user_id :*
54− (text & nullable) `as` #email )
61+ nullable text `as` #email )
55 ( primaryKey #id `as` #pk_emails :*
62 ( primaryKey #id `as` #pk_emails :*
56 foreignKey #user_id #users #id
63 foreignKey #user_id #users #id
57 OnDeleteCascade OnUpdateCascade `as` #fk_user_id )
64 OnDeleteCascade OnUpdateCascade `as` #fk_user_id )
@@ -59,20 +66,11 @@ let
62−Now that we have a couple migrations we can chain them together.
69+Now that we have a couple migrations we can chain them together into an `AlignedList`.
64>>> let migrations = makeUsers :>> makeEmails :>> Done
71>>> let migrations = makeUsers :>> makeEmails :>> Done
66−>>> :{
73+Now run the migrations.
69− :: Has "migrations" schemas MigrationsSchema
70− => PQ schemas schemas IO ()
72− result <- runQuery (select Star (from (table (#migrations ! #schema_migrations `as` #m))))
73− num <- ntuples result
77>>> import Control.Monad.IO.Class
75>>> import Control.Monad.IO.Class
@@ -117,6 +115,10 @@ Migrations already run:
117Migrations left to run:
115Migrations left to run:
118 - make users table
116 - make users table
119 - make emails table
117 - make emails table
119+In addition to enabling `Migration`s using pure SQL `Definition`s for
120+the `up` and `down` instructions, you can also perform impure `IO` actions
121+by using a `Migration`s over the `Terminally` `PQ` `IO` category.
121{-# LANGUAGE
123{-# LANGUAGE
122 DataKinds
124 DataKinds
@@ -206,9 +208,9 @@ instance Migratory Definition where
206 migrateUp = migrateUp . alignedMap pureMigration
208 migrateUp = migrateUp . alignedMap pureMigration
207 migrateDown = migrateDown . alignedMap pureMigration
209 migrateDown = migrateDown . alignedMap pureMigration
209−{- | `Terminally` turns an indexed monad transformer into a category by
211+{- | `Terminally` turns an indexed monad transformer and the monad it transforms
210−restricting the return type to `()` and permuting the type variables.
212+into a category by restricting the return type to @()@ and permuting the type variables.
211−This is similar to how applying a monad to `()` yields a monoid.
213+This is similar to how applying a monad to @()@ yields a monoid.
212Since a `Terminally` action has a trivial return value, the only reason
214Since a `Terminally` action has a trivial return value, the only reason
213to run one is for the side effects, in particular database effects.
215to run one is for the side effects, in particular database effects.
@@ -224,8 +226,9 @@ instance
224 id = Terminally (return ())
226 id = Terminally (return ())
225 Terminally g . Terminally f = Terminally $ pqThen g f
227 Terminally g . Terminally f = Terminally $ pqThen g f
227−-- | terminally ignores the output of a computation, returning `()` and
229+-- | `terminally` ignores the output of a computation, returning @()@ and
228−-- wrapping it up in a `Terminally`.
230+-- wrapping it up into a `Terminally`. You can lift an action in the base monad
231+-- by using @terminally . lift@.
229terminally
232terminally
230 :: Functor (trans x0 x1 monad)
233 :: Functor (trans x0 x1 monad)
231 => trans x0 x1 monad ignore
234 => trans x0 x1 monad ignore