diff --git a/hasql-transaction.cabal b/hasql-transaction.cabal
--- a/hasql-transaction.cabal
+++ b/hasql-transaction.cabal
@@ -1,7 +1,7 @@
 name:
   hasql-transaction
 version:
-  0.3.1
+  0.4.2
 category:
   Hasql, Database, PostgreSQL
 synopsis:
@@ -47,7 +47,7 @@
   exposed-modules:
     Hasql.Transaction
   build-depends:
-    hasql >= 0.15 && < 0.16,
+    hasql >= 0.17 && < 0.20,
     -- database:
     postgresql-error-codes >= 1 && < 2,
     -- data:
@@ -57,6 +57,7 @@
     contravariant >= 1.3 && < 2,
     contravariant-extras >= 0.3 && < 0.4,
     either >= 4.4.1 && < 5,
+    mtl >= 2 && < 3,
     transformers >= 0.3 && < 0.5,
     -- general:
     base-prelude >= 0.1.19 && < 0.2
diff --git a/library/Hasql/Transaction.hs b/library/Hasql/Transaction.hs
--- a/library/Hasql/Transaction.hs
+++ b/library/Hasql/Transaction.hs
@@ -6,13 +6,14 @@
   -- * Transaction monad
   Transaction,
   run,
+  sql,
   query,
 )
 where
 
 import Hasql.Transaction.Prelude
-import qualified Hasql.Connection as Connection
 import qualified Hasql.Query as Query
+import qualified Hasql.Session as Session
 import qualified Hasql.Transaction.Queries as Queries
 import qualified PostgreSQL.ErrorCodes as ErrorCodes
 
@@ -24,7 +25,7 @@
 -- while automatically retrying the transaction in case of conflicts.
 -- Thus this abstraction closely reproduces the behaviour of 'STM'.
 newtype Transaction a =
-  Transaction (ReaderT (Connection.Connection, IORef Int) (EitherT Query.ResultsError IO) a)
+  Transaction (Session.Session a)
   deriving (Functor, Applicative, Monad)
 
 -- |
@@ -56,27 +57,22 @@
   deriving (Show, Eq, Ord, Enum, Bounded)
 
 -- |
--- Execute the transaction using the provided isolation level, mode and a database connection.
+-- Execute the transaction using the provided isolation level, mode and database connection.
 {-# INLINABLE run #-}
-run :: Transaction a -> IsolationLevel -> Mode -> Connection.Connection -> IO (Either Query.ResultsError a)
-run (Transaction tx) isolation mode connection =
-  runEitherT $ do
-    EitherT $ Query.run (Queries.beginTransaction mode') () connection
-    counterRef <- lift $ newIORef 0
-    resultEither <- lift $ runEitherT $ runReaderT tx (connection, counterRef)
+run :: Transaction a -> IsolationLevel -> Mode -> Session.Session a
+run (Transaction session) isolation mode =
+  do
+    Session.query () (Queries.beginTransaction mode')
+    resultEither <- tryError session
     case resultEither of
-      Left (Query.ResultError (Query.ServerError code _ _ _))
-        | code == ErrorCodes.serialization_failure ->
-          EitherT $ run (Transaction tx) isolation mode connection
-      _ -> do
-        result <- EitherT $ pure resultEither
-        let
-          query =
-            if commit
-              then Queries.commitTransaction
-              else Queries.abortTransaction
-          in
-            EitherT $ Query.run query () connection
+      Left error ->
+        case error of
+          Session.ResultError (Session.ServerError code _ _ _) | code == ErrorCodes.serialization_failure ->
+            run (Transaction session) isolation mode
+          _ -> 
+            throwError error
+      Right result -> do
+        Session.query () $ bool Queries.abortTransaction Queries.commitTransaction commit
         pure result
   where
     mode' =
@@ -88,9 +84,17 @@
         WriteWithoutCommitting -> (True, False)
 
 -- |
--- Execute a query in the context of a transaction.
-{-# INLINABLE query #-}
+-- Possibly a multi-statement query,
+-- which however cannot be parameterized or prepared,
+-- nor can any results of it be collected.
+{-# INLINE sql #-}
+sql :: ByteString -> Transaction ()
+sql sql =
+  Transaction $ Session.sql sql
+
+-- |
+-- Parameters and a specification of the parametric query to apply them to.
+{-# INLINE query #-}
 query :: a -> Query.Query a b -> Transaction b
 query params query =
-  Transaction $ ReaderT $ \(connection, _) -> EitherT $
-  Query.run query params connection
+  Transaction $ Session.query params query
diff --git a/library/Hasql/Transaction/Prelude.hs b/library/Hasql/Transaction/Prelude.hs
--- a/library/Hasql/Transaction/Prelude.hs
+++ b/library/Hasql/Transaction/Prelude.hs
@@ -1,6 +1,7 @@
 module Hasql.Transaction.Prelude
 ( 
   module Exports,
+  tryError,
 )
 where
 
@@ -17,6 +18,10 @@
 import Control.Monad.Trans.Reader as Exports hiding (liftCallCC, liftCatch)
 import Control.Monad.Trans.State.Strict as Exports hiding (liftCallCC, liftCatch, liftListen, liftPass)
 
+-- mtl
+-------------------------
+import Control.Monad.Error.Class as Exports (MonadError (..))
+
 -- contravariant
 -------------------------
 import Data.Functor.Contravariant as Exports
@@ -34,3 +39,7 @@
 -- bytestring
 -------------------------
 import Data.ByteString as Exports (ByteString)
+
+tryError :: MonadError e m => m a -> m (Either e a)
+tryError m =
+  catchError (liftM Right m) (return . Left)
diff --git a/library/Hasql/Transaction/Queries.hs b/library/Hasql/Transaction/Queries.hs
--- a/library/Hasql/Transaction/Queries.hs
+++ b/library/Hasql/Transaction/Queries.hs
@@ -21,7 +21,7 @@
 
 beginTransaction :: TransactionMode -> HQ.Query () ()
 beginTransaction (isolation, write) =
-  HQ.Query sql HE.unit HD.unit True
+  HQ.statement sql HE.unit HD.unit True
   where
     sql =
       TB.toByteString $
@@ -43,11 +43,11 @@
 
 commitTransaction :: HQ.Query () ()
 commitTransaction =
-  HQ.Query "commit" HE.unit HD.unit True
+  HQ.statement "commit" HE.unit HD.unit True
 
 abortTransaction :: HQ.Query () ()
 abortTransaction =
-  HQ.Query "abort" HE.unit HD.unit True
+  HQ.statement "abort" HE.unit HD.unit True
 
 
 -- * Streaming
@@ -55,7 +55,7 @@
 
 declareCursor :: ByteString -> ByteString -> HE.Params a -> HQ.Query a ()
 declareCursor name sql encoder =
-  HQ.Query sql' encoder HD.unit False
+  HQ.statement sql' encoder HD.unit False
   where
     sql' =
       TB.toByteString $
@@ -63,11 +63,11 @@
 
 closeCursor :: HQ.Query ByteString ()
 closeCursor =
-  HQ.Query "CLOSE $1" (HE.value HE.bytea) HD.unit True
+  HQ.statement "CLOSE $1" (HE.value HE.bytea) HD.unit True
 
 fetchFromCursor :: (b -> a -> b) -> b -> HD.Row a -> HQ.Query (Int64, ByteString) b
 fetchFromCursor step init rowDec =
-  HQ.Query sql encoder decoder True
+  HQ.statement sql encoder decoder True
   where
     sql =
       "FETCH FORWARD $1 FROM $2"
