postgresql-transactional
is a simple monadic wrapper around the SQL
primitives introduced by the postgresql-simple package. It provides
simple and predictable semantics for database operations, enforces awareness of
Postgres's transactional nature at API boundaries, and obviates the need for
transaction boilerplate in SQL queries.
Though the primitives provided by the postgresql-simple package are
fast and powerful, their interface is (by design) very basic: specifically, all
query functions take a shared Connection
parameter and operate in the IO
monad.
query :: FromRow r => Connection -> Query -> IO [r]
execute :: ToRow q => Connection -> Query -> q -> IO Int64
By virtue of the fact that (usually) all queries in a given scope are routed
through a single Connection
, we can abstract away the shared Connection
parameter by wrapping a ReaderT Connection
in a monad transformer:
newtype PGTransactionT m a =
PGTransactionT (ReaderT Postgres.Connection m a)
deriving (Functor, Applicative, Monad, MonadTrans, MonadIO,
MonadReader Postgres.Connection)
type PGTransaction a = PGTransactionT IO a
In the common case, the m
parameter will simply be IO
. The library provides
the type alias type PGTransaction a = PGTransactionT IO a
to simplify type
signatures in these cases.
We can then reimplement our query functions in a more natural fashion:
query :: (FromRow r, MonadIO m) => Query -> PGTransactionT m [a]
execute :: (ToRow q, MonadIO m) => Query -> q -> PGTransactionT m Int64
And we can then use the postgresql-simple withTransaction
function
to provide runPGTransaction
, which executes a given PGTransactionT
block
with rollback semantics:
runPGTransaction :: MonadBaseControl IO m => PGTransactionT m a -> Postgres.Connection -> m a
Use of the MonadBaseControl IO m
constraint leaves open the option of
embedding additional effects with the m
parameter, such as logging, state, or
error-handling.
We also provide a PGTagged
monad transformer that is equivalent to PGTransaction
, but includes
a phantom type in each relevant type signature that indicates whether said function has read-only
or write-enabled effects. This can be useful when dispatching read-only queries to Postgres replicas.
-
Docs Complete documenation for all parts of Helium can be found at docs.helium.com.
-
chat.helium.com - If you have questions or ideas about how to use this code - or any part of Helium - head over the chat.helium.com. We're standing by to help.
postgresql-transactional
was extracted from a production Haskell project at
Helium. It is open-source software © Helium Systems, Inc., and
released to the public under the terms of the MIT license.