hasql
Safe HaskellNone
LanguageHaskell2010

Hasql.Session

Synopsis

Documentation

data Session a #

A sequence of operations to be executed in the context of a single database connection with exclusive access to it.

Construct sessions using helpers in this module such as statement, pipeline and script, or use onLibpqConnection for a low-level escape hatch.

To actually execute a Session use use, which manages concurrent access to the shared connection state and returns either a UseError or the result:

result <- Hasql.Connection.use connection mySession

The MonadError instance ranges over SessionError only - the failures that leave the connection usable. A failure that leaves the connection gone (ConnectionUseError) is not a value throwError can construct or catchError can see: it propagates through every later >>= untouched and reaches use whole. This is enforced structurally by this Monad instance rather than by a check any handler has to remember to make.

A caught error retains every connection-state change made before it - both branches below thread the state through, matching ExceptT e (State s) rather than StateT s (Either e). A statement whose PARSE succeeded and whose EXECUTE failed has truthfully been prepared on the server, so the cache entry survives the catch; only statement and pipeline revert it themselves, on an ordinary failure, since they alone know whether what they recorded was actually confirmed by the server.

Note: while most session errors are returned as values, user code executed inside a session may still throw exceptions, and an interruption from another thread (timeout, killThread) arrives the same way. Such an exception propagates out of use, which finishes the connection on the way out rather than trying to bring it back to a clean state.

Instances

Instances details
Applicative Session # 
Instance details

Defined in Hasql.Engine.Contexts.Session

Methods

pure :: a -> Session a #

(<*>) :: Session (a -> b) -> Session a -> Session b #

liftA2 :: (a -> b -> c) -> Session a -> Session b -> Session c #

(*>) :: Session a -> Session b -> Session b #

(<*) :: Session a -> Session b -> Session a #

Functor Session # 
Instance details

Defined in Hasql.Engine.Contexts.Session

Methods

fmap :: (a -> b) -> Session a -> Session b #

(<$) :: a -> Session b -> Session a #

Monad Session # 
Instance details

Defined in Hasql.Engine.Contexts.Session

Methods

(>>=) :: Session a -> (a -> Session b) -> Session b #

(>>) :: Session a -> Session b -> Session b #

return :: a -> Session a #

MonadIO Session # 
Instance details

Defined in Hasql.Engine.Contexts.Session

Methods

liftIO :: IO a -> Session a #

MonadError SessionError Session #

Ranges over SessionError only. throwError can only construct SessionUseError, and catchError only ever sees that constructor - ConnectionUseError flows past both untouched. See the note on the Session type.

Instance details

Defined in Hasql.Engine.Contexts.Session

pipeline :: Pipeline result -> Session result #

Execute a pipeline.

script :: Text -> Session () #

Possibly a multi-statement query, which however cannot be parameterized or prepared, nor can any results of it be collected.

statement :: params -> Statement params result -> Session result #

Execute a statement by providing parameters to it.

onLibpqConnection :: (Connection -> IO (Either UseError a, Connection)) -> Session a #

Execute an operation on the raw libpq connection possibly producing an error and updating the connection. This is a low-level escape hatch for custom integrations.

You can supply a new connection in the result to replace it in the running Hasql connection. The responsibility to close the old libpq connection is on you. Otherwise, just return the same connection you've received.

Producing a Left value will cause the session to fail with the given error. Regardless of success or failure, the connection will be replaced with the one you return.

Throwing exceptions is okay, but it costs the connection: use finishes the one it handed to the session and spends the handle. It finishes that one - not any replacement you returned before the exception, since an exception unwinds past the state carrying it. A replacement lost that way is yours to close, like the connection it displaced.

Restoring the connection is on you on the ordinary return path. Whatever protocol state you leave it in - pipeline mode still on, results undrained, a command in progress - is what the next session in the same use inherits, and what gets handed back to the pool afterwards. Nothing repairs it for you: the driver only ever finishes a connection it cannot vouch for, and it has no way of telling that this one is in that state. Returning a Left of ConnectionUseError is how you say so - use finishes the connection when it sees it.