diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+# v0.4.5.4
+
+- Widen the hasql bounds to support `hasql-2`
diff --git a/hasql-cursor-query.cabal b/hasql-cursor-query.cabal
--- a/hasql-cursor-query.cabal
+++ b/hasql-cursor-query.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: hasql-cursor-query
-version: 0.4.5.3
+version: 0.4.5.4
 category: Hasql, Database, PostgreSQL, Streaming
 synopsis: A declarative abstraction over PostgreSQL Cursor
 homepage: https://github.com/nikita-volkov/hasql-cursor-query
@@ -10,6 +10,8 @@
 copyright: (c) 2016, Nikita Volkov
 license: MIT
 license-file: LICENSE
+extra-doc-files:
+  CHANGELOG.md
 
 source-repository head
   type: git
@@ -64,6 +66,10 @@
 
 common test
   import: base
+  default-extensions:
+    BlockArguments
+    ImportQualifiedPost
+
   ghc-options:
     -threaded
     "-with-rtsopts=-N -I0 -qg"
@@ -90,25 +96,36 @@
     bytestring >=0.10 && <0.14,
     contravariant >=1.3 && <2,
     foldl >1 && <2,
-    hasql >=1.9 && <1.10,
+    hasql >=1.10 && <1.11 || >=2.0 && <2.1,
     hasql-cursor-transaction >=0.6 && <0.7,
-    hasql-transaction >=1.1 && <1.2,
+    hasql-transaction >=1.1 && <1.3,
     profunctors >=5 && <6,
+    text >=1 && <3,
 
-test-suite tasty
+test-suite test
   import: test
   type: exitcode-stdio-1.0
-  hs-source-dirs: tasty
+  hs-source-dirs: src/integration-tests
   main-is: Main.hs
   other-modules:
-    Main.CursorQueries
-    Main.IO
-    Main.Statements
+    Helpers.Adapters
+    Helpers.CursorQueries
+    Helpers.Hooks
+    Helpers.Scripts
+    Helpers.Statements
+    Specs.CursorQuerySpec
+    Specs.SpecHook
 
+  build-tool-depends:
+    hspec-discover:hspec-discover ^>=2.11.12
+
   build-depends:
     foldl >=1.2 && <2,
     hasql,
     hasql-cursor-query,
-    rebase <2,
-    tasty >=0.12 && <2,
-    tasty-hunit >=0.9 && <0.11,
+    hspec >=2.6 && <3,
+    pqi ^>=1.0,
+    pqi-ffi ^>=1.0,
+    pqi-native ^>=1.0,
+    rerebase >=1.15 && <2,
+    testcontainers-postgresql >=0.2 && <0.3,
diff --git a/library/Hasql/CursorQuery/Private/CursorTransactions.hs b/library/Hasql/CursorQuery/Private/CursorTransactions.hs
--- a/library/Hasql/CursorQuery/Private/CursorTransactions.hs
+++ b/library/Hasql/CursorQuery/Private/CursorTransactions.hs
@@ -1,6 +1,7 @@
 module Hasql.CursorQuery.Private.CursorTransactions where
 
 import qualified Control.Foldl as D
+import qualified Data.Text.Encoding as Text
 import qualified Hasql.CursorQuery.Private.CursorQuery as B
 import qualified Hasql.CursorQuery.Private.Decoders as I
 import Hasql.CursorQuery.Private.Prelude
@@ -36,5 +37,5 @@
 cursorQuery :: params -> B.CursorQuery params result -> G.CursorTransaction s result
 cursorQuery params (B.CursorQuery template encoder (B.ReducingDecoder rowDecoder rowsFold) batchSize) =
   do
-    cursor <- G.declareCursor template (G.encodedParams encoder params)
+    cursor <- G.declareCursor (Text.decodeUtf8 template) (G.encodedParams encoder params)
     fetchAndFoldCursor cursor batchSize rowDecoder rowsFold
diff --git a/library/Hasql/CursorQuery/Private/Prelude.hs b/library/Hasql/CursorQuery/Private/Prelude.hs
--- a/library/Hasql/CursorQuery/Private/Prelude.hs
+++ b/library/Hasql/CursorQuery/Private/Prelude.hs
@@ -57,6 +57,7 @@
 import GHC.Generics as Exports (Generic, Generic1)
 import GHC.IO.Exception as Exports
 import Numeric as Exports
+import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))
 import System.Environment as Exports
 import System.Exit as Exports
 import System.IO as Exports
@@ -68,4 +69,3 @@
 import Text.Printf as Exports (hPrintf, printf)
 import Text.Read as Exports (Read (..), readEither, readMaybe)
 import Unsafe.Coerce as Exports
-import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))
diff --git a/src/integration-tests/Helpers/Adapters.hs b/src/integration-tests/Helpers/Adapters.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Helpers/Adapters.hs
@@ -0,0 +1,30 @@
+module Helpers.Adapters
+  ( adapters,
+    byAdapter,
+    hook,
+  )
+where
+
+import Pqi qualified
+import Pqi.Ffi qualified
+import Pqi.Native qualified
+import Prelude
+import Test.Hspec
+
+adapters :: [Pqi.Adapter]
+adapters =
+  [ Pqi.Ffi.adapter,
+    Pqi.Native.adapter
+  ]
+
+-- | Run the given spec-building function once per available Pqi adapter,
+-- nesting each run under a @describe@ named after the adapter.
+byAdapter :: (Pqi.Adapter -> Spec) -> Spec
+byAdapter f =
+  for_ adapters \adapter ->
+    describe (toList (Pqi.name adapter)) (f adapter)
+
+hook :: SpecWith Pqi.Adapter -> Spec
+hook hookedSpec =
+  byAdapter \adapter ->
+    mapSubject (const adapter) hookedSpec
diff --git a/src/integration-tests/Helpers/CursorQueries.hs b/src/integration-tests/Helpers/CursorQueries.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Helpers/CursorQueries.hs
@@ -0,0 +1,35 @@
+module Helpers.CursorQueries where
+
+import Control.Foldl qualified as Foldl
+import Hasql.CursorQuery
+import Hasql.Decoders qualified as D
+import Hasql.Encoders qualified as E
+import Prelude
+
+countPGType :: CursorQuery () Int
+countPGType =
+  cursorQuery sql encoder decoder batchSize_10
+  where
+    sql =
+      "select oid::int8, typname::text from pg_type"
+    encoder =
+      E.noParams
+    decoder =
+      reducingDecoder rowDecoder Foldl.length
+      where
+        rowDecoder =
+          (,) <$> (D.column . D.nonNullable) D.int8 <*> (D.column . D.nonNullable) D.text
+
+selectOidAndTypeName :: CursorQuery () [(Int64, Text)]
+selectOidAndTypeName =
+  cursorQuery sql encoder decoder batchSize_10
+  where
+    sql =
+      "select oid::int8, typname::text from pg_type"
+    encoder =
+      E.noParams
+    decoder =
+      reducingDecoder rowDecoder Foldl.list
+      where
+        rowDecoder =
+          (,) <$> (D.column . D.nonNullable) D.int8 <*> (D.column . D.nonNullable) D.text
diff --git a/src/integration-tests/Helpers/Hooks.hs b/src/integration-tests/Helpers/Hooks.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Helpers/Hooks.hs
@@ -0,0 +1,19 @@
+-- | Hooks for Hspec.
+module Helpers.Hooks where
+
+import Data.Bool
+import Prelude hiding (Handler)
+import TestcontainersPostgresql qualified
+
+-- | Testing action in the scope of the host name and port of a running fresh isolated postgres server.
+type Handler = (Text, Word16) -> IO ()
+
+postgres17 :: Handler -> IO ()
+postgres17 handler =
+  TestcontainersPostgresql.run
+    TestcontainersPostgresql.Config
+      { forwardLogs = False,
+        tagName = "postgres:17",
+        auth = TestcontainersPostgresql.TrustAuth
+      }
+    (\(host, portInt) -> handler (host, fromIntegral portInt))
diff --git a/src/integration-tests/Helpers/Scripts.hs b/src/integration-tests/Helpers/Scripts.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Helpers/Scripts.hs
@@ -0,0 +1,37 @@
+module Helpers.Scripts
+  ( ScopeParams,
+    onConnection,
+    session,
+  )
+where
+
+import Hasql.Connection qualified as Connection
+import Hasql.Connection.Settings qualified as Settings
+import Hasql.Session qualified as Session
+import Pqi qualified
+import Prelude
+
+-- |
+-- Adapter, host and port of a running isolated postgres server.
+type ScopeParams = (Pqi.Adapter, Text, Word16)
+
+-- |
+-- Acquire a connection against the running isolated postgres server,
+-- releasing it once the action completes.
+onConnection :: ScopeParams -> (Connection.Connection -> IO ()) -> IO ()
+onConnection (adapter, host, port) action =
+  bracket acquire Connection.release action
+  where
+    acquire =
+      Connection.acquire adapter connectionSettings
+        >>= either (fail . show) return
+    connectionSettings =
+      Settings.hostAndPort host port
+        <> Settings.user "postgres"
+        <> Settings.password "postgres"
+        <> Settings.dbname "postgres"
+
+session :: Connection.Connection -> Session.Session a -> IO a
+session connection theSession =
+  Connection.use connection theSession
+    >>= either (fail . show) return
diff --git a/src/integration-tests/Helpers/Statements.hs b/src/integration-tests/Helpers/Statements.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Helpers/Statements.hs
@@ -0,0 +1,31 @@
+module Helpers.Statements where
+
+import Hasql.Decoders qualified as D
+import Hasql.Encoders qualified as E
+import Hasql.Statement
+import Prelude
+
+countPGType :: Statement () Int
+countPGType =
+  preparable sql encoder decoder
+  where
+    sql =
+      "select count(*) from pg_type"
+    encoder =
+      E.noParams
+    decoder =
+      (D.singleRow . D.column . D.nonNullable . fmap fromIntegral) D.int8
+
+selectOidAndTypeName :: Statement () [(Int64, Text)]
+selectOidAndTypeName =
+  preparable sql encoder decoder
+  where
+    sql =
+      "select oid::int8, typname::text from pg_type"
+    encoder =
+      E.noParams
+    decoder =
+      D.rowList rowDecoder
+      where
+        rowDecoder =
+          (,) <$> (D.column . D.nonNullable) D.int8 <*> (D.column . D.nonNullable) D.text
diff --git a/src/integration-tests/Main.hs b/src/integration-tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Main.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/src/integration-tests/Specs/CursorQuerySpec.hs b/src/integration-tests/Specs/CursorQuerySpec.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Specs/CursorQuerySpec.hs
@@ -0,0 +1,23 @@
+module Specs.CursorQuerySpec where
+
+import Hasql.CursorQuery.Sessions qualified as CursorQuery
+import Hasql.Session qualified as Session
+import Helpers.CursorQueries qualified as CursorQueries
+import Helpers.Scripts qualified as Scripts
+import Helpers.Statements qualified as Statements
+import Prelude
+import Test.Hspec
+
+spec :: SpecWith Scripts.ScopeParams
+spec = do
+  it "Counts the same amount of rows as a plain aggregate query" \scopeParams ->
+    Scripts.onConnection scopeParams \connection -> do
+      count1 <- Scripts.session connection (Session.statement () Statements.countPGType)
+      count2 <- Scripts.session connection (CursorQuery.cursorQuery () CursorQueries.countPGType)
+      count2 `shouldBe` count1
+
+  it "Selects the same rows as a plain select query" \scopeParams ->
+    Scripts.onConnection scopeParams \connection -> do
+      result1 <- Scripts.session connection (Session.statement () Statements.selectOidAndTypeName)
+      result2 <- Scripts.session connection (CursorQuery.cursorQuery () CursorQueries.selectOidAndTypeName)
+      result2 `shouldBe` result1
diff --git a/src/integration-tests/Specs/SpecHook.hs b/src/integration-tests/Specs/SpecHook.hs
new file mode 100644
--- /dev/null
+++ b/src/integration-tests/Specs/SpecHook.hs
@@ -0,0 +1,15 @@
+-- Docs: https://hspec.github.io/hspec-discover.html
+module Specs.SpecHook where
+
+import Helpers.Adapters qualified as Adapters
+import Helpers.Hooks qualified as Hooks
+import Helpers.Scripts qualified as Scripts
+import Test.Hspec
+
+hook :: SpecWith Scripts.ScopeParams -> Spec
+hook hookedSpec =
+  Adapters.hook
+    ( aroundAllWith
+        (\action adapter -> Hooks.postgres17 \(host, port) -> action (adapter, host, port))
+        hookedSpec
+    )
diff --git a/tasty/Main.hs b/tasty/Main.hs
deleted file mode 100644
--- a/tasty/Main.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module Main where
-
-import qualified Hasql.CursorQuery.Sessions as C
-import qualified Hasql.Session as A
-import qualified Main.CursorQueries as D
-import qualified Main.IO as F
-import qualified Main.Statements as E
-import Rebase.Prelude
-import Test.Tasty
-import Test.Tasty.HUnit
-import Test.Tasty.Runners
-
-main :: IO ()
-main =
-  defaultMain $
-    localOption (NumThreads 1) $
-      testGroup
-        "All tests"
-        [ testCase "Amount" $
-            do
-              (count1, count2) <- F.session ((,) <$> A.statement () E.countPGType <*> C.cursorQuery () D.countPGType)
-              assertEqual (show (count1, count2)) count1 count2,
-          testCase "Equality" $
-            do
-              (result1, result2) <- F.session ((,) <$> A.statement () E.slectOIDAndTypeName <*> C.cursorQuery () D.slectOIDAndTypeName)
-              assertEqual (show (result1, result2)) result1 result2
-        ]
diff --git a/tasty/Main/CursorQueries.hs b/tasty/Main/CursorQueries.hs
deleted file mode 100644
--- a/tasty/Main/CursorQueries.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-module Main.CursorQueries where
-
-import qualified Control.Foldl as C
-import Hasql.CursorQuery
-import qualified Hasql.Decoders as B
-import qualified Hasql.Encoders as A
-import Rebase.Prelude
-
-countPGType :: CursorQuery () Int
-countPGType =
-  cursorQuery sql encoder decoder batchSize_10
-  where
-    sql =
-      "select oid, typname from pg_type"
-    encoder =
-      A.noParams
-    decoder =
-      reducingDecoder rowDecoder fold
-      where
-        rowDecoder =
-          (,) <$> (B.column . B.nonNullable) B.int8 <*> (B.column . B.nonNullable) B.text
-        fold =
-          C.length
-
-slectOIDAndTypeName :: CursorQuery () [(Int64, Text)]
-slectOIDAndTypeName =
-  cursorQuery sql encoder decoder batchSize_10
-  where
-    sql =
-      "select oid, typname from pg_type"
-    encoder =
-      A.noParams
-    decoder =
-      reducingDecoder rowDecoder fold
-      where
-        rowDecoder =
-          (,) <$> (B.column . B.nonNullable) B.int8 <*> (B.column . B.nonNullable) B.text
-        fold =
-          C.list
diff --git a/tasty/Main/IO.hs b/tasty/Main/IO.hs
deleted file mode 100644
--- a/tasty/Main/IO.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-module Main.IO where
-
-import qualified Hasql.Connection as B
-import qualified Hasql.Connection.Setting as Setting
-import qualified Hasql.Connection.Setting.Connection as Connection
-import qualified Hasql.Connection.Setting.Connection.Param as Param
-import qualified Hasql.Session as A
-import Rebase.Prelude
-
-session :: A.Session a -> IO a
-session session =
-  withConnection (A.run session)
-    >>= either (fail . show) (either (fail . show) return)
-  where
-    withConnection :: (B.Connection -> IO a) -> IO (Either B.ConnectionError a)
-    withConnection handler =
-      runExceptT $ acquire >>= \connection -> use connection <* release connection
-      where
-        acquire =
-          ExceptT $ B.acquire settings
-          where
-            settings =
-              [ Setting.connection
-                  ( Connection.params
-                      [ Param.host "localhost",
-                        Param.port 5432,
-                        Param.user "postgres",
-                        Param.password "postgres",
-                        Param.dbname "postgres"
-                      ]
-                  )
-              ]
-        use connection =
-          lift $ handler connection
-        release connection =
-          lift $ B.release connection
diff --git a/tasty/Main/Statements.hs b/tasty/Main/Statements.hs
deleted file mode 100644
--- a/tasty/Main/Statements.hs
+++ /dev/null
@@ -1,31 +0,0 @@
-module Main.Statements where
-
-import qualified Hasql.Decoders as B
-import qualified Hasql.Encoders as A
-import Hasql.Statement
-import Rebase.Prelude
-
-countPGType :: Statement () Int
-countPGType =
-  Statement sql encoder decoder True
-  where
-    sql =
-      "select count(*) from pg_type"
-    encoder =
-      A.noParams
-    decoder =
-      (B.singleRow . B.column . B.nonNullable . fmap fromIntegral) B.int8
-
-slectOIDAndTypeName :: Statement () [(Int64, Text)]
-slectOIDAndTypeName =
-  Statement sql encoder decoder True
-  where
-    sql =
-      "select oid, typname from pg_type"
-    encoder =
-      A.noParams
-    decoder =
-      B.rowList rowDecoder
-      where
-        rowDecoder =
-          (,) <$> (B.column . B.nonNullable) B.int8 <*> (B.column . B.nonNullable) B.text
