diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,10 @@
+# v0.1.0.0
+
+## Breaking
+
+- Migrated to record-of-functions API structure
+
+## Non-breaking
+
+- Added parity specification for pipeline sync operations
+- Added UnescapeBytea to the conformance test battery
diff --git a/pqi-conformance.cabal b/pqi-conformance.cabal
--- a/pqi-conformance.cabal
+++ b/pqi-conformance.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: pqi-conformance
-version: 0.0.1.2
+version: 0.1.0.0
 category: Database, PostgreSQL, Testing
 synopsis: Differential conformance tests for pqi adapters
 description:
@@ -164,6 +164,7 @@
     Pqi.Conformance.Operation.Pass
     Pqi.Conformance.Operation.PipelineStatus
     Pqi.Conformance.Operation.PipelineSync
+    Pqi.Conformance.Operation.PipelineSync.Parity
     Pqi.Conformance.Operation.Port
     Pqi.Conformance.Operation.Prepare
     Pqi.Conformance.Operation.ProtocolVersion
@@ -190,6 +191,7 @@
     Pqi.Conformance.Operation.Socket
     Pqi.Conformance.Operation.Status
     Pqi.Conformance.Operation.TransactionStatus
+    Pqi.Conformance.Operation.UnescapeBytea
     Pqi.Conformance.Operation.UnsafeFreeResult
     Pqi.Conformance.Operation.User
     Pqi.Conformance.Prelude
@@ -203,6 +205,6 @@
     directory >=1.3 && <1.4,
     hspec >=2.11 && <2.12,
     postgresql-libpq >=0.11 && <0.12,
-    pqi ^>=0.0,
+    pqi ^>=0.1,
     testcontainers-postgresql ^>=0.2,
     text >=1.2 && <3,
diff --git a/src/library/Pqi/Conformance.hs b/src/library/Pqi/Conformance.hs
--- a/src/library/Pqi/Conformance.hs
+++ b/src/library/Pqi/Conformance.hs
@@ -1,11 +1,11 @@
 -- | A reusable differential-testing toolkit for @pqi@ adapters.
 --
--- An adapter's test suite calls 'specs' with a @'Proxy' \@MyConnection@. The
+-- An adapter's test suite calls 'specs' with its 'Pqi.Adapter' value. The
 -- battery runs the same operation on the candidate and on the FFI reference
 -- and asserts that the protocol-derived observations match.
 --
 -- The throwaway PostgreSQL container lifecycle is baked into 'specs', so
--- adapter test suites only need @hspec (specs (Proxy \@MyConnection))@; they
+-- adapter test suites only need @hspec (specs MyAdapter.adapter)@; they
 -- don't have to know about @testcontainers@ at all. The SCRAM-SHA-256
 -- authentication spec is part of the @connectdb@ group and boots its own
 -- password-auth container.
@@ -14,7 +14,7 @@
   )
 where
 
-import Pqi (IsConnection)
+import qualified Pqi
 import Pqi.Conformance.Harness
 import qualified Pqi.Conformance.Operation.BackendPID as BackendPID
 import qualified Pqi.Conformance.Operation.Cancel as Cancel
@@ -113,6 +113,7 @@
 import qualified Pqi.Conformance.Operation.Socket as Socket
 import qualified Pqi.Conformance.Operation.Status as Status
 import qualified Pqi.Conformance.Operation.TransactionStatus as TransactionStatus
+import qualified Pqi.Conformance.Operation.UnescapeBytea as UnescapeBytea
 import qualified Pqi.Conformance.Operation.UnsafeFreeResult as UnsafeFreeResult
 import qualified Pqi.Conformance.Operation.User as User
 import Pqi.Conformance.Prelude
@@ -122,117 +123,118 @@
 -- trust-auth container, plus SCRAM-SHA-256 authentication (which boots its own
 -- password-auth container). Every operation spec is differential against the
 -- FFI reference.
-specs :: (IsConnection c) => Proxy c -> Spec
-specs proxy = parallel do
+specs :: Pqi.Adapter -> Spec
+specs adapter = parallel do
   containerHook do
     -- Connection lifecycle
-    Connectdb.spec proxy
-    ConnectStart.spec proxy
-    ConnectPoll.spec proxy
-    NewNullConnection.spec proxy
-    IsNullConnection.spec proxy
-    Finish.spec proxy
-    Reset.spec proxy
-    ResetStart.spec proxy
-    ResetPoll.spec proxy
+    Connectdb.spec adapter
+    ConnectStart.spec adapter
+    ConnectPoll.spec adapter
+    NewNullConnection.spec adapter
+    IsNullConnection.spec adapter
+    Finish.spec adapter
+    Reset.spec adapter
+    ResetStart.spec adapter
+    ResetPoll.spec adapter
     -- Connection information accessors
-    Db.spec proxy
-    User.spec proxy
-    Pass.spec proxy
-    Host.spec proxy
-    Port.spec proxy
-    Options.spec proxy
-    Status.spec proxy
-    TransactionStatus.spec proxy
-    ParameterStatus.spec proxy
-    ProtocolVersion.spec proxy
-    ServerVersion.spec proxy
-    ErrorMessage.spec proxy
-    Socket.spec proxy
-    BackendPID.spec proxy
-    ConnectionNeedsPassword.spec proxy
-    ConnectionUsedPassword.spec proxy
+    Db.spec adapter
+    User.spec adapter
+    Pass.spec adapter
+    Host.spec adapter
+    Port.spec adapter
+    Options.spec adapter
+    Status.spec adapter
+    TransactionStatus.spec adapter
+    ParameterStatus.spec adapter
+    ProtocolVersion.spec adapter
+    ServerVersion.spec adapter
+    ErrorMessage.spec adapter
+    Socket.spec adapter
+    BackendPID.spec adapter
+    ConnectionNeedsPassword.spec adapter
+    ConnectionUsedPassword.spec adapter
     -- Querying
-    Exec.spec proxy
-    ExecParams.spec proxy
-    Prepare.spec proxy
-    ExecPrepared.spec proxy
-    DescribePrepared.spec proxy
-    DescribePortal.spec proxy
+    Exec.spec adapter
+    ExecParams.spec adapter
+    Prepare.spec adapter
+    ExecPrepared.spec adapter
+    DescribePrepared.spec adapter
+    DescribePortal.spec adapter
     -- Escaping
-    EscapeStringConn.spec proxy
-    EscapeByteaConn.spec proxy
-    EscapeIdentifier.spec proxy
+    EscapeStringConn.spec adapter
+    EscapeByteaConn.spec adapter
+    EscapeIdentifier.spec adapter
+    UnescapeBytea.spec adapter
     -- Asynchronous command processing
-    SendQuery.spec proxy
-    SendQueryParams.spec proxy
-    SendPrepare.spec proxy
-    SendQueryPrepared.spec proxy
-    SendDescribePrepared.spec proxy
-    SendDescribePortal.spec proxy
-    GetResult.spec proxy
-    ConsumeInput.spec proxy
-    IsBusy.spec proxy
-    Setnonblocking.spec proxy
-    Isnonblocking.spec proxy
-    SetSingleRowMode.spec proxy
-    Flush.spec proxy
+    SendQuery.spec adapter
+    SendQueryParams.spec adapter
+    SendPrepare.spec adapter
+    SendQueryPrepared.spec adapter
+    SendDescribePrepared.spec adapter
+    SendDescribePortal.spec adapter
+    GetResult.spec adapter
+    ConsumeInput.spec adapter
+    IsBusy.spec adapter
+    Setnonblocking.spec adapter
+    Isnonblocking.spec adapter
+    SetSingleRowMode.spec adapter
+    Flush.spec adapter
     -- Pipelining
-    PipelineStatus.spec proxy
-    EnterPipelineMode.spec proxy
-    ExitPipelineMode.spec proxy
-    PipelineSync.spec proxy
-    SendFlushRequest.spec proxy
+    PipelineStatus.spec adapter
+    EnterPipelineMode.spec adapter
+    ExitPipelineMode.spec adapter
+    PipelineSync.spec adapter
+    SendFlushRequest.spec adapter
     -- Cancellation
-    GetCancel.spec proxy
-    Cancel.spec proxy
+    GetCancel.spec adapter
+    Cancel.spec adapter
     -- Notifications and notices
-    Notifies.spec proxy
-    DisableNoticeReporting.spec proxy
-    EnableNoticeReporting.spec proxy
-    GetNotice.spec proxy
+    Notifies.spec adapter
+    DisableNoticeReporting.spec adapter
+    EnableNoticeReporting.spec adapter
+    GetNotice.spec adapter
     -- Copy sub-protocol
-    PutCopyData.spec proxy
-    PutCopyEnd.spec proxy
-    GetCopyData.spec proxy
+    PutCopyData.spec adapter
+    PutCopyEnd.spec adapter
+    GetCopyData.spec adapter
     -- Large objects
-    LoCreat.spec proxy
-    LoCreate.spec proxy
-    LoImport.spec proxy
-    LoImportWithOid.spec proxy
-    LoExport.spec proxy
-    LoOpen.spec proxy
-    LoWrite.spec proxy
-    LoRead.spec proxy
-    LoSeek.spec proxy
-    LoTell.spec proxy
-    LoTruncate.spec proxy
-    LoClose.spec proxy
-    LoUnlink.spec proxy
+    LoCreat.spec adapter
+    LoCreate.spec adapter
+    LoImport.spec adapter
+    LoImportWithOid.spec adapter
+    LoExport.spec adapter
+    LoOpen.spec adapter
+    LoWrite.spec adapter
+    LoRead.spec adapter
+    LoSeek.spec adapter
+    LoTell.spec adapter
+    LoTruncate.spec adapter
+    LoClose.spec adapter
+    LoUnlink.spec adapter
     -- Connection control
-    ClientEncoding.spec proxy
-    SetClientEncoding.spec proxy
-    SetErrorVerbosity.spec proxy
+    ClientEncoding.spec adapter
+    SetClientEncoding.spec adapter
+    SetErrorVerbosity.spec adapter
     -- Result inspection
-    ResultStatus.spec proxy
-    ResultErrorMessage.spec proxy
-    ResultErrorField.spec proxy
-    UnsafeFreeResult.spec proxy
-    Ntuples.spec proxy
-    Nfields.spec proxy
-    Fname.spec proxy
-    Fnumber.spec proxy
-    Ftable.spec proxy
-    Ftablecol.spec proxy
-    Fformat.spec proxy
-    Ftype.spec proxy
-    Fmod.spec proxy
-    Fsize.spec proxy
-    Getvalue.spec proxy
-    GetvalueCopy.spec proxy
-    Getisnull.spec proxy
-    Getlength.spec proxy
-    Nparams.spec proxy
-    Paramtype.spec proxy
-    CmdStatus.spec proxy
-    CmdTuples.spec proxy
+    ResultStatus.spec adapter
+    ResultErrorMessage.spec adapter
+    ResultErrorField.spec adapter
+    UnsafeFreeResult.spec adapter
+    Ntuples.spec adapter
+    Nfields.spec adapter
+    Fname.spec adapter
+    Fnumber.spec adapter
+    Ftable.spec adapter
+    Ftablecol.spec adapter
+    Fformat.spec adapter
+    Ftype.spec adapter
+    Fmod.spec adapter
+    Fsize.spec adapter
+    Getvalue.spec adapter
+    GetvalueCopy.spec adapter
+    Getisnull.spec adapter
+    Getlength.spec adapter
+    Nparams.spec adapter
+    Paramtype.spec adapter
+    CmdStatus.spec adapter
+    CmdTuples.spec adapter
diff --git a/src/library/Pqi/Conformance/Harness.hs b/src/library/Pqi/Conformance/Harness.hs
--- a/src/library/Pqi/Conformance/Harness.hs
+++ b/src/library/Pqi/Conformance/Harness.hs
@@ -14,9 +14,9 @@
 import qualified Data.ByteString.Char8 as ByteString.Char8
 import qualified Data.Text as Text
 import Data.Unique (hashUnique, newUnique)
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Prelude
-import Pqi.Conformance.Reference (Reference)
+import qualified Pqi.Conformance.Reference as Reference
 import Test.Hspec
 import qualified TestcontainersPostgresql as TcPg
 
@@ -48,16 +48,15 @@
 -- Each call creates a fresh database for the scenario and drops it afterwards,
 -- so tests are isolated even when the container is shared.
 differential ::
-  forall c a.
-  (Eq a, Show a, IsConnection c, HasCallStack) =>
-  Proxy c ->
+  (Eq a, Show a, HasCallStack) =>
+  Pqi.Adapter ->
   ByteString ->
-  (forall c'. (IsConnection c') => c' -> IO a) ->
+  (Pqi.Connection -> IO a) ->
   Expectation
-differential _ adminConninfo scenario =
+differential adapter adminConninfo scenario =
   withTestDb adminConninfo \testConninfo -> do
-    candidate <- bracket (connectdb testConninfo :: IO c) finish scenario
-    reference <- bracket (connectdb testConninfo :: IO Reference) finish scenario
+    candidate <- bracket (adapter.connectdb testConninfo) (.finish) scenario
+    reference <- bracket (Reference.adapter.connectdb testConninfo) (.finish) scenario
     candidate `shouldBe` reference
 
 -- Create a uniquely named database, run the action against it, and drop it on
@@ -77,23 +76,22 @@
 
 adminExec :: ByteString -> ByteString -> IO ()
 adminExec conninfo sql = do
-  conn <- connectdb conninfo :: IO Reference
-  _ <- exec conn sql
-  finish conn
+  conn <- Reference.adapter.connectdb conninfo
+  _ <- conn.exec sql
+  conn.finish
 
 -- | Like 'differential', but for scenarios that exercise connection
 -- establishment itself ('Pqi.connectdb' on a broken conninfo,
--- 'Pqi.connectStart', 'Pqi.newNullConnection', ...): instead of an
--- opened connection the scenario receives the conninfo and the candidate's
--- connection type (via 'Proxy'), and manages any connections it opens itself.
+-- 'Pqi.connectStart', 'Pqi.newNullConnection', ...): instead of an opened
+-- connection the scenario receives the conninfo and the adapter to use, and
+-- manages any connections it opens itself.
 differentialConnect ::
-  forall c a.
-  (Eq a, Show a, IsConnection c, HasCallStack) =>
-  Proxy c ->
+  (Eq a, Show a, HasCallStack) =>
+  Pqi.Adapter ->
   ByteString ->
-  (forall c'. (IsConnection c', HasCallStack) => Proxy c' -> ByteString -> IO a) ->
+  (Pqi.Adapter -> ByteString -> IO a) ->
   Expectation
-differentialConnect proxy conninfo scenario = do
-  candidate <- scenario proxy conninfo
-  reference <- scenario (Proxy :: Proxy Reference) conninfo
+differentialConnect adapter conninfo scenario = do
+  candidate <- scenario adapter conninfo
+  reference <- scenario Reference.adapter conninfo
   candidate `shouldBe` reference
diff --git a/src/library/Pqi/Conformance/Observation.hs b/src/library/Pqi/Conformance/Observation.hs
--- a/src/library/Pqi/Conformance/Observation.hs
+++ b/src/library/Pqi/Conformance/Observation.hs
@@ -1,16 +1,16 @@
 -- | Plain, comparable snapshots of a connection or result.
 --
--- The candidate adapter and the FFI reference produce values of /different/
--- types (@'Pqi.ResultOf' candidate@ vs @'Pqi.ResultOf'
--- 'Pqi.Ffi.Connection'@), so they cannot be compared directly. Instead we
--- project each into one of these driver-independent records and compare those.
+-- Every adapter produces the same concrete 'Pqi.Connection'\/'Pqi.Result'
+-- record, so comparing candidate and reference values directly would also
+-- compare the per-connection identity fields (@backendPID@, @socket@), which
+-- are structurally incomparable across two independently-opened connections.
+-- Projecting each into one of these driver-independent records first keeps
+-- the comparison to protocol-derived information only.
 --
 -- Only protocol-derived information is captured: both adapters parse the same
--- wire bytes, so these fields genuinely agree. Per-connection identity values
--- (@backendPID@, @socket@) are structurally incomparable across connections
--- and are omitted. All result fields — including the flat error message text
--- and all structured error fields — are captured in full and compared
--- byte-identically.
+-- wire bytes, so these fields genuinely agree. All result fields — including
+-- the flat error message text and all structured error fields — are captured
+-- in full and compared byte-identically.
 module Pqi.Conformance.Observation
   ( ResultObservation (..),
     FieldObservation (..),
@@ -21,7 +21,6 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
 import qualified Pqi as Lq
 import Pqi.Conformance.Prelude
 
@@ -67,40 +66,40 @@
   deriving stock (Eq, Show)
 
 -- | Project a result into a 'ResultObservation'.
-observeResult :: (IsResult r) => r -> IO ResultObservation
+observeResult :: Lq.Result -> IO ResultObservation
 observeResult result = do
-  status <- Lq.resultStatus result
+  status <- result.resultStatus
   errorFields <-
     traverse
-      (\code -> (,) code <$> Lq.resultErrorField result code)
+      (\code -> (,) code <$> result.resultErrorField code)
       [minBound .. maxBound]
-  errorMessage <- Lq.resultErrorMessage result
-  ntuples <- Lq.ntuples result
-  nfields <- Lq.nfields result
-  nparams <- Lq.nparams result
-  paramTypes <- traverse (Lq.paramtype result) [0 .. nparams - 1]
+  errorMessage <- result.resultErrorMessage
+  ntuples <- result.ntuples
+  nfields <- result.nfields
+  nparams <- result.nparams
+  paramTypes <- traverse result.paramtype [0 .. nparams - 1]
   fields <- traverse (observeField result) [0 .. nfields - 1]
   rows <- traverse (\row -> traverse (observeCell result row) [0 .. nfields - 1]) [0 .. ntuples - 1]
-  cmdStatus <- Lq.cmdStatus result
-  cmdTuples <- Lq.cmdTuples result
+  cmdStatus <- result.cmdStatus
+  cmdTuples <- result.cmdTuples
   pure ResultObservation {..}
 
-observeField :: (IsResult r) => r -> Int32 -> IO FieldObservation
+observeField :: Lq.Result -> Int32 -> IO FieldObservation
 observeField result column = do
-  name <- Lq.fname result column
-  typeOid <- Lq.ftype result column
-  modifier <- Lq.fmod result column
-  size <- Lq.fsize result column
-  format <- Lq.fformat result column
-  tableOid <- Lq.ftable result column
-  tableColumn <- Lq.ftablecol result column
+  name <- result.fname column
+  typeOid <- result.ftype column
+  modifier <- result.fmod column
+  size <- result.fsize column
+  format <- result.fformat column
+  tableOid <- result.ftable column
+  tableColumn <- result.ftablecol column
   pure FieldObservation {..}
 
-observeCell :: (IsResult r) => r -> Int32 -> Int32 -> IO CellObservation
+observeCell :: Lq.Result -> Int32 -> Int32 -> IO CellObservation
 observeCell result row column = do
-  value <- Lq.getvalue result row column
-  isNull <- Lq.getisnull result row column
-  length <- Lq.getlength result row column
+  value <- result.getvalue row column
+  isNull <- result.getisnull row column
+  length <- result.getlength row column
   pure CellObservation {..}
 
 -- | A snapshot of the comparable portion of a connection's state. The
@@ -125,20 +124,20 @@
   deriving stock (Eq, Show)
 
 -- | Project a connection into a 'ConnectionObservation'.
-observeConnection :: (IsConnection c) => c -> IO ConnectionObservation
+observeConnection :: Lq.Connection -> IO ConnectionObservation
 observeConnection connection = do
-  status <- Lq.status connection
-  transactionStatus <- Lq.transactionStatus connection
-  serverVersion <- Lq.serverVersion connection
-  serverVersionParam <- Lq.parameterStatus connection "server_version"
-  protocolVersion <- Lq.protocolVersion connection
-  db <- Lq.db connection
-  user <- Lq.user connection
-  pass <- Lq.pass connection
-  host <- Lq.host connection
-  port <- Lq.port connection
-  options <- Lq.options connection
-  connectionNeedsPassword <- Lq.connectionNeedsPassword connection
-  connectionUsedPassword <- Lq.connectionUsedPassword connection
-  let isNull = Lq.isNullConnection connection
+  status <- connection.status
+  transactionStatus <- connection.transactionStatus
+  serverVersion <- connection.serverVersion
+  serverVersionParam <- connection.parameterStatus "server_version"
+  protocolVersion <- connection.protocolVersion
+  db <- connection.db
+  user <- connection.user
+  pass <- connection.pass
+  host <- connection.host
+  port <- connection.port
+  options <- connection.options
+  connectionNeedsPassword <- connection.connectionNeedsPassword
+  connectionUsedPassword <- connection.connectionUsedPassword
+  let isNull = connection.isNullConnection
   pure ConnectionObservation {..}
diff --git a/src/library/Pqi/Conformance/Operation/BackendPID.hs b/src/library/Pqi/Conformance/Operation/BackendPID.hs
--- a/src/library/Pqi/Conformance/Operation/BackendPID.hs
+++ b/src/library/Pqi/Conformance/Operation/BackendPID.hs
@@ -7,14 +7,14 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "backendPID" do
     it "is positive on an open connection" \conninfo ->
-      differential proxy conninfo \connection ->
-        (> 0) <$> backendPID connection
+      differential adapter conninfo \connection ->
+        (> 0) <$> connection.backendPID
diff --git a/src/library/Pqi/Conformance/Operation/Cancel.hs b/src/library/Pqi/Conformance/Operation/Cancel.hs
--- a/src/library/Pqi/Conformance/Operation/Cancel.hs
+++ b/src/library/Pqi/Conformance/Operation/Cancel.hs
@@ -9,7 +9,7 @@
   )
 where
 
-import Pqi (IsCancel (..), IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import qualified Pqi.Conformance.Operation.Cancel.Cleanup as Cleanup
 import qualified Pqi.Conformance.Operation.Cancel.Stale as Stale
@@ -17,35 +17,35 @@
 import Pqi.Conformance.Scenario (drainResults, execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "cancel" do
     it "succeeds on an idle connection" \conninfo ->
-      differential proxy conninfo \connection -> do
-        handle <- getCancel connection
-        for handle cancel
+      differential adapter conninfo \connection -> do
+        handle <- connection.getCancel
+        for handle (.cancel)
 
     it "fails a running query with 57014" \conninfo ->
-      differential proxy conninfo \connection -> do
-        sent <- sendQuery connection "select pg_sleep(10)"
+      differential adapter conninfo \connection -> do
+        sent <- connection.sendQuery "select pg_sleep(10)"
         threadDelay 100000
-        handle <- getCancel connection
-        cancelled <- for handle cancel
+        handle <- connection.getCancel
+        cancelled <- for handle (.cancel)
         results <- drainResults connection
         usable <- execScenario "select 1" connection
         pure (sent, cancelled, results, usable)
 
     it "leaves the connection usable after cancelling a short-running query" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         outcomes <- replicateM 3 do
-          sent <- sendQuery connection "select pg_sleep(0.1)"
+          sent <- connection.sendQuery "select pg_sleep(0.1)"
           threadDelay 50000
-          handle <- getCancel connection
-          cancelled <- for handle cancel
+          handle <- connection.getCancel
+          cancelled <- for handle (.cancel)
           results <- drainResults connection
           usable <- execScenario "select 1" connection
           pure (sent, cancelled, results, usable)
         pure outcomes
 
-    Cleanup.spec proxy
-    Stale.spec proxy
+    Cleanup.spec adapter
+    Stale.spec adapter
diff --git a/src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs b/src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs
--- a/src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs
+++ b/src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs
@@ -15,13 +15,13 @@
 where
 
 import Control.Exception (bracket)
-import Pqi (ExecStatus (..), IsCancel (..), IsConnection (..), IsResult (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: forall c. (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "cancel cleanup" do
     -- Reproduces the state that hasql's cleanUpAfterInterruption reaches after
     -- a timeout fires mid-pipeline:
@@ -38,11 +38,11 @@
     --   5. exec runs a follow-up command — it must NOT be cancelled by the
     --      stale signal that arrived in step 2.
     it "does not corrupt subsequent commands when cancel is called after pipeline results are drained" \conninfo ->
-      bracket (connectdb conninfo :: IO c) finish \connection -> do
+      bracket (adapter.connectdb conninfo) (.finish) \connection -> do
         -- Enter pipeline mode and dispatch a fast query.
-        _ <- enterPipelineMode connection
-        _ <- sendQueryParams connection "select 1" [] Lq.Text
-        _ <- pipelineSync connection
+        _ <- connection.enterPipelineMode
+        _ <- connection.sendQueryParams "select 1" [] Lq.Text
+        _ <- connection.pipelineSync
 
         -- Wait for the server to process the query so both messages
         -- (CommandComplete + ReadyForQuery) are already in the socket by the
@@ -53,7 +53,7 @@
         -- After this loop exits, asyncPending=True in pqi-native because
         -- ReadyForQuery has not been read yet.
         let drainAll = do
-              mr <- getResult connection
+              mr <- connection.getResult
               case mr of
                 Nothing -> pure ()
                 Just _ -> drainAll
@@ -61,8 +61,8 @@
 
         -- Send cancel.  Because asyncPending=True in pqi-native, a cancel
         -- request is dispatched to the server despite the query being done.
-        handle <- getCancel connection
-        for_ handle cancel
+        handle <- connection.getCancel
+        for_ handle (.cancel)
 
         -- Give the stale cancel enough time to reach the server and set
         -- QueryCancelPending before the next command arrives.
@@ -72,13 +72,13 @@
         drainAll
 
         -- Exit pipeline mode (sends an implicit Sync in the reference impl).
-        _ <- exitPipelineMode connection
+        _ <- connection.exitPipelineMode
 
         -- A follow-up command must succeed; 57014 here means the stale cancel
         -- corrupted the connection.
-        mResult <- exec connection "select 1"
+        mResult <- connection.exec "select 1"
         case mResult of
           Nothing -> expectationFailure "exec returned no result after pipeline cleanup"
-          Just (result :: ResultOf c) -> do
-            status <- resultStatus result
-            status `shouldBe` TuplesOk
+          Just result -> do
+            status <- result.resultStatus
+            status `shouldBe` Lq.TuplesOk
diff --git a/src/library/Pqi/Conformance/Operation/Cancel/Stale.hs b/src/library/Pqi/Conformance/Operation/Cancel/Stale.hs
--- a/src/library/Pqi/Conformance/Operation/Cancel/Stale.hs
+++ b/src/library/Pqi/Conformance/Operation/Cancel/Stale.hs
@@ -32,7 +32,7 @@
 where
 
 import Control.Exception (bracket)
-import Pqi (ExecStatus (..), FieldCode (..), IsCancel (..), IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Observation (ResultObservation (..))
 import Pqi.Conformance.Prelude
@@ -47,12 +47,12 @@
 iterations :: Int
 iterations = 30
 
-spec :: forall c. (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "stale cancel" do
     it "does not corrupt the next command when a cancel is sent during pipeline clean-up" \conninfo -> do
       outcomes <- for [1 .. iterations] \i -> do
-        outcome <- runScenario proxy conninfo
+        outcome <- runScenario adapter conninfo
         pure (i, outcome)
       -- Every iteration's victim query must have succeeded.  Any iteration that
       -- did not means a stale cancel corrupted the connection (57014).
@@ -63,22 +63,20 @@
 -- the victim query succeeded, or @Just (status, sqlstate)@ describing how it
 -- failed (e.g. @(FatalError, Just "57014")@).
 runScenario ::
-  forall c.
-  (IsConnection c) =>
-  Proxy c ->
+  Pqi.Adapter ->
   ByteString ->
-  IO (Maybe (ExecStatus, Maybe ByteString))
-runScenario _ conninfo =
-  bracket (connectdb conninfo :: IO c) finish \connection -> do
+  IO (Maybe (Lq.ExecStatus, Maybe ByteString))
+runScenario adapter conninfo =
+  bracket (adapter.connectdb conninfo) (.finish) \connection -> do
     -- Build a pipeline with a fast query followed by a slow one, mirroring the
     -- sequence hasql issues.  Two prepared statements keep pendingParses in
     -- play, matching the real-world reproduction.
-    _ <- enterPipelineMode connection
-    _ <- sendPrepare connection "s1" "select $1::int" Nothing
-    _ <- sendQueryPrepared connection "s1" [Just ("42", Lq.Text)] Lq.Text
-    _ <- sendPrepare connection "s2" "select pg_sleep($1)" (Just [float8Oid])
-    _ <- sendQueryPrepared connection "s2" [Just ("0.1", Lq.Text)] Lq.Text
-    _ <- pipelineSync connection
+    _ <- connection.enterPipelineMode
+    _ <- connection.sendPrepare "s1" "select $1::int" Nothing
+    _ <- connection.sendQueryPrepared "s1" [Just ("42", Lq.Text)] Lq.Text
+    _ <- connection.sendPrepare "s2" "select pg_sleep($1)" (Just [float8Oid])
+    _ <- connection.sendQueryPrepared "s2" [Just ("0.1", Lq.Text)] Lq.Text
+    _ <- connection.pipelineSync
 
     -- Interrupt the read mid-pipeline, exactly as hasql's timeout does: the
     -- slow pg_sleep is still running when the read is abandoned.
@@ -87,27 +85,27 @@
     -- cleanUpAfterInterruption: drain, cancel, drain.  The cancel is sent while
     -- the pipeline is mid-flight; on buggy code its SIGINT can arrive late.
     _ <- drainResults connection
-    mHandle <- getCancel connection
-    _ <- for mHandle cancel
+    mHandle <- connection.getCancel
+    _ <- for mHandle (.cancel)
     _ <- drainResults connection
 
     -- leavePipeline: the exact restore sequence hasql performs, including the
     -- retry it falls back to.
-    statusBefore <- pipelineStatus connection
+    statusBefore <- connection.pipelineStatus
     when (statusBefore == Lq.PipelineOn) do
-      _ <- pipelineSync connection
+      _ <- connection.pipelineSync
       _ <- drainResults connection
-      _ <- sendFlushRequest connection
+      _ <- connection.sendFlushRequest
       _ <- drainResults connection
-      ok <- exitPipelineMode connection
+      ok <- connection.exitPipelineMode
       unless ok do
         _ <- drainResults connection
-        void (exitPipelineMode connection)
+        void connection.exitPipelineMode
 
     -- The victim.  It must not be cancelled by the stale signal.
     execScenario "select 99" connection >>= \case
-      Nothing -> pure (Just (FatalError, Just "no-result"))
+      Nothing -> pure (Just (Lq.FatalError, Just "no-result"))
       Just observation ->
         case observation.status of
-          TuplesOk -> pure Nothing
-          status -> pure (Just (status, fromMaybe Nothing (lookup DiagSqlstate observation.errorFields)))
+          Lq.TuplesOk -> pure Nothing
+          status -> pure (Just (status, fromMaybe Nothing (lookup Lq.DiagSqlstate observation.errorFields)))
diff --git a/src/library/Pqi/Conformance/Operation/ClientEncoding.hs b/src/library/Pqi/Conformance/Operation/ClientEncoding.hs
--- a/src/library/Pqi/Conformance/Operation/ClientEncoding.hs
+++ b/src/library/Pqi/Conformance/Operation/ClientEncoding.hs
@@ -6,22 +6,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "clientEncoding" do
     it "round-trips and governs result re-encoding" \conninfo ->
-      differential proxy conninfo \connection -> do
-        initial <- clientEncoding connection
-        setOk <- setClientEncoding connection "LATIN1"
-        switched <- clientEncoding connection
-        reported <- parameterStatus connection "client_encoding"
+      differential adapter conninfo \connection -> do
+        initial <- connection.clientEncoding
+        setOk <- connection.setClientEncoding "LATIN1"
+        switched <- connection.clientEncoding
+        reported <- connection.parameterStatus "client_encoding"
         latinCell <- execScenario "select chr(233) as e" connection
-        restoreOk <- setClientEncoding connection "UTF8"
+        restoreOk <- connection.setClientEncoding "UTF8"
         utfCell <- execScenario "select chr(233) as e" connection
         pure (initial, setOk, switched, reported, latinCell, restoreOk, utfCell)
diff --git a/src/library/Pqi/Conformance/Operation/CmdStatus.hs b/src/library/Pqi/Conformance/Operation/CmdStatus.hs
--- a/src/library/Pqi/Conformance/Operation/CmdStatus.hs
+++ b/src/library/Pqi/Conformance/Operation/CmdStatus.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "cmdStatus" do
     it "reports the command tag for each command" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_cmd_status (id int4, label text)"
-        let tagOf sql = exec connection sql >>= traverse cmdStatus
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_cmd_status (id int4, label text)"
+        let tagOf sql = connection.exec sql >>= traverse (.cmdStatus)
         insert <- tagOf "insert into conformance_cmd_status values (1, 'a'), (2, 'b')"
         update <- tagOf "update conformance_cmd_status set label = 'c' where id = 1"
         delete <- tagOf "delete from conformance_cmd_status where id = 2"
diff --git a/src/library/Pqi/Conformance/Operation/CmdTuples.hs b/src/library/Pqi/Conformance/Operation/CmdTuples.hs
--- a/src/library/Pqi/Conformance/Operation/CmdTuples.hs
+++ b/src/library/Pqi/Conformance/Operation/CmdTuples.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "cmdTuples" do
     it "reports the affected-row count for each command" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_cmd_tuples (id int4, label text)"
-        let countOf sql = exec connection sql >>= traverse cmdTuples
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_cmd_tuples (id int4, label text)"
+        let countOf sql = connection.exec sql >>= traverse (.cmdTuples)
         insert <- countOf "insert into conformance_cmd_tuples values (1, 'a'), (2, 'b')"
         update <- countOf "update conformance_cmd_tuples set label = 'c' where id = 1"
         delete <- countOf "delete from conformance_cmd_tuples where id = 2"
diff --git a/src/library/Pqi/Conformance/Operation/ConnectPoll.hs b/src/library/Pqi/Conformance/Operation/ConnectPoll.hs
--- a/src/library/Pqi/Conformance/Operation/ConnectPoll.hs
+++ b/src/library/Pqi/Conformance/Operation/ConnectPoll.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (pollUntilDone)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "connectPoll" do
     it "reaches a terminal polling status and a ready connection" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) conninfo' -> do
-        connection <- connectStart conninfo' :: IO c
-        terminal <- pollUntilDone (connectPoll connection)
-        connStatus <- status connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectStart conninfo'
+        terminal <- pollUntilDone connection.connectPoll
+        connStatus <- connection.status
+        connection.finish
         pure (terminal, connStatus)
diff --git a/src/library/Pqi/Conformance/Operation/ConnectStart.hs b/src/library/Pqi/Conformance/Operation/ConnectStart.hs
--- a/src/library/Pqi/Conformance/Operation/ConnectStart.hs
+++ b/src/library/Pqi/Conformance/Operation/ConnectStart.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (pollUntilDone)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "connectStart" do
     it "begins an asynchronous connection that polls to readiness" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) conninfo' -> do
-        connection <- connectStart conninfo' :: IO c
-        polled <- pollUntilDone (connectPoll connection)
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectStart conninfo'
+        polled <- pollUntilDone connection.connectPoll
         observation <- observeConnection connection
-        finish connection
+        connection.finish
         pure (polled, observation)
diff --git a/src/library/Pqi/Conformance/Operation/Connectdb.hs b/src/library/Pqi/Conformance/Operation/Connectdb.hs
--- a/src/library/Pqi/Conformance/Operation/Connectdb.hs
+++ b/src/library/Pqi/Conformance/Operation/Connectdb.hs
@@ -11,49 +11,49 @@
 import qualified Data.ByteString.Char8 as ByteString.Char8
 import qualified Data.Map.Strict as Map
 import qualified Data.Text as Text
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
-import Pqi.Conformance.Reference (Reference)
+import qualified Pqi.Conformance.Reference as Reference
 import Test.Hspec
 import qualified TestcontainersPostgresql as TcPg
 
-spec :: forall c. (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy = do
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter = do
   describe "connectdb" do
     it "opens a usable connection" \conninfo ->
-      differential proxy conninfo observeConnection
+      differential adapter conninfo observeConnection
 
     it "accepts a URI-format conninfo" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy d) conninfo' -> do
-        connection <- connectdb (kvToUri conninfo') :: IO d
-        s <- status connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectdb (kvToUri conninfo')
+        s <- connection.status
+        connection.finish
         pure s
 
     it "rejects an unknown database" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy d) conninfo' -> do
-        connection <- connectdb (conninfo' <> " dbname=pqi_no_such_db") :: IO d
-        observation <- status connection
-        nullness <- pure (isNullConnection connection)
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectdb (conninfo' <> " dbname=pqi_no_such_db")
+        observation <- connection.status
+        nullness <- pure connection.isNullConnection
+        connection.finish
         pure (observation, nullness)
 
     it "rejects an unknown user" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy d) conninfo' -> do
-        connection <- connectdb (conninfo' <> " user=pqi_no_such_user") :: IO d
-        observation <- status connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectdb (conninfo' <> " user=pqi_no_such_user")
+        observation <- connection.status
+        connection.finish
         pure observation
 
     it "defaults the user like the reference when user is omitted" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy d) conninfo' -> do
-        connection <- connectdb (dropUser conninfo') :: IO d
-        resolvedUser <- user connection
-        observedStatus <- status connection
-        observedError <- errorMessage connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectdb (dropUser conninfo')
+        resolvedUser <- connection.user
+        observedStatus <- connection.status
+        observedError <- connection.errorMessage
+        connection.finish
         pure (resolvedUser, observedStatus, observedError)
 
   describe "SCRAM-SHA-256 authentication" do
@@ -65,8 +65,8 @@
                 TcPg.auth = TcPg.CredentialsAuth "scram" "secret"
               }
 
-          scramScenario :: forall c. (IsConnection c) => c -> IO (Maybe ResultObservation)
-          scramScenario connection = exec connection "select 1 as scram_works" >>= traverse observeResult
+          scramScenario :: Pqi.Connection -> IO (Maybe ResultObservation)
+          scramScenario connection = connection.exec "select 1 as scram_works" >>= traverse observeResult
        in TcPg.run scramConfig \(host, port) -> do
             let conninfo =
                   ByteString.Char8.pack
@@ -76,9 +76,9 @@
                         <> show port
                         <> " user=scram password=secret dbname=scram"
                     )
-            native <- bracket (connectdb conninfo) finish (scramScenario @c)
-            reference <- bracket (connectdb conninfo) finish (scramScenario @Reference)
-            native `shouldBe` reference
+            candidate <- bracket (adapter.connectdb conninfo) (.finish) scramScenario
+            reference <- bracket (Reference.adapter.connectdb conninfo) (.finish) scramScenario
+            candidate `shouldBe` reference
 
 -- | Drop the @user=…@ token from a @key=value@ conninfo, leaving the user
 -- unspecified so the adapter must apply its own default. libpq derives the
diff --git a/src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs b/src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs
--- a/src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs
+++ b/src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "connectionNeedsPassword" do
     it "reports whether a password was needed" \conninfo ->
-      differential proxy conninfo connectionNeedsPassword
+      differential adapter conninfo (.connectionNeedsPassword)
diff --git a/src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs b/src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs
--- a/src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs
+++ b/src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "connectionUsedPassword" do
     it "reports whether a password was used" \conninfo ->
-      differential proxy conninfo connectionUsedPassword
+      differential adapter conninfo (.connectionUsedPassword)
diff --git a/src/library/Pqi/Conformance/Operation/ConsumeInput.hs b/src/library/Pqi/Conformance/Operation/ConsumeInput.hs
--- a/src/library/Pqi/Conformance/Operation/ConsumeInput.hs
+++ b/src/library/Pqi/Conformance/Operation/ConsumeInput.hs
@@ -6,22 +6,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "consumeInput" do
     it "drives result collection together with isBusy" \conninfo ->
-      differential proxy conninfo \connection -> do
-        sent <- sendQuery connection "select 42"
+      differential adapter conninfo \connection -> do
+        sent <- connection.sendQuery "select 42"
         let settle (0 :: Int) = pure False
             settle n = do
-              consumed <- consumeInput connection
-              busy <- isBusy connection
+              consumed <- connection.consumeInput
+              busy <- connection.isBusy
               if busy then threadDelay 1000 >> settle (n - 1) else pure consumed
         consumed <- settle 10000
         results <- drainResults connection
diff --git a/src/library/Pqi/Conformance/Operation/Db.hs b/src/library/Pqi/Conformance/Operation/Db.hs
--- a/src/library/Pqi/Conformance/Operation/Db.hs
+++ b/src/library/Pqi/Conformance/Operation/Db.hs
@@ -4,13 +4,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "db" do
     it "reports the database name from the conninfo" \conninfo ->
-      differential proxy conninfo db
+      differential adapter conninfo (.db)
diff --git a/src/library/Pqi/Conformance/Operation/DescribePortal.hs b/src/library/Pqi/Conformance/Operation/DescribePortal.hs
--- a/src/library/Pqi/Conformance/Operation/DescribePortal.hs
+++ b/src/library/Pqi/Conformance/Operation/DescribePortal.hs
@@ -5,24 +5,23 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "describePortal" do
     it "describes a declared cursor" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "begin"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "begin"
         _ <-
-          exec
-            connection
+          connection.exec
             "declare conformance_cursor cursor for select 1 :: int4 as n, 'x' :: text as t"
-        describePortal connection "conformance_cursor" >>= traverse observeResult
+        connection.describePortal "conformance_cursor" >>= traverse observeResult
 
     it "rejects an unknown portal" \conninfo ->
-      differential proxy conninfo \connection ->
-        describePortal connection "conformance_no_portal" >>= traverse observeResult
+      differential adapter conninfo \connection ->
+        connection.describePortal "conformance_no_portal" >>= traverse observeResult
diff --git a/src/library/Pqi/Conformance/Operation/DescribePrepared.hs b/src/library/Pqi/Conformance/Operation/DescribePrepared.hs
--- a/src/library/Pqi/Conformance/Operation/DescribePrepared.hs
+++ b/src/library/Pqi/Conformance/Operation/DescribePrepared.hs
@@ -6,26 +6,26 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (int8Oid)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "describePrepared" do
     it "reports parameter types" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_desc" "select $1 :: int4, $2 :: text" Nothing
-        describePrepared connection "conformance_desc" >>= traverse observeResult
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_desc" "select $1 :: int4, $2 :: text" Nothing
+        connection.describePrepared "conformance_desc" >>= traverse observeResult
 
     it "reports explicit parameter types" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_typed" "select $1" (Just [int8Oid])
-        describePrepared connection "conformance_typed" >>= traverse observeResult
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_typed" "select $1" (Just [int8Oid])
+        connection.describePrepared "conformance_typed" >>= traverse observeResult
 
     it "rejects an unknown statement" \conninfo ->
-      differential proxy conninfo \connection ->
-        describePrepared connection "conformance_missing" >>= traverse observeResult
+      differential adapter conninfo \connection ->
+        connection.describePrepared "conformance_missing" >>= traverse observeResult
diff --git a/src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs b/src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs
--- a/src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs
+++ b/src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs
@@ -5,23 +5,23 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "disableNoticeReporting" do
     it "stops retaining notices once disabled" \conninfo ->
-      differential proxy conninfo \connection -> do
-        enableNoticeReporting connection
+      differential adapter conninfo \connection -> do
+        connection.enableNoticeReporting
         whileEnabled <- raiseNoticeAndCollect connection
-        disableNoticeReporting connection
+        connection.disableNoticeReporting
         whileDisabled <- raiseNoticeAndCollect connection
         pure (whileEnabled, whileDisabled)
 
-raiseNoticeAndCollect :: (IsConnection c) => c -> IO Bool
+raiseNoticeAndCollect :: Pqi.Connection -> IO Bool
 raiseNoticeAndCollect connection = do
-  _ <- exec connection "do $$ begin raise notice 'conformance notice'; end $$"
-  isJust <$> getNotice connection
+  _ <- connection.exec "do $$ begin raise notice 'conformance notice'; end $$"
+  isJust <$> connection.getNotice
diff --git a/src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs b/src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs
--- a/src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs
+++ b/src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs
@@ -6,22 +6,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "enableNoticeReporting" do
     it "makes a raised notice retrievable" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         beforeEnable <- raiseNoticeAndCollect connection
-        enableNoticeReporting connection
+        connection.enableNoticeReporting
         afterEnable <- raiseNoticeAndCollect connection
         pure (beforeEnable, afterEnable)
 
-raiseNoticeAndCollect :: (IsConnection c) => c -> IO Bool
+raiseNoticeAndCollect :: Pqi.Connection -> IO Bool
 raiseNoticeAndCollect connection = do
-  _ <- exec connection "do $$ begin raise notice 'conformance notice'; end $$"
-  isJust <$> getNotice connection
+  _ <- connection.exec "do $$ begin raise notice 'conformance notice'; end $$"
+  isJust <$> connection.getNotice
diff --git a/src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs b/src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs
--- a/src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs
+++ b/src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "enterPipelineMode" do
     it "enters pipeline mode and is idempotent" \conninfo ->
-      differential proxy conninfo \connection -> do
-        before <- pipelineStatus connection
-        entered <- enterPipelineMode connection
-        enteredAgain <- enterPipelineMode connection
-        while <- pipelineStatus connection
-        _ <- exitPipelineMode connection
+      differential adapter conninfo \connection -> do
+        before <- connection.pipelineStatus
+        entered <- connection.enterPipelineMode
+        enteredAgain <- connection.enterPipelineMode
+        while <- connection.pipelineStatus
+        _ <- connection.exitPipelineMode
         pure (before, entered, enteredAgain, while)
diff --git a/src/library/Pqi/Conformance/Operation/ErrorMessage.hs b/src/library/Pqi/Conformance/Operation/ErrorMessage.hs
--- a/src/library/Pqi/Conformance/Operation/ErrorMessage.hs
+++ b/src/library/Pqi/Conformance/Operation/ErrorMessage.hs
@@ -15,48 +15,48 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "errorMessage" do
     it "is empty on a healthy connection" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "select 1"
-        errorMessage connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "select 1"
+        connection.errorMessage
 
     it "is populated after a failed exec and cleared by a subsequent success" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "do $$ begin raise exception 'conformance error'; end $$"
-        afterFail <- errorMessage connection
-        _ <- exec connection "select 1"
-        afterSuccess <- errorMessage connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "do $$ begin raise exception 'conformance error'; end $$"
+        afterFail <- connection.errorMessage
+        _ <- connection.exec "select 1"
+        afterSuccess <- connection.errorMessage
         pure (afterFail, afterSuccess)
 
     it "is populated after a failed getResult and cleared by a subsequent success" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- sendQuery connection "do $$ begin raise exception 'conformance error'; end $$"
+      differential adapter conninfo \connection -> do
+        _ <- connection.sendQuery "do $$ begin raise exception 'conformance error'; end $$"
         _ <- drainResults connection
-        afterFail <- errorMessage connection
-        _ <- sendQuery connection "select 1"
+        afterFail <- connection.errorMessage
+        _ <- connection.sendQuery "select 1"
         _ <- drainResults connection
-        afterSuccess <- errorMessage connection
+        afterSuccess <- connection.errorMessage
         pure (afterFail, afterSuccess)
 
     it "is populated after a connection failure" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) conninfo' -> do
-        conn <- connectdb (conninfo' <> " user=pqi_no_such_user") :: IO c
-        msg <- errorMessage conn
-        finish conn
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        conn <- adapter'.connectdb (conninfo' <> " user=pqi_no_such_user")
+        msg <- conn.errorMessage
+        conn.finish
         pure msg
 
     it "is the null-connection sentinel on a null connection" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) _ -> do
-        conn <- newNullConnection :: IO c
-        msg <- errorMessage conn
-        finish conn
+      differentialConnect adapter conninfo \adapter' _ -> do
+        conn <- adapter'.newNullConnection
+        msg <- conn.errorMessage
+        conn.finish
         pure msg
diff --git a/src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs b/src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs
--- a/src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs
+++ b/src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs
@@ -6,17 +6,17 @@
 where
 
 import qualified Data.ByteString as ByteString
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "escapeByteaConn" do
     it "escapes a range of binary inputs" \conninfo ->
-      differential proxy conninfo \connection ->
-        traverse (escapeByteaConn connection) byteaCases
+      differential adapter conninfo \connection ->
+        traverse connection.escapeByteaConn byteaCases
   where
     byteaCases =
       [ "",
diff --git a/src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs b/src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs
--- a/src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs
+++ b/src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs
@@ -5,22 +5,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "escapeIdentifier" do
     it "escapes a range of identifiers" \conninfo ->
-      differential proxy conninfo \connection ->
-        traverse (escapeIdentifier connection) identifierCases
+      differential adapter conninfo \connection ->
+        traverse connection.escapeIdentifier identifierCases
 
     it "produces identifiers that round-trip through a query" \conninfo ->
-      differential proxy conninfo \connection -> do
-        escaped <- escapeIdentifier connection "Wéird \"column\" name"
+      differential adapter conninfo \connection -> do
+        escaped <- connection.escapeIdentifier "Wéird \"column\" name"
         for escaped \identifier ->
           execScenario ("select 1 as " <> identifier) connection
   where
diff --git a/src/library/Pqi/Conformance/Operation/EscapeStringConn.hs b/src/library/Pqi/Conformance/Operation/EscapeStringConn.hs
--- a/src/library/Pqi/Conformance/Operation/EscapeStringConn.hs
+++ b/src/library/Pqi/Conformance/Operation/EscapeStringConn.hs
@@ -5,26 +5,26 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "escapeStringConn" do
     it "escapes a range of strings" \conninfo ->
-      differential proxy conninfo \connection ->
-        traverse (escapeStringConn connection) stringCases
+      differential adapter conninfo \connection ->
+        traverse connection.escapeStringConn stringCases
 
     it "rejects invalid encoding" \conninfo ->
-      differential proxy conninfo \connection ->
-        escapeStringConn connection "\255\254"
+      differential adapter conninfo \connection ->
+        connection.escapeStringConn "\255\254"
 
     it "produces literals that round-trip through a query" \conninfo ->
-      differential proxy conninfo \connection -> do
-        escaped <- escapeStringConn connection "it's \\ tricky\nstuff"
+      differential adapter conninfo \connection -> do
+        escaped <- connection.escapeStringConn "it's \\ tricky\nstuff"
         for escaped \literal ->
           execScenario ("select '" <> literal <> "' :: text") connection
   where
diff --git a/src/library/Pqi/Conformance/Operation/Exec.hs b/src/library/Pqi/Conformance/Operation/Exec.hs
--- a/src/library/Pqi/Conformance/Operation/Exec.hs
+++ b/src/library/Pqi/Conformance/Operation/Exec.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection)
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "exec" do
     let forCase title sql =
-          it title \conninfo -> differential proxy conninfo (execScenario sql)
+          it title \conninfo -> differential adapter conninfo (execScenario sql)
     forCase "select literal" "select 1"
     forCase "multi-row, multi-column" "select i, i * 2 from generate_series (1, 3) as i"
     forCase "nulls and text" "select null :: int4, 'hello' :: text, true"
diff --git a/src/library/Pqi/Conformance/Operation/ExecParams.hs b/src/library/Pqi/Conformance/Operation/ExecParams.hs
--- a/src/library/Pqi/Conformance/Operation/ExecParams.hs
+++ b/src/library/Pqi/Conformance/Operation/ExecParams.hs
@@ -6,7 +6,6 @@
   )
 where
 
-import Pqi (IsConnection (..))
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
@@ -14,31 +13,31 @@
 import Pqi.Conformance.Scenario
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Lq.Adapter -> SpecWith ByteString
+spec adapter =
   describe "execParams" do
     it "text result format" \conninfo ->
-      differential proxy conninfo (paramsScenario Lq.Text)
+      differential adapter conninfo (paramsScenario Lq.Text)
     it "binary result format" \conninfo ->
-      differential proxy conninfo (paramsScenario Lq.Binary)
+      differential adapter conninfo (paramsScenario Lq.Binary)
     it "null parameter" \conninfo ->
-      differential proxy conninfo (observed "select $1 :: int4 as maybe_value" [Nothing] Lq.Text)
+      differential adapter conninfo (observed "select $1 :: int4 as maybe_value" [Nothing] Lq.Text)
     it "no parameters" \conninfo ->
-      differential proxy conninfo (observed "select 'none' :: text" [] Lq.Text)
+      differential adapter conninfo (observed "select 'none' :: text" [] Lq.Text)
     it "binary parameter" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: int4 * 2" [Just (int4Oid, "\NUL\NUL\NUL*", Lq.Binary)] Lq.Text
     it "binary bytea round-trip" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: bytea" [Just (byteaOid, "\NUL\1\2\255", Lq.Binary)] Lq.Binary
     it "inferred parameter type" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: int4 + 1" [Just (0, "41", Lq.Text)] Lq.Text
     it "empty string is not null" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: text, length ($1 :: text)" [Just (textOid, "", Lq.Text)] Lq.Text
     it "many mixed parameters" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed
           "select $1 :: int8, $2 :: float8, $3 :: bool, $4 :: text, $5 :: int4, $6 :: text"
           [ Just (int8Oid, "9000000000000000000", Lq.Text),
@@ -50,8 +49,8 @@
           ]
           Lq.Text
     it "DML with parameters" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_exec_params (id int4)"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_exec_params (id int4)"
         insert <-
           observed
             "insert into conformance_exec_params values ($1), ($2)"
@@ -66,15 +65,15 @@
             connection
         pure (insert, check)
     it "too few parameters" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: int4 + $2 :: int4" [Just (int4Oid, "1", Lq.Text)] Lq.Text
     it "malformed parameter value" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ observed "select $1 :: int4" [Just (int4Oid, "not-a-number", Lq.Text)] Lq.Text
     it "multiple statements are rejected" \conninfo ->
-      differential proxy conninfo (observed "select 1; select 2" [] Lq.Text)
+      differential adapter conninfo (observed "select 1; select 2" [] Lq.Text)
 
-paramsScenario :: (IsConnection c) => Lq.Format -> c -> IO (Maybe ResultObservation)
+paramsScenario :: Lq.Format -> Lq.Connection -> IO (Maybe ResultObservation)
 paramsScenario resultFormat =
   observed
     "select $1 :: int4 + $2 :: int4 as sum, $3 :: text as label"
diff --git a/src/library/Pqi/Conformance/Operation/ExecPrepared.hs b/src/library/Pqi/Conformance/Operation/ExecPrepared.hs
--- a/src/library/Pqi/Conformance/Operation/ExecPrepared.hs
+++ b/src/library/Pqi/Conformance/Operation/ExecPrepared.hs
@@ -6,41 +6,40 @@
   )
 where
 
-import Pqi (IsConnection (..))
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Lq.Adapter -> SpecWith ByteString
+spec adapter =
   describe "execPrepared" do
     it "executes a prepared statement" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_stmt" "select $1 :: text as a, $2 :: int4 as b" Nothing
-        execPrepared connection "conformance_stmt" [Just ("hello", Lq.Text), Just ("7", Lq.Text)] Lq.Text
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_stmt" "select $1 :: text as a, $2 :: int4 as b" Nothing
+        connection.execPrepared "conformance_stmt" [Just ("hello", Lq.Text), Just ("7", Lq.Text)] Lq.Text
           >>= traverse observeResult
 
     it "executes the unnamed prepared statement" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "" "select $1 :: int4 + 1" Nothing
-        execPrepared connection "" [Just ("41", Lq.Text)] Lq.Text
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "" "select $1 :: int4 + 1" Nothing
+        connection.execPrepared "" [Just ("41", Lq.Text)] Lq.Text
           >>= traverse observeResult
 
     it "executes a zero-parameter statement" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_no_params" "select 42" Nothing
-        execPrepared connection "conformance_no_params" [] Lq.Text
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_no_params" "select 42" Nothing
+        connection.execPrepared "conformance_no_params" [] Lq.Text
           >>= traverse observeResult
 
     it "binds null and binary parameters with a binary result" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_binary" "select $1 :: text, $2 :: bytea" Nothing
-        execPrepared connection "conformance_binary" [Nothing, Just ("\NUL\1\255", Lq.Binary)] Lq.Binary
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_binary" "select $1 :: text, $2 :: bytea" Nothing
+        connection.execPrepared "conformance_binary" [Nothing, Just ("\NUL\1\255", Lq.Binary)] Lq.Binary
           >>= traverse observeResult
 
     it "rejects an unknown statement" \conninfo ->
-      differential proxy conninfo \connection ->
-        execPrepared connection "conformance_missing" [] Lq.Text
+      differential adapter conninfo \connection ->
+        connection.execPrepared "conformance_missing" [] Lq.Text
           >>= traverse observeResult
diff --git a/src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs b/src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs
--- a/src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs
+++ b/src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs
@@ -5,7 +5,6 @@
   )
 where
 
-import Pqi (IsConnection (..))
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
@@ -13,25 +12,25 @@
 import System.Timeout (timeout)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Lq.Adapter -> SpecWith ByteString
+spec adapter =
   describe "exitPipelineMode" do
     it "returns the connection to its non-pipeline status" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- enterPipelineMode connection
-        exited <- exitPipelineMode connection
-        after <- pipelineStatus connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.enterPipelineMode
+        exited <- connection.exitPipelineMode
+        after <- connection.pipelineStatus
         pure (exited, after)
 
     it "fails with work pending and succeeds once drained" \conninfo ->
-      differential proxy conninfo \connection -> do
-        entered <- enterPipelineMode connection
-        sent <- sendQueryParams connection "select 1" [] Lq.Text
-        prematureExit <- exitPipelineMode connection
-        synced <- pipelineSync connection
+      differential adapter conninfo \connection -> do
+        entered <- connection.enterPipelineMode
+        sent <- connection.sendQueryParams "select 1" [] Lq.Text
+        prematureExit <- connection.exitPipelineMode
+        synced <- connection.pipelineSync
         results <- takeCommandResults connection
         syncResult <- takeResult connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure (entered, sent, prematureExit, synced, results, syncResult, exited)
 
     -- Reproduces the cleanup sequence that hasql's cleanUpAfterInterruption +
@@ -41,14 +40,14 @@
     -- is consumed, then the connection is restored via the exact drain/sync
     -- sequence that hasql uses.
     it "recovers after mid-pipeline cancel (mirrors cleanUpAfterInterruption)" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- enterPipelineMode connection
-        _ <- sendPrepare connection "s1" "select 1" Nothing
-        _ <- sendQueryPrepared connection "s1" [] Lq.Text
-        _ <- sendPrepare connection "s2" "select pg_sleep($1)" (Just [float8Oid])
-        _ <- sendQueryPrepared connection "s2" [Just ("0.5", Lq.Text)] Lq.Text
-        _ <- pipelineSync connection
-        _ <- sendFlushRequest connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.enterPipelineMode
+        _ <- connection.sendPrepare "s1" "select 1" Nothing
+        _ <- connection.sendQueryPrepared "s1" [] Lq.Text
+        _ <- connection.sendPrepare "s2" "select pg_sleep($1)" (Just [float8Oid])
+        _ <- connection.sendQueryPrepared "s2" [Just ("0.5", Lq.Text)] Lq.Text
+        _ <- connection.pipelineSync
+        _ <- connection.sendFlushRequest
         -- Consume what toPipelineIO would have read before the timeout:
         -- parse1 result + separator, exec1 result + separator, parse2 result + separator.
         _ <- takeCommandResults connection
@@ -56,17 +55,17 @@
         _ <- takeCommandResults connection
         -- exec2 (pg_sleep) is still running; cancel it to simulate the
         -- timeout-triggered cancel in cleanUpAfterInterruption.
-        mHandle <- getCancel connection
-        _ <- for mHandle Lq.cancel
+        mHandle <- connection.getCancel
+        _ <- for mHandle (.cancel)
         -- cleanUpAfterInterruption: drain1, then drain2 (after cancel)
         _ <- drainResults connection
         _ <- drainResults connection
         -- leavePipeline: new Sync, drain, Flush, drain
-        _ <- pipelineSync connection
+        _ <- connection.pipelineSync
         _ <- drainResults connection
-        _ <- sendFlushRequest connection
+        _ <- connection.sendFlushRequest
         _ <- drainResults connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure exited
 
     -- Reproduces the failure seen in hasql's "Leaves the connection usable
@@ -82,36 +81,36 @@
     -- currently fails to leave pipeline mode, which is the bug this scenario
     -- captures.
     it "recovers after timeout interrupts mid-pipeline read" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- enterPipelineMode connection
-        _ <- sendPrepare connection "s1" "select $1::int" Nothing
-        _ <- sendQueryPrepared connection "s1" [Just ("42", Lq.Text)] Lq.Text
-        _ <- sendPrepare connection "s2" "select pg_sleep($1)" (Just [float8Oid])
-        _ <- sendQueryPrepared connection "s2" [Just ("0.1", Lq.Text)] Lq.Text
-        _ <- pipelineSync connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.enterPipelineMode
+        _ <- connection.sendPrepare "s1" "select $1::int" Nothing
+        _ <- connection.sendQueryPrepared "s1" [Just ("42", Lq.Text)] Lq.Text
+        _ <- connection.sendPrepare "s2" "select pg_sleep($1)" (Just [float8Oid])
+        _ <- connection.sendQueryPrepared "s2" [Just ("0.1", Lq.Text)] Lq.Text
+        _ <- connection.pipelineSync
         -- Interrupt the read just like hasql's Connection.use + timeout does.
         _ <- timeout 50000 (drainResults connection)
         -- cleanUpAfterInterruption
         _ <- drainResults connection
-        mHandle <- getCancel connection
-        _ <- for mHandle Lq.cancel
+        mHandle <- connection.getCancel
+        _ <- for mHandle (.cancel)
         _ <- drainResults connection
         -- leavePipeline (including the retry that hasql performs)
-        pipelineStatusBefore <- pipelineStatus connection
+        pipelineStatusBefore <- connection.pipelineStatus
         exited <-
           if pipelineStatusBefore == Lq.PipelineOn
             then do
-              _ <- pipelineSync connection
+              _ <- connection.pipelineSync
               _ <- drainResults connection
-              _ <- sendFlushRequest connection
+              _ <- connection.sendFlushRequest
               _ <- drainResults connection
-              ok <- exitPipelineMode connection
+              ok <- connection.exitPipelineMode
               if ok
                 then pure True
                 else do
                   _ <- drainResults connection
-                  exitPipelineMode connection
+                  connection.exitPipelineMode
             else pure True
-        afterStatus <- pipelineStatus connection
+        afterStatus <- connection.pipelineStatus
         usable <- execScenario "select 99" connection
         pure (exited, afterStatus, usable)
diff --git a/src/library/Pqi/Conformance/Operation/Fformat.hs b/src/library/Pqi/Conformance/Operation/Fformat.hs
--- a/src/library/Pqi/Conformance/Operation/Fformat.hs
+++ b/src/library/Pqi/Conformance/Operation/Fformat.hs
@@ -6,22 +6,21 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Lq.Adapter -> SpecWith ByteString
+spec adapter =
   describe "fformat" do
     it "follows the requested result format" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         let formatsOf fmt =
-              execParams connection "select 1 :: int4, 'x' :: text" [] fmt
+              connection.execParams "select 1 :: int4, 'x' :: text" [] fmt
                 >>= traverse \r -> do
-                  n <- nfields r
-                  traverse (fformat r) [0 .. n - 1]
+                  n <- r.nfields
+                  traverse r.fformat [0 .. n - 1]
         textFormats <- formatsOf Lq.Text
         binaryFormats <- formatsOf Lq.Binary
         pure (textFormats, binaryFormats)
diff --git a/src/library/Pqi/Conformance/Operation/Finish.hs b/src/library/Pqi/Conformance/Operation/Finish.hs
--- a/src/library/Pqi/Conformance/Operation/Finish.hs
+++ b/src/library/Pqi/Conformance/Operation/Finish.hs
@@ -5,23 +5,23 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "finish" do
     it "closes an open connection cleanly" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) conninfo' -> do
-        connection <- connectdb conninfo' :: IO c
-        before <- status connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        connection <- adapter'.connectdb conninfo'
+        before <- connection.status
+        connection.finish
         pure before
 
     it "closes the null sentinel cleanly" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) _ -> do
-        connection <- newNullConnection :: IO c
-        finish connection
+      differentialConnect adapter conninfo \adapter' _ -> do
+        connection <- adapter'.newNullConnection
+        connection.finish
         pure ()
diff --git a/src/library/Pqi/Conformance/Operation/Flush.hs b/src/library/Pqi/Conformance/Operation/Flush.hs
--- a/src/library/Pqi/Conformance/Operation/Flush.hs
+++ b/src/library/Pqi/Conformance/Operation/Flush.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults, flushUntilDone)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "flush" do
     it "flushes queued output to completion in non-blocking mode" \conninfo ->
-      differential proxy conninfo \connection -> do
-        setOk <- setnonblocking connection True
-        sent <- sendQuery connection "select 1"
-        flushed <- flushUntilDone (flush connection)
+      differential adapter conninfo \connection -> do
+        setOk <- connection.setnonblocking True
+        sent <- connection.sendQuery "select 1"
+        flushed <- flushUntilDone connection.flush
         results <- drainResults connection
-        restoreOk <- setnonblocking connection False
+        restoreOk <- connection.setnonblocking False
         pure (setOk, sent, flushed, results, restoreOk)
diff --git a/src/library/Pqi/Conformance/Operation/Fmod.hs b/src/library/Pqi/Conformance/Operation/Fmod.hs
--- a/src/library/Pqi/Conformance/Operation/Fmod.hs
+++ b/src/library/Pqi/Conformance/Operation/Fmod.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "fmod" do
     it "reports type modifiers and degrades out of range" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         result <-
-          exec connection "select 1.5 :: numeric(10,2), 'pad' :: char(5), 'x' :: varchar(3), true, 1 :: int4"
+          connection.exec "select 1.5 :: numeric(10,2), 'pad' :: char(5), 'x' :: varchar(3), true, 1 :: int4"
         for result \r -> do
-          n <- nfields r
-          modifiers <- traverse (fmod r) [0 .. n - 1]
-          outOfRange <- fmod r 9
+          n <- r.nfields
+          modifiers <- traverse r.fmod [0 .. n - 1]
+          outOfRange <- r.fmod 9
           pure (modifiers, outOfRange)
diff --git a/src/library/Pqi/Conformance/Operation/Fname.hs b/src/library/Pqi/Conformance/Operation/Fname.hs
--- a/src/library/Pqi/Conformance/Operation/Fname.hs
+++ b/src/library/Pqi/Conformance/Operation/Fname.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "fname" do
     it "names columns and degrades out of range" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 1 as foo, 2 as bar"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 1 as foo, 2 as bar"
         for result \r -> do
-          n <- nfields r
-          names <- traverse (fname r) [0 .. n - 1]
-          outOfRange <- fname r 5
+          n <- r.nfields
+          names <- traverse r.fname [0 .. n - 1]
+          outOfRange <- r.fname 5
           pure (names, outOfRange)
diff --git a/src/library/Pqi/Conformance/Operation/Fnumber.hs b/src/library/Pqi/Conformance/Operation/Fnumber.hs
--- a/src/library/Pqi/Conformance/Operation/Fnumber.hs
+++ b/src/library/Pqi/Conformance/Operation/Fnumber.hs
@@ -6,18 +6,18 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "fnumber" do
     it "resolves names like an identifier" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 1 as foo, 2 as \"Bar\""
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 1 as foo, 2 as \"Bar\""
         for result \r ->
           traverse
-            (fnumber r)
+            r.fnumber
             ["foo", "FOO", "Foo", "Bar", "bar", "\"Bar\"", "\"foo\"", "missing"]
diff --git a/src/library/Pqi/Conformance/Operation/Fsize.hs b/src/library/Pqi/Conformance/Operation/Fsize.hs
--- a/src/library/Pqi/Conformance/Operation/Fsize.hs
+++ b/src/library/Pqi/Conformance/Operation/Fsize.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "fsize" do
     it "reports type sizes and degrades out of range" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 1 :: int2, 1 :: int4, 1 :: int8, 'x' :: text, true"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 1 :: int2, 1 :: int4, 1 :: int8, 'x' :: text, true"
         for result \r -> do
-          n <- nfields r
-          sizes <- traverse (fsize r) [0 .. n - 1]
-          outOfRange <- fsize r 9
+          n <- r.nfields
+          sizes <- traverse r.fsize [0 .. n - 1]
+          outOfRange <- r.fsize 9
           pure (sizes, outOfRange)
diff --git a/src/library/Pqi/Conformance/Operation/Ftable.hs b/src/library/Pqi/Conformance/Operation/Ftable.hs
--- a/src/library/Pqi/Conformance/Operation/Ftable.hs
+++ b/src/library/Pqi/Conformance/Operation/Ftable.hs
@@ -8,17 +8,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "ftable" do
     it "reports the source-table OID, or none for a computed column" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select relname, relkind, 1 as computed from pg_catalog.pg_class where false"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select relname, relkind, 1 as computed from pg_catalog.pg_class where false"
         for result \r -> do
-          n <- nfields r
-          traverse (ftable r) [0 .. n - 1]
+          n <- r.nfields
+          traverse r.ftable [0 .. n - 1]
diff --git a/src/library/Pqi/Conformance/Operation/Ftablecol.hs b/src/library/Pqi/Conformance/Operation/Ftablecol.hs
--- a/src/library/Pqi/Conformance/Operation/Ftablecol.hs
+++ b/src/library/Pqi/Conformance/Operation/Ftablecol.hs
@@ -8,17 +8,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "ftablecol" do
     it "reports the source-column number, or zero for a computed column" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select relname, relkind, 1 as computed from pg_catalog.pg_class where false"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select relname, relkind, 1 as computed from pg_catalog.pg_class where false"
         for result \r -> do
-          n <- nfields r
-          traverse (ftablecol r) [0 .. n - 1]
+          n <- r.nfields
+          traverse r.ftablecol [0 .. n - 1]
diff --git a/src/library/Pqi/Conformance/Operation/Ftype.hs b/src/library/Pqi/Conformance/Operation/Ftype.hs
--- a/src/library/Pqi/Conformance/Operation/Ftype.hs
+++ b/src/library/Pqi/Conformance/Operation/Ftype.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "ftype" do
     it "reports type OIDs and degrades out of range" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 1 :: int4, 'x' :: text, true, 1.5 :: float8, 1 :: int2"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 1 :: int4, 'x' :: text, true, 1.5 :: float8, 1 :: int2"
         for result \r -> do
-          n <- nfields r
-          types <- traverse (ftype r) [0 .. n - 1]
-          outOfRange <- ftype r 9
+          n <- r.nfields
+          types <- traverse r.ftype [0 .. n - 1]
+          outOfRange <- r.ftype 9
           pure (types, outOfRange)
diff --git a/src/library/Pqi/Conformance/Operation/GetCancel.hs b/src/library/Pqi/Conformance/Operation/GetCancel.hs
--- a/src/library/Pqi/Conformance/Operation/GetCancel.hs
+++ b/src/library/Pqi/Conformance/Operation/GetCancel.hs
@@ -5,14 +5,14 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getCancel" do
     it "produces a handle for an open connection" \conninfo ->
-      differential proxy conninfo \connection ->
-        isJust <$> getCancel connection
+      differential adapter conninfo \connection ->
+        isJust <$> connection.getCancel
diff --git a/src/library/Pqi/Conformance/Operation/GetCopyData.hs b/src/library/Pqi/Conformance/Operation/GetCopyData.hs
--- a/src/library/Pqi/Conformance/Operation/GetCopyData.hs
+++ b/src/library/Pqi/Conformance/Operation/GetCopyData.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection)
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (collectCopyOut, drainResults, execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getCopyData" do
     let copyOut sql conninfo =
-          differential proxy conninfo \connection -> do
+          differential adapter conninfo \connection -> do
             started <- execScenario sql connection
             rows <- collectCopyOut connection
             outcome <- drainResults connection
diff --git a/src/library/Pqi/Conformance/Operation/GetNotice.hs b/src/library/Pqi/Conformance/Operation/GetNotice.hs
--- a/src/library/Pqi/Conformance/Operation/GetNotice.hs
+++ b/src/library/Pqi/Conformance/Operation/GetNotice.hs
@@ -10,18 +10,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getNotice" do
     it "yields the formatted notice while enabled and then drains" \conninfo ->
-      differential proxy conninfo \connection -> do
-        enableNoticeReporting connection
-        _ <- exec connection "do $$ begin raise notice 'conformance notice'; end $$"
-        firstNotice <- getNotice connection
-        afterDrain <- getNotice connection
+      differential adapter conninfo \connection -> do
+        connection.enableNoticeReporting
+        _ <- connection.exec "do $$ begin raise notice 'conformance notice'; end $$"
+        firstNotice <- connection.getNotice
+        afterDrain <- connection.getNotice
         pure (firstNotice, afterDrain)
diff --git a/src/library/Pqi/Conformance/Operation/GetResult.hs b/src/library/Pqi/Conformance/Operation/GetResult.hs
--- a/src/library/Pqi/Conformance/Operation/GetResult.hs
+++ b/src/library/Pqi/Conformance/Operation/GetResult.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (takeResult)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getResult" do
     it "yields each result then a Nothing terminator" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- sendQuery connection "select 1 :: int4"
+      differential adapter conninfo \connection -> do
+        _ <- connection.sendQuery "select 1 :: int4"
         first <- takeResult connection
         terminator <- takeResult connection
         afterTerminator <- takeResult connection
diff --git a/src/library/Pqi/Conformance/Operation/Getisnull.hs b/src/library/Pqi/Conformance/Operation/Getisnull.hs
--- a/src/library/Pqi/Conformance/Operation/Getisnull.hs
+++ b/src/library/Pqi/Conformance/Operation/Getisnull.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getisnull" do
     it "distinguishes null, empty, and non-empty cells" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 'x' :: text, '' :: text, null :: int4"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 'x' :: text, '' :: text, null :: int4"
         for result \r -> do
-          nonEmpty <- getisnull r 0 0
-          empty <- getisnull r 0 1
-          nullCell <- getisnull r 0 2
+          nonEmpty <- r.getisnull 0 0
+          empty <- r.getisnull 0 1
+          nullCell <- r.getisnull 0 2
           pure (nonEmpty, empty, nullCell)
diff --git a/src/library/Pqi/Conformance/Operation/Getlength.hs b/src/library/Pqi/Conformance/Operation/Getlength.hs
--- a/src/library/Pqi/Conformance/Operation/Getlength.hs
+++ b/src/library/Pqi/Conformance/Operation/Getlength.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getlength" do
     it "reports byte lengths across cell shapes" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 'hello' :: text, 'héllo' :: text, '' :: text, null :: int4"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 'hello' :: text, 'héllo' :: text, '' :: text, null :: int4"
         for result \r -> do
-          n <- nfields r
-          traverse (getlength r 0) [0 .. n - 1]
+          n <- r.nfields
+          traverse (r.getlength 0) [0 .. n - 1]
diff --git a/src/library/Pqi/Conformance/Operation/Getvalue.hs b/src/library/Pqi/Conformance/Operation/Getvalue.hs
--- a/src/library/Pqi/Conformance/Operation/Getvalue.hs
+++ b/src/library/Pqi/Conformance/Operation/Getvalue.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getvalue" do
     it "reads cells, nulls, and degrades out of range" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 'hello' :: text, null :: int4"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 'hello' :: text, null :: int4"
         for result \r -> do
-          present <- getvalue r 0 0
-          nullCell <- getvalue r 0 1
-          badRow <- getvalue r 1 0
-          badColumn <- getvalue r 0 5
+          present <- r.getvalue 0 0
+          nullCell <- r.getvalue 0 1
+          badRow <- r.getvalue 1 0
+          badColumn <- r.getvalue 0 5
           pure (present, nullCell, badRow, badColumn)
diff --git a/src/library/Pqi/Conformance/Operation/GetvalueCopy.hs b/src/library/Pqi/Conformance/Operation/GetvalueCopy.hs
--- a/src/library/Pqi/Conformance/Operation/GetvalueCopy.hs
+++ b/src/library/Pqi/Conformance/Operation/GetvalueCopy.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "getvalue'" do
     it "copies cells that survive freeing the result" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 'copied' :: text, null :: int4"
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 'copied' :: text, null :: int4"
         for result \r -> do
-          first <- getvalue' r 0 0
-          second <- getvalue' r 0 1
-          unsafeFreeResult r
+          first <- r.getvalue' 0 0
+          second <- r.getvalue' 0 1
+          r.unsafeFreeResult
           pure (first, second)
diff --git a/src/library/Pqi/Conformance/Operation/Host.hs b/src/library/Pqi/Conformance/Operation/Host.hs
--- a/src/library/Pqi/Conformance/Operation/Host.hs
+++ b/src/library/Pqi/Conformance/Operation/Host.hs
@@ -4,13 +4,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "host" do
     it "reports the host from the conninfo" \conninfo ->
-      differential proxy conninfo host
+      differential adapter conninfo (.host)
diff --git a/src/library/Pqi/Conformance/Operation/IsBusy.hs b/src/library/Pqi/Conformance/Operation/IsBusy.hs
--- a/src/library/Pqi/Conformance/Operation/IsBusy.hs
+++ b/src/library/Pqi/Conformance/Operation/IsBusy.hs
@@ -5,22 +5,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "isBusy" do
     it "settles to not-busy after the result arrives" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- sendQuery connection "select 42"
-        let settle (0 :: Int) = isBusy connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.sendQuery "select 42"
+        let settle (0 :: Int) = connection.isBusy
             settle n = do
-              _ <- consumeInput connection
-              busy <- isBusy connection
+              _ <- connection.consumeInput
+              busy <- connection.isBusy
               if busy then threadDelay 1000 >> settle (n - 1) else pure busy
         stillBusy <- settle 10000
         _ <- drainResults connection
diff --git a/src/library/Pqi/Conformance/Operation/IsNullConnection.hs b/src/library/Pqi/Conformance/Operation/IsNullConnection.hs
--- a/src/library/Pqi/Conformance/Operation/IsNullConnection.hs
+++ b/src/library/Pqi/Conformance/Operation/IsNullConnection.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "isNullConnection" do
     it "is True for the null sentinel and False for an open connection" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) conninfo' -> do
-        nullConn <- newNullConnection :: IO c
-        let nullIsNull = isNullConnection nullConn
-        finish nullConn
-        openConn <- connectdb conninfo' :: IO c
-        let openIsNull = isNullConnection openConn
-        finish openConn
+      differentialConnect adapter conninfo \adapter' conninfo' -> do
+        nullConn <- adapter'.newNullConnection
+        let nullIsNull = nullConn.isNullConnection
+        nullConn.finish
+        openConn <- adapter'.connectdb conninfo'
+        let openIsNull = openConn.isNullConnection
+        openConn.finish
         pure (nullIsNull, openIsNull)
diff --git a/src/library/Pqi/Conformance/Operation/Isnonblocking.hs b/src/library/Pqi/Conformance/Operation/Isnonblocking.hs
--- a/src/library/Pqi/Conformance/Operation/Isnonblocking.hs
+++ b/src/library/Pqi/Conformance/Operation/Isnonblocking.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "isnonblocking" do
     it "is off initially and reflects a change" \conninfo ->
-      differential proxy conninfo \connection -> do
-        initially <- isnonblocking connection
-        _ <- setnonblocking connection True
-        afterEnable <- isnonblocking connection
+      differential adapter conninfo \connection -> do
+        initially <- connection.isnonblocking
+        _ <- connection.setnonblocking True
+        afterEnable <- connection.isnonblocking
         pure (initially, afterEnable)
diff --git a/src/library/Pqi/Conformance/Operation/LoClose.hs b/src/library/Pqi/Conformance/Operation/LoClose.hs
--- a/src/library/Pqi/Conformance/Operation/LoClose.hs
+++ b/src/library/Pqi/Conformance/Operation/LoClose.hs
@@ -5,24 +5,24 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loClose" do
     it "closes the descriptor and invalidates further reads" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadMode
-            closed <- for fd \f -> loClose connection f
-            readAfterClose <- join <$> for fd \f -> loRead connection f 10
+            fd <- connection.loOpen o ReadMode
+            closed <- for fd (connection.loClose)
+            readAfterClose <- join <$> for fd \f -> connection.loRead f 10
             pure (closed, readAfterClose)
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/LoCreat.hs b/src/library/Pqi/Conformance/Operation/LoCreat.hs
--- a/src/library/Pqi/Conformance/Operation/LoCreat.hs
+++ b/src/library/Pqi/Conformance/Operation/LoCreat.hs
@@ -8,18 +8,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loCreat" do
     it "creates a large object" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
-          traverse_ (loUnlink connection) oid
+          oid <- connection.loCreat
+          traverse_ connection.loUnlink oid
           pure (isJust oid)
diff --git a/src/library/Pqi/Conformance/Operation/LoCreate.hs b/src/library/Pqi/Conformance/Operation/LoCreate.hs
--- a/src/library/Pqi/Conformance/Operation/LoCreate.hs
+++ b/src/library/Pqi/Conformance/Operation/LoCreate.hs
@@ -8,23 +8,23 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loCreate" do
     it "creates a large object with an explicit OID" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
           -- Best-effort cleanup of leftovers from an earlier crashed run; its
           -- outcome legitimately differs between runs, so it is not observed.
-          _ <- loUnlink connection explicitOid
-          created <- loCreate connection explicitOid
-          unlinked <- for created (loUnlink connection)
+          _ <- connection.loUnlink explicitOid
+          created <- connection.loCreate explicitOid
+          unlinked <- for created connection.loUnlink
           pure (created, unlinked)
   where
     explicitOid = 424242
diff --git a/src/library/Pqi/Conformance/Operation/LoExport.hs b/src/library/Pqi/Conformance/Operation/LoExport.hs
--- a/src/library/Pqi/Conformance/Operation/LoExport.hs
+++ b/src/library/Pqi/Conformance/Operation/LoExport.hs
@@ -9,26 +9,26 @@
 where
 
 import qualified Data.ByteString as ByteString
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import System.Directory (removeFile)
 import System.IO (hClose, openBinaryTempFile)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loExport" do
     it "exports an imported object, round-tripping its bytes" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         (importPath, importHandle) <- openBinaryTempFile "/tmp" "pqi-conformance-export-in"
         ByteString.hPut importHandle payload
         hClose importHandle
         (exportPath, exportHandle) <- openBinaryTempFile "/tmp" "pqi-conformance-export-out"
         hClose exportHandle
-        imported <- loImport connection importPath
-        exported <- for imported \o -> loExport connection o exportPath
-        traverse_ (loUnlink connection) imported
+        imported <- connection.loImport importPath
+        exported <- for imported \o -> connection.loExport o exportPath
+        traverse_ connection.loUnlink imported
         roundTripped <- ByteString.readFile exportPath
         removeFile importPath
         removeFile exportPath
diff --git a/src/library/Pqi/Conformance/Operation/LoImport.hs b/src/library/Pqi/Conformance/Operation/LoImport.hs
--- a/src/library/Pqi/Conformance/Operation/LoImport.hs
+++ b/src/library/Pqi/Conformance/Operation/LoImport.hs
@@ -9,22 +9,22 @@
 where
 
 import qualified Data.ByteString as ByteString
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import System.Directory (removeFile)
 import System.IO (hClose, openBinaryTempFile)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loImport" do
     it "imports a file as a large object" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         (path, handle) <- openBinaryTempFile "/tmp" "pqi-conformance-import"
         ByteString.hPut handle "pqi conformance payload"
         hClose handle
-        imported <- loImport connection path
-        traverse_ (loUnlink connection) imported
+        imported <- connection.loImport path
+        traverse_ connection.loUnlink imported
         removeFile path
         pure (isJust imported)
diff --git a/src/library/Pqi/Conformance/Operation/LoImportWithOid.hs b/src/library/Pqi/Conformance/Operation/LoImportWithOid.hs
--- a/src/library/Pqi/Conformance/Operation/LoImportWithOid.hs
+++ b/src/library/Pqi/Conformance/Operation/LoImportWithOid.hs
@@ -10,24 +10,24 @@
 where
 
 import qualified Data.ByteString as ByteString
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import System.Directory (removeFile)
 import System.IO (hClose, openBinaryTempFile)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loImportWithOid" do
     it "imports a file as a large object with an explicit OID" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         (path, handle) <- openBinaryTempFile "/tmp" "pqi-conformance-import-oid"
         ByteString.hPut handle "pqi conformance payload"
         hClose handle
-        _ <- loUnlink connection explicitOid
-        imported <- loImportWithOid connection path explicitOid
-        unlinked <- for imported (loUnlink connection)
+        _ <- connection.loUnlink explicitOid
+        imported <- connection.loImportWithOid path explicitOid
+        unlinked <- for imported connection.loUnlink
         removeFile path
         pure (imported, unlinked)
   where
diff --git a/src/library/Pqi/Conformance/Operation/LoOpen.hs b/src/library/Pqi/Conformance/Operation/LoOpen.hs
--- a/src/library/Pqi/Conformance/Operation/LoOpen.hs
+++ b/src/library/Pqi/Conformance/Operation/LoOpen.hs
@@ -5,26 +5,26 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loOpen" do
     it "opens an existing object and rejects a missing one" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           opened <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
-            traverse_ (loClose connection) fd
+            fd <- connection.loOpen o ReadWriteMode
+            traverse_ connection.loClose fd
             pure (isJust fd)
           loUnlink' oid connection
-          missing <- loOpen connection 4242424 ReadMode
+          missing <- connection.loOpen 4242424 ReadMode
           pure (opened, isJust missing)
   where
-    loUnlink' oid connection = traverse_ (loUnlink connection) oid
+    loUnlink' oid connection = traverse_ connection.loUnlink oid
diff --git a/src/library/Pqi/Conformance/Operation/LoRead.hs b/src/library/Pqi/Conformance/Operation/LoRead.hs
--- a/src/library/Pqi/Conformance/Operation/LoRead.hs
+++ b/src/library/Pqi/Conformance/Operation/LoRead.hs
@@ -5,27 +5,27 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..), SeekMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loRead" do
     it "reads back what was written" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
+            fd <- connection.loOpen o ReadWriteMode
             readBytes <- for fd \f -> do
-              _ <- loWrite connection f "hello, large object"
-              _ <- loSeek connection f AbsoluteSeek 0
-              loRead connection f 5
-            traverse_ (loClose connection) fd
+              _ <- connection.loWrite f "hello, large object"
+              _ <- connection.loSeek f AbsoluteSeek 0
+              connection.loRead f 5
+            traverse_ connection.loClose fd
             pure readBytes
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/LoSeek.hs b/src/library/Pqi/Conformance/Operation/LoSeek.hs
--- a/src/library/Pqi/Conformance/Operation/LoSeek.hs
+++ b/src/library/Pqi/Conformance/Operation/LoSeek.hs
@@ -5,29 +5,29 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..), SeekMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loSeek" do
     it "seeks absolutely, relatively, and from the end" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
+            fd <- connection.loOpen o ReadWriteMode
             seeks <- for fd \f -> do
-              _ <- loWrite connection f "hello, large object"
-              absolute <- loSeek connection f AbsoluteSeek 0
-              relative <- loSeek connection f RelativeSeek 2
-              fromEnd <- loSeek connection f SeekFromEnd (-6)
+              _ <- connection.loWrite f "hello, large object"
+              absolute <- connection.loSeek f AbsoluteSeek 0
+              relative <- connection.loSeek f RelativeSeek 2
+              fromEnd <- connection.loSeek f SeekFromEnd (-6)
               pure (absolute, relative, fromEnd)
-            traverse_ (loClose connection) fd
+            traverse_ connection.loClose fd
             pure seeks
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/LoTell.hs b/src/library/Pqi/Conformance/Operation/LoTell.hs
--- a/src/library/Pqi/Conformance/Operation/LoTell.hs
+++ b/src/library/Pqi/Conformance/Operation/LoTell.hs
@@ -5,29 +5,29 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..), SeekMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loTell" do
     it "reports the position after a write and a seek" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
+            fd <- connection.loOpen o ReadWriteMode
             positions <- for fd \f -> do
-              _ <- loWrite connection f "hello, large object"
-              afterWrite <- loTell connection f
-              _ <- loSeek connection f AbsoluteSeek 3
-              afterSeek <- loTell connection f
+              _ <- connection.loWrite f "hello, large object"
+              afterWrite <- connection.loTell f
+              _ <- connection.loSeek f AbsoluteSeek 3
+              afterSeek <- connection.loTell f
               pure (afterWrite, afterSeek)
-            traverse_ (loClose connection) fd
+            traverse_ connection.loClose fd
             pure positions
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/LoTruncate.hs b/src/library/Pqi/Conformance/Operation/LoTruncate.hs
--- a/src/library/Pqi/Conformance/Operation/LoTruncate.hs
+++ b/src/library/Pqi/Conformance/Operation/LoTruncate.hs
@@ -5,28 +5,28 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..), SeekMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loTruncate" do
     it "truncates to a new size" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
+            fd <- connection.loOpen o ReadWriteMode
             result <- for fd \f -> do
-              _ <- loWrite connection f "hello, large object"
-              truncated <- loTruncate connection f 5
-              newEnd <- loSeek connection f SeekFromEnd 0
+              _ <- connection.loWrite f "hello, large object"
+              truncated <- connection.loTruncate f 5
+              newEnd <- connection.loSeek f SeekFromEnd 0
               pure (truncated, newEnd)
-            traverse_ (loClose connection) fd
+            traverse_ connection.loClose fd
             pure result
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/LoUnlink.hs b/src/library/Pqi/Conformance/Operation/LoUnlink.hs
--- a/src/library/Pqi/Conformance/Operation/LoUnlink.hs
+++ b/src/library/Pqi/Conformance/Operation/LoUnlink.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loUnlink" do
     it "removes an existing object and rejects a missing one" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
-          removed <- for oid (loUnlink connection)
-          missing <- loUnlink connection 4242424
+          oid <- connection.loCreat
+          removed <- for oid connection.loUnlink
+          missing <- connection.loUnlink 4242424
           pure (removed, missing)
diff --git a/src/library/Pqi/Conformance/Operation/LoWrite.hs b/src/library/Pqi/Conformance/Operation/LoWrite.hs
--- a/src/library/Pqi/Conformance/Operation/LoWrite.hs
+++ b/src/library/Pqi/Conformance/Operation/LoWrite.hs
@@ -4,24 +4,24 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (inTransaction)
 import System.IO (IOMode (..))
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "loWrite" do
     it "reports the number of bytes written" \conninfo ->
-      differential proxy conninfo \connection ->
+      differential adapter conninfo \connection ->
         inTransaction connection do
-          oid <- loCreat connection
+          oid <- connection.loCreat
           outcome <- for oid \o -> do
-            fd <- loOpen connection o ReadWriteMode
-            written <- for fd \f -> loWrite connection f "hello, large object"
-            traverse_ (loClose connection) fd
+            fd <- connection.loOpen o ReadWriteMode
+            written <- for fd \f -> connection.loWrite f "hello, large object"
+            traverse_ connection.loClose fd
             pure written
-          traverse_ (loUnlink connection) oid
+          traverse_ connection.loUnlink oid
           pure outcome
diff --git a/src/library/Pqi/Conformance/Operation/NewNullConnection.hs b/src/library/Pqi/Conformance/Operation/NewNullConnection.hs
--- a/src/library/Pqi/Conformance/Operation/NewNullConnection.hs
+++ b/src/library/Pqi/Conformance/Operation/NewNullConnection.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "newNullConnection" do
     it "produces a connection that is null and bad" \conninfo ->
-      differentialConnect proxy conninfo \(_ :: Proxy c) _ -> do
-        connection <- newNullConnection :: IO c
-        nullness <- pure (isNullConnection connection)
-        badness <- status connection
-        finish connection
+      differentialConnect adapter conninfo \adapter' _ -> do
+        connection <- adapter'.newNullConnection
+        nullness <- pure connection.isNullConnection
+        badness <- connection.status
+        connection.finish
         pure (nullness, badness)
diff --git a/src/library/Pqi/Conformance/Operation/Nfields.hs b/src/library/Pqi/Conformance/Operation/Nfields.hs
--- a/src/library/Pqi/Conformance/Operation/Nfields.hs
+++ b/src/library/Pqi/Conformance/Operation/Nfields.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "nfields" do
     it "counts columns across result shapes" \conninfo ->
-      differential proxy conninfo \connection -> do
-        let countOf sql = exec connection sql >>= traverse nfields
+      differential adapter conninfo \connection -> do
+        let countOf sql = connection.exec sql >>= traverse (.nfields)
         several <- countOf "select 1, 2, 3"
         zero <- countOf "select"
         command <- countOf "create temporary table conformance_nfields (id int4)"
diff --git a/src/library/Pqi/Conformance/Operation/Notifies.hs b/src/library/Pqi/Conformance/Operation/Notifies.hs
--- a/src/library/Pqi/Conformance/Operation/Notifies.hs
+++ b/src/library/Pqi/Conformance/Operation/Notifies.hs
@@ -12,46 +12,47 @@
   )
 where
 
-import Pqi (IsConnection (..), Notify (..))
+import Pqi (Notify (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "notifies" do
     it "is empty with no pending notifications" \conninfo ->
-      differential proxy conninfo \connection ->
-        fmap channelAndPayload <$> notifies connection
+      differential adapter conninfo \connection ->
+        fmap channelAndPayload <$> connection.notifies
 
     it "delivers a listen/notify round-trip and then drains" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "listen conformance_channel"
-        _ <- exec connection "notify conformance_channel, 'payload-1'"
-        notification <- notifies connection
-        pid <- backendPID connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "listen conformance_channel"
+        _ <- connection.exec "notify conformance_channel, 'payload-1'"
+        notification <- connection.notifies
+        pid <- connection.backendPID
         for_ notification \n -> n.bePid `shouldBe` pid
-        drained <- fmap channelAndPayload <$> notifies connection
+        drained <- fmap channelAndPayload <$> connection.notifies
         pure (fmap channelAndPayload notification, drained)
 
     it "queues notifications in order" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "listen conformance_channel"
-        _ <- exec connection "notify conformance_channel, 'first'"
-        _ <- exec connection "notify conformance_channel, 'second'"
-        first <- notifies connection
-        second <- notifies connection
-        third <- notifies connection
-        pid <- backendPID connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "listen conformance_channel"
+        _ <- connection.exec "notify conformance_channel, 'first'"
+        _ <- connection.exec "notify conformance_channel, 'second'"
+        first <- connection.notifies
+        second <- connection.notifies
+        third <- connection.notifies
+        pid <- connection.backendPID
         for_ first \n -> n.bePid `shouldBe` pid
         for_ second \n -> n.bePid `shouldBe` pid
         pure (fmap channelAndPayload first, fmap channelAndPayload second, fmap channelAndPayload third)
 
     it "stops delivery after unlisten" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "listen conformance_channel"
-        _ <- exec connection "unlisten conformance_channel"
-        _ <- exec connection "notify conformance_channel, 'lost'"
-        fmap channelAndPayload <$> notifies connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "listen conformance_channel"
+        _ <- connection.exec "unlisten conformance_channel"
+        _ <- connection.exec "notify conformance_channel, 'lost'"
+        fmap channelAndPayload <$> connection.notifies
   where
     channelAndPayload notification = (notification.relname, notification.extra)
diff --git a/src/library/Pqi/Conformance/Operation/Nparams.hs b/src/library/Pqi/Conformance/Operation/Nparams.hs
--- a/src/library/Pqi/Conformance/Operation/Nparams.hs
+++ b/src/library/Pqi/Conformance/Operation/Nparams.hs
@@ -5,22 +5,22 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "nparams" do
     it "counts the parameters of a prepared statement" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_nparams" "select $1 :: int4, $2 :: text" Nothing
-        described <- describePrepared connection "conformance_nparams"
-        for described nparams
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_nparams" "select $1 :: int4, $2 :: text" Nothing
+        described <- connection.describePrepared "conformance_nparams"
+        for described (.nparams)
 
     it "is zero for a parameterless statement" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_nparams_zero" "select 42" Nothing
-        described <- describePrepared connection "conformance_nparams_zero"
-        for described nparams
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_nparams_zero" "select 42" Nothing
+        described <- connection.describePrepared "conformance_nparams_zero"
+        for described (.nparams)
diff --git a/src/library/Pqi/Conformance/Operation/Ntuples.hs b/src/library/Pqi/Conformance/Operation/Ntuples.hs
--- a/src/library/Pqi/Conformance/Operation/Ntuples.hs
+++ b/src/library/Pqi/Conformance/Operation/Ntuples.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "ntuples" do
     it "counts rows across result shapes" \conninfo ->
-      differential proxy conninfo \connection -> do
-        let countOf sql = exec connection sql >>= traverse ntuples
+      differential adapter conninfo \connection -> do
+        let countOf sql = connection.exec sql >>= traverse (.ntuples)
         many <- countOf "select i from generate_series (1, 3) as i"
         none <- countOf "select 1 where false"
         command <- countOf "create temporary table conformance_ntuples (id int4)"
diff --git a/src/library/Pqi/Conformance/Operation/Options.hs b/src/library/Pqi/Conformance/Operation/Options.hs
--- a/src/library/Pqi/Conformance/Operation/Options.hs
+++ b/src/library/Pqi/Conformance/Operation/Options.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "options" do
     it "reports the command-line options from the conninfo" \conninfo ->
-      differential proxy conninfo options
+      differential adapter conninfo (.options)
diff --git a/src/library/Pqi/Conformance/Operation/ParameterStatus.hs b/src/library/Pqi/Conformance/Operation/ParameterStatus.hs
--- a/src/library/Pqi/Conformance/Operation/ParameterStatus.hs
+++ b/src/library/Pqi/Conformance/Operation/ParameterStatus.hs
@@ -5,21 +5,21 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "parameterStatus" do
     it "reports parameter statuses, including GUC_REPORT updates" \conninfo ->
-      differential proxy conninfo \connection -> do
-        before <- parameterStatus connection "application_name"
-        _ <- exec connection "set application_name to 'pqi-conformance'"
-        after <- parameterStatus connection "application_name"
-        clientEncoding <- parameterStatus connection "client_encoding"
-        standardConformingStrings <- parameterStatus connection "standard_conforming_strings"
-        integerDatetimes <- parameterStatus connection "integer_datetimes"
-        missing <- parameterStatus connection "no_such_parameter"
+      differential adapter conninfo \connection -> do
+        before <- connection.parameterStatus "application_name"
+        _ <- connection.exec "set application_name to 'pqi-conformance'"
+        after <- connection.parameterStatus "application_name"
+        clientEncoding <- connection.parameterStatus "client_encoding"
+        standardConformingStrings <- connection.parameterStatus "standard_conforming_strings"
+        integerDatetimes <- connection.parameterStatus "integer_datetimes"
+        missing <- connection.parameterStatus "no_such_parameter"
         pure (before, after, clientEncoding, standardConformingStrings, integerDatetimes, missing)
diff --git a/src/library/Pqi/Conformance/Operation/Paramtype.hs b/src/library/Pqi/Conformance/Operation/Paramtype.hs
--- a/src/library/Pqi/Conformance/Operation/Paramtype.hs
+++ b/src/library/Pqi/Conformance/Operation/Paramtype.hs
@@ -5,27 +5,27 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (int8Oid)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "paramtype" do
     it "reports inferred parameter types" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_paramtype" "select $1 :: int4, $2 :: text" Nothing
-        described <- describePrepared connection "conformance_paramtype"
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_paramtype" "select $1 :: int4, $2 :: text" Nothing
+        described <- connection.describePrepared "conformance_paramtype"
         for described \r -> do
-          n <- nparams r
-          traverse (paramtype r) [0 .. n - 1]
+          n <- r.nparams
+          traverse r.paramtype [0 .. n - 1]
 
     it "reports explicitly requested parameter types" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_paramtype_typed" "select $1" (Just [int8Oid])
-        described <- describePrepared connection "conformance_paramtype_typed"
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_paramtype_typed" "select $1" (Just [int8Oid])
+        described <- connection.describePrepared "conformance_paramtype_typed"
         for described \r -> do
-          n <- nparams r
-          traverse (paramtype r) [0 .. n - 1]
+          n <- r.nparams
+          traverse r.paramtype [0 .. n - 1]
diff --git a/src/library/Pqi/Conformance/Operation/Pass.hs b/src/library/Pqi/Conformance/Operation/Pass.hs
--- a/src/library/Pqi/Conformance/Operation/Pass.hs
+++ b/src/library/Pqi/Conformance/Operation/Pass.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "pass" do
     it "reports the password from the conninfo" \conninfo ->
-      differential proxy conninfo pass
+      differential adapter conninfo (.pass)
diff --git a/src/library/Pqi/Conformance/Operation/PipelineStatus.hs b/src/library/Pqi/Conformance/Operation/PipelineStatus.hs
--- a/src/library/Pqi/Conformance/Operation/PipelineStatus.hs
+++ b/src/library/Pqi/Conformance/Operation/PipelineStatus.hs
@@ -6,28 +6,28 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (takeCommandResults, takeResult)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "pipelineStatus" do
     it "reports off, on, aborted, and recovery" \conninfo ->
-      differential proxy conninfo \connection -> do
-        off <- pipelineStatus connection
-        _ <- enterPipelineMode connection
-        on <- pipelineStatus connection
-        _ <- traverse (\sql -> sendQueryParams connection sql [] Lq.Text) ["select 1", "select 1 / 0", "select 3"]
-        _ <- pipelineSync connection
+      differential adapter conninfo \connection -> do
+        off <- connection.pipelineStatus
+        _ <- connection.enterPipelineMode
+        on <- connection.pipelineStatus
+        _ <- traverse (\sql -> connection.sendQueryParams sql [] Lq.Text) ["select 1", "select 1 / 0", "select 3"]
+        _ <- connection.pipelineSync
         _ <- takeCommandResults connection
         _ <- takeCommandResults connection
-        aborted <- pipelineStatus connection
+        aborted <- connection.pipelineStatus
         _ <- takeCommandResults connection
         _ <- takeResult connection
-        recovered <- pipelineStatus connection
-        _ <- exitPipelineMode connection
+        recovered <- connection.pipelineStatus
+        _ <- connection.exitPipelineMode
         pure (off, on, aborted, recovered)
diff --git a/src/library/Pqi/Conformance/Operation/PipelineSync.hs b/src/library/Pqi/Conformance/Operation/PipelineSync.hs
--- a/src/library/Pqi/Conformance/Operation/PipelineSync.hs
+++ b/src/library/Pqi/Conformance/Operation/PipelineSync.hs
@@ -5,52 +5,54 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
+import qualified Pqi.Conformance.Operation.PipelineSync.Parity as Parity
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (takeCommandResults, takeResult)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "pipelineSync" do
+    Parity.spec adapter
     it "collects pipelined queries per sync" \conninfo ->
-      differential proxy conninfo \connection -> do
-        entered <- enterPipelineMode connection
+      differential adapter conninfo \connection -> do
+        entered <- connection.enterPipelineMode
         sent <-
           traverse
-            (\sql -> sendQueryParams connection sql [] Lq.Text)
+            (\sql -> connection.sendQueryParams sql [] Lq.Text)
             ["select 1 :: int4", "select 'two' :: text", "select 3 :: int4, 'three' :: text"]
-        synced <- pipelineSync connection
+        synced <- connection.pipelineSync
         first <- takeCommandResults connection
         second <- takeCommandResults connection
         third <- takeCommandResults connection
         syncResult <- takeResult connection
         idle <- takeResult connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure (entered, sent, synced, first, second, third, syncResult, idle, exited)
 
     it "aborts the rest of the pipeline after an error" \conninfo ->
-      differential proxy conninfo \connection -> do
-        entered <- enterPipelineMode connection
+      differential adapter conninfo \connection -> do
+        entered <- connection.enterPipelineMode
         sent <-
           traverse
-            (\sql -> sendQueryParams connection sql [] Lq.Text)
+            (\sql -> connection.sendQueryParams sql [] Lq.Text)
             ["select 1", "select 1 / 0", "select 3"]
-        synced <- pipelineSync connection
+        synced <- connection.pipelineSync
         first <- takeCommandResults connection
         failed <- takeCommandResults connection
         aborted <- takeCommandResults connection
         syncResult <- takeResult connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure (entered, sent, synced, first, failed, aborted, syncResult, exited)
 
     it "returns a sync result when called without prior commands" \conninfo ->
-      differential proxy conninfo \connection -> do
-        entered <- enterPipelineMode connection
-        synced <- pipelineSync connection
+      differential adapter conninfo \connection -> do
+        entered <- connection.enterPipelineMode
+        synced <- connection.pipelineSync
         syncResult <- takeResult connection
         trailing <- takeResult connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure (entered, synced, syncResult, trailing, exited)
diff --git a/src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs b/src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs
new file mode 100644
--- /dev/null
+++ b/src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs
@@ -0,0 +1,52 @@
+-- | Reproduces the @hasql@ pipeline parity benchmark scenarios at the @pqi@
+-- level: the same sequence of queries is run both sequentially and inside a
+-- pipeline, and the two ways of executing them must produce identical
+-- observations.
+--
+-- This is a scenario test for 'Pqi.pipelineSync': it marks the sync point that
+-- lets the pipelined batch complete.
+module Pqi.Conformance.Operation.PipelineSync.Parity
+  ( spec,
+  )
+where
+
+import qualified Pqi
+import qualified Pqi as Lq
+import Pqi.Conformance.Harness
+import Pqi.Conformance.Observation
+import Pqi.Conformance.Prelude
+import Pqi.Conformance.Scenario (observed, takeCommandResults, takeResult)
+import Test.Hspec
+
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
+  describe "parity" do
+    it "manySmallResults matches sequential execution" \conninfo ->
+      differential adapter conninfo \connection -> do
+        let query = "SELECT 1, 2"
+        sequential <- replicateM 100 (observed query [] Lq.Text connection)
+        entered <- connection.enterPipelineMode
+        sent <- replicateM 100 (connection.sendQueryParams query [] Lq.Text)
+        synced <- connection.pipelineSync
+        pipeline <- replicateM 100 (takeCommandResults connection)
+        syncResult <- takeResult connection
+        trailing <- takeResult connection
+        exited <- connection.exitPipelineMode
+        let pipelineResults = map fst pipeline
+        sequential `shouldBe` pipelineResults
+        pure (entered, sent, synced, pipeline, syncResult, trailing, exited, sequential)
+
+    it "manyLargeResults matches sequential execution" \conninfo ->
+      differential adapter conninfo \connection -> do
+        let query = "SELECT generate_series(0,1000) as a, generate_series(1000,2000) as b"
+        sequential <- replicateM 100 (observed query [] Lq.Text connection)
+        entered <- connection.enterPipelineMode
+        sent <- replicateM 100 (connection.sendQueryParams query [] Lq.Text)
+        synced <- connection.pipelineSync
+        pipeline <- replicateM 100 (takeCommandResults connection)
+        syncResult <- takeResult connection
+        trailing <- takeResult connection
+        exited <- connection.exitPipelineMode
+        let pipelineResults = map fst pipeline
+        sequential `shouldBe` pipelineResults
+        pure (entered, sent, synced, pipeline, syncResult, trailing, exited, sequential)
diff --git a/src/library/Pqi/Conformance/Operation/Port.hs b/src/library/Pqi/Conformance/Operation/Port.hs
--- a/src/library/Pqi/Conformance/Operation/Port.hs
+++ b/src/library/Pqi/Conformance/Operation/Port.hs
@@ -4,13 +4,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "port" do
     it "reports the port from the conninfo" \conninfo ->
-      differential proxy conninfo port
+      differential adapter conninfo (.port)
diff --git a/src/library/Pqi/Conformance/Operation/Prepare.hs b/src/library/Pqi/Conformance/Operation/Prepare.hs
--- a/src/library/Pqi/Conformance/Operation/Prepare.hs
+++ b/src/library/Pqi/Conformance/Operation/Prepare.hs
@@ -5,24 +5,24 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "prepare" do
     it "reports its own result" \conninfo ->
-      differential proxy conninfo \connection ->
-        prepare connection "conformance_prep_result" "select $1 :: int4" Nothing
+      differential adapter conninfo \connection ->
+        connection.prepare "conformance_prep_result" "select $1 :: int4" Nothing
           >>= traverse observeResult
 
     it "rejects a duplicate statement name" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         first <-
-          prepare connection "conformance_dup" "select 1" Nothing >>= traverse observeResult
+          connection.prepare "conformance_dup" "select 1" Nothing >>= traverse observeResult
         second <-
-          prepare connection "conformance_dup" "select 2" Nothing >>= traverse observeResult
+          connection.prepare "conformance_dup" "select 2" Nothing >>= traverse observeResult
         pure (first, second)
diff --git a/src/library/Pqi/Conformance/Operation/ProtocolVersion.hs b/src/library/Pqi/Conformance/Operation/ProtocolVersion.hs
--- a/src/library/Pqi/Conformance/Operation/ProtocolVersion.hs
+++ b/src/library/Pqi/Conformance/Operation/ProtocolVersion.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "protocolVersion" do
     it "reports the protocol version" \conninfo ->
-      differential proxy conninfo protocolVersion
+      differential adapter conninfo (.protocolVersion)
diff --git a/src/library/Pqi/Conformance/Operation/PutCopyData.hs b/src/library/Pqi/Conformance/Operation/PutCopyData.hs
--- a/src/library/Pqi/Conformance/Operation/PutCopyData.hs
+++ b/src/library/Pqi/Conformance/Operation/PutCopyData.hs
@@ -5,32 +5,32 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults, execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "putCopyData" do
     it "streams rows into a COPY FROM STDIN" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_copy (id int4, label text)"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_copy (id int4, label text)"
         started <- execScenario "copy conformance_copy from stdin" connection
-        firstRow <- putCopyData connection "1\thello\n"
-        secondRow <- putCopyData connection "2\tworld\n"
-        ended <- putCopyEnd connection Nothing
+        firstRow <- connection.putCopyData "1\thello\n"
+        secondRow <- connection.putCopyData "2\tworld\n"
+        ended <- connection.putCopyEnd Nothing
         outcome <- drainResults connection
         check <-
           execScenario "select count(*), min(label), max(label) from conformance_copy" connection
         pure (started, firstRow, secondRow, ended, outcome, check)
 
     it "feeds malformed data that the server rejects" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_copy_bad (id int4)"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_copy_bad (id int4)"
         started <- execScenario "copy conformance_copy_bad from stdin" connection
-        row <- putCopyData connection "not-a-number\n"
-        ended <- putCopyEnd connection Nothing
+        row <- connection.putCopyData "not-a-number\n"
+        ended <- connection.putCopyEnd Nothing
         outcome <- drainResults connection
         pure (started, row, ended, outcome)
diff --git a/src/library/Pqi/Conformance/Operation/PutCopyEnd.hs b/src/library/Pqi/Conformance/Operation/PutCopyEnd.hs
--- a/src/library/Pqi/Conformance/Operation/PutCopyEnd.hs
+++ b/src/library/Pqi/Conformance/Operation/PutCopyEnd.hs
@@ -5,31 +5,31 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults, execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "putCopyEnd" do
     it "commits the copied rows when ended without an error" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_copy_end (id int4)"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_copy_end (id int4)"
         started <- execScenario "copy conformance_copy_end from stdin" connection
-        row <- putCopyData connection "1\n"
-        ended <- putCopyEnd connection Nothing
+        row <- connection.putCopyData "1\n"
+        ended <- connection.putCopyEnd Nothing
         outcome <- drainResults connection
         check <- execScenario "select count(*) from conformance_copy_end" connection
         pure (started, row, ended, outcome, check)
 
     it "aborts the copy when ended with an error" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "create temporary table conformance_copy_abort (id int4)"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "create temporary table conformance_copy_abort (id int4)"
         started <- execScenario "copy conformance_copy_abort from stdin" connection
-        row <- putCopyData connection "1\n"
-        ended <- putCopyEnd connection (Just "conformance abort")
+        row <- connection.putCopyData "1\n"
+        ended <- connection.putCopyEnd (Just "conformance abort")
         outcome <- drainResults connection
         check <- execScenario "select count(*) from conformance_copy_abort" connection
         pure (started, row, ended, outcome, check)
diff --git a/src/library/Pqi/Conformance/Operation/Reset.hs b/src/library/Pqi/Conformance/Operation/Reset.hs
--- a/src/library/Pqi/Conformance/Operation/Reset.hs
+++ b/src/library/Pqi/Conformance/Operation/Reset.hs
@@ -5,22 +5,22 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "reset" do
     it "restores a fresh session" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- prepare connection "conformance_reset" "select 1" Nothing
-        _ <- exec connection "begin"
-        inTransaction <- transactionStatus connection
-        reset connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.prepare "conformance_reset" "select 1" Nothing
+        _ <- connection.exec "begin"
+        inTransaction <- connection.transactionStatus
+        connection.reset
         afterReset <- observeConnection connection
         -- The prepared statement must be gone in the fresh session.
-        describeAfter <- describePrepared connection "conformance_reset" >>= traverse observeResult
+        describeAfter <- connection.describePrepared "conformance_reset" >>= traverse observeResult
         pure (inTransaction, afterReset, describeAfter)
diff --git a/src/library/Pqi/Conformance/Operation/ResetPoll.hs b/src/library/Pqi/Conformance/Operation/ResetPoll.hs
--- a/src/library/Pqi/Conformance/Operation/ResetPoll.hs
+++ b/src/library/Pqi/Conformance/Operation/ResetPoll.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (pollUntilDone)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "resetPoll" do
     it "reaches a terminal polling status and a ready connection" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "begin"
-        started <- resetStart connection
-        terminal <- pollUntilDone (resetPoll connection)
-        afterStatus <- transactionStatus connection
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "begin"
+        started <- connection.resetStart
+        terminal <- pollUntilDone connection.resetPoll
+        afterStatus <- connection.transactionStatus
         pure (started, terminal, afterStatus)
diff --git a/src/library/Pqi/Conformance/Operation/ResetStart.hs b/src/library/Pqi/Conformance/Operation/ResetStart.hs
--- a/src/library/Pqi/Conformance/Operation/ResetStart.hs
+++ b/src/library/Pqi/Conformance/Operation/ResetStart.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (pollUntilDone)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "resetStart" do
     it "begins an asynchronous reset that polls to a fresh session" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "begin"
-        started <- resetStart connection
-        polled <- pollUntilDone (resetPoll connection)
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "begin"
+        started <- connection.resetStart
+        polled <- pollUntilDone connection.resetPoll
         afterReset <- observeConnection connection
         pure (started, polled, afterReset)
diff --git a/src/library/Pqi/Conformance/Operation/ResultErrorField.hs b/src/library/Pqi/Conformance/Operation/ResultErrorField.hs
--- a/src/library/Pqi/Conformance/Operation/ResultErrorField.hs
+++ b/src/library/Pqi/Conformance/Operation/ResultErrorField.hs
@@ -10,17 +10,17 @@
   )
 where
 
-import Pqi (IsConnection)
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execAllScenario, execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "resultErrorField" do
     let forCase title sql =
-          it title \conninfo -> differential proxy conninfo (execScenario sql)
+          it title \conninfo -> differential adapter conninfo (execScenario sql)
     forCase "syntax error" "selct 1"
     forCase "undefined table" "select * from pqi_no_such_table"
     forCase "undefined column" "select no_such_column from (select 1) as t"
@@ -33,7 +33,7 @@
     forCase "value too long" "select 'abc' :: varchar(2)"
 
     it "constraint violations" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ execAllScenario
           [ "create temporary table conformance_errors (id int4 primary key, label text not null)",
             "insert into conformance_errors values (1, 'a')",
@@ -42,5 +42,5 @@
           ]
 
     it "a failed transaction block rejects further commands" \conninfo ->
-      differential proxy conninfo
+      differential adapter conninfo
         $ execAllScenario ["begin", "select 1 / 0", "select 1", "rollback", "select 1"]
diff --git a/src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs b/src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs
--- a/src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs
+++ b/src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs
@@ -12,20 +12,20 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "resultErrorMessage" do
     it "matches the full formatted libpq error string on failure and is empty on success" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         failed <-
-          exec connection "do $$ begin raise exception 'conformance error'; end $$"
-            >>= traverse resultErrorMessage
+          connection.exec "do $$ begin raise exception 'conformance error'; end $$"
+            >>= traverse (.resultErrorMessage)
         succeeded <-
-          exec connection "select 1"
-            >>= traverse resultErrorMessage
+          connection.exec "select 1"
+            >>= traverse (.resultErrorMessage)
         pure (failed, succeeded)
diff --git a/src/library/Pqi/Conformance/Operation/ResultStatus.hs b/src/library/Pqi/Conformance/Operation/ResultStatus.hs
--- a/src/library/Pqi/Conformance/Operation/ResultStatus.hs
+++ b/src/library/Pqi/Conformance/Operation/ResultStatus.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "resultStatus" do
     it "reports the status for each kind of result" \conninfo ->
-      differential proxy conninfo \connection -> do
-        let statusOf sql = exec connection sql >>= traverse resultStatus
+      differential adapter conninfo \connection -> do
+        let statusOf sql = connection.exec sql >>= traverse (.resultStatus)
         tuples <- statusOf "select 1"
         command <- statusOf "create temporary table conformance_status (id int4)"
         empty <- statusOf ""
diff --git a/src/library/Pqi/Conformance/Operation/SendDescribePortal.hs b/src/library/Pqi/Conformance/Operation/SendDescribePortal.hs
--- a/src/library/Pqi/Conformance/Operation/SendDescribePortal.hs
+++ b/src/library/Pqi/Conformance/Operation/SendDescribePortal.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendDescribePortal" do
     it "describes a declared cursor asynchronously" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- exec connection "begin"
-        _ <- exec connection "declare conformance_async_cursor cursor for select 1 :: int4 as n"
-        sent <- sendDescribePortal connection "conformance_async_cursor"
+      differential adapter conninfo \connection -> do
+        _ <- connection.exec "begin"
+        _ <- connection.exec "declare conformance_async_cursor cursor for select 1 :: int4 as n"
+        sent <- connection.sendDescribePortal "conformance_async_cursor"
         results <- drainResults connection
         pure (sent, results)
diff --git a/src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs b/src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs
--- a/src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs
+++ b/src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendDescribePrepared" do
     it "describes a prepared statement asynchronously" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- sendPrepare connection "conformance_send_desc" "select $1 :: int4 * 2" Nothing
+      differential adapter conninfo \connection -> do
+        _ <- connection.sendPrepare "conformance_send_desc" "select $1 :: int4 * 2" Nothing
         _ <- drainResults connection
-        sent <- sendDescribePrepared connection "conformance_send_desc"
+        sent <- connection.sendDescribePrepared "conformance_send_desc"
         results <- drainResults connection
         pure (sent, results)
diff --git a/src/library/Pqi/Conformance/Operation/SendFlushRequest.hs b/src/library/Pqi/Conformance/Operation/SendFlushRequest.hs
--- a/src/library/Pqi/Conformance/Operation/SendFlushRequest.hs
+++ b/src/library/Pqi/Conformance/Operation/SendFlushRequest.hs
@@ -5,23 +5,23 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (takeCommandResults, takeResult)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendFlushRequest" do
     it "delivers results without a sync" \conninfo ->
-      differential proxy conninfo \connection -> do
-        entered <- enterPipelineMode connection
-        sent <- sendQueryParams connection "select 42" [] Lq.Text
-        flushRequested <- sendFlushRequest connection
+      differential adapter conninfo \connection -> do
+        entered <- connection.enterPipelineMode
+        sent <- connection.sendQueryParams "select 42" [] Lq.Text
+        flushRequested <- connection.sendFlushRequest
         results <- takeCommandResults connection
-        synced <- pipelineSync connection
+        synced <- connection.pipelineSync
         syncResult <- takeResult connection
-        exited <- exitPipelineMode connection
+        exited <- connection.exitPipelineMode
         pure (entered, sent, flushRequested, results, synced, syncResult, exited)
diff --git a/src/library/Pqi/Conformance/Operation/SendPrepare.hs b/src/library/Pqi/Conformance/Operation/SendPrepare.hs
--- a/src/library/Pqi/Conformance/Operation/SendPrepare.hs
+++ b/src/library/Pqi/Conformance/Operation/SendPrepare.hs
@@ -5,17 +5,17 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendPrepare" do
     it "prepares a statement asynchronously" \conninfo ->
-      differential proxy conninfo \connection -> do
-        sent <- sendPrepare connection "conformance_send_prepare" "select $1 :: int4 * 2" Nothing
+      differential adapter conninfo \connection -> do
+        sent <- connection.sendPrepare "conformance_send_prepare" "select $1 :: int4 * 2" Nothing
         results <- drainResults connection
         pure (sent, results)
diff --git a/src/library/Pqi/Conformance/Operation/SendQuery.hs b/src/library/Pqi/Conformance/Operation/SendQuery.hs
--- a/src/library/Pqi/Conformance/Operation/SendQuery.hs
+++ b/src/library/Pqi/Conformance/Operation/SendQuery.hs
@@ -6,18 +6,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendQuery" do
     let sendAndDrain sql conninfo =
-          differential proxy conninfo \connection -> do
-            sent <- sendQuery connection sql
+          differential adapter conninfo \connection -> do
+            sent <- connection.sendQuery sql
             results <- drainResults connection
             pure (sent, results)
     it "sends a query and collects its result"
diff --git a/src/library/Pqi/Conformance/Operation/SendQueryParams.hs b/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
--- a/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
+++ b/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
@@ -5,21 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults, int4Oid)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendQueryParams" do
     it "sends a parameterized query and collects its result" \conninfo ->
-      differential proxy conninfo \connection -> do
+      differential adapter conninfo \connection -> do
         sent <-
-          sendQueryParams
-            connection
+          connection.sendQueryParams
             "select $1 :: int4 + $2 :: int4, $3 :: text"
             [Just (int4Oid, "40", Lq.Text), Just (int4Oid, "2", Lq.Text), Nothing]
             Lq.Text
diff --git a/src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs b/src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs
--- a/src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs
+++ b/src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs
@@ -5,20 +5,20 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "sendQueryPrepared" do
     it "executes a prepared statement asynchronously" \conninfo ->
-      differential proxy conninfo \connection -> do
-        _ <- sendPrepare connection "conformance_send_exec" "select $1 :: int4 * 2" Nothing
+      differential adapter conninfo \connection -> do
+        _ <- connection.sendPrepare "conformance_send_exec" "select $1 :: int4 * 2" Nothing
         _ <- drainResults connection
-        sent <- sendQueryPrepared connection "conformance_send_exec" [Just ("21", Lq.Text)] Lq.Text
+        sent <- connection.sendQueryPrepared "conformance_send_exec" [Just ("21", Lq.Text)] Lq.Text
         results <- drainResults connection
         pure (sent, results)
diff --git a/src/library/Pqi/Conformance/Operation/ServerVersion.hs b/src/library/Pqi/Conformance/Operation/ServerVersion.hs
--- a/src/library/Pqi/Conformance/Operation/ServerVersion.hs
+++ b/src/library/Pqi/Conformance/Operation/ServerVersion.hs
@@ -5,13 +5,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "serverVersion" do
     it "reports the server version as an integer" \conninfo ->
-      differential proxy conninfo serverVersion
+      differential adapter conninfo (.serverVersion)
diff --git a/src/library/Pqi/Conformance/Operation/SetClientEncoding.hs b/src/library/Pqi/Conformance/Operation/SetClientEncoding.hs
--- a/src/library/Pqi/Conformance/Operation/SetClientEncoding.hs
+++ b/src/library/Pqi/Conformance/Operation/SetClientEncoding.hs
@@ -5,19 +5,19 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "setClientEncoding" do
     it "rejects an unknown encoding and leaves the session usable" \conninfo ->
-      differential proxy conninfo \connection -> do
-        rejected <- setClientEncoding connection "BOGUS_ENCODING"
-        unchanged <- clientEncoding connection
-        stillIdle <- transactionStatus connection
+      differential adapter conninfo \connection -> do
+        rejected <- connection.setClientEncoding "BOGUS_ENCODING"
+        unchanged <- connection.clientEncoding
+        stillIdle <- connection.transactionStatus
         stillWorks <- execScenario "select 1" connection
         pure (rejected, unchanged, stillIdle, stillWorks)
diff --git a/src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs b/src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs
--- a/src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs
+++ b/src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import qualified Pqi as Lq
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "setErrorVerbosity" do
     it "returns the previous setting" \conninfo ->
-      differential proxy conninfo \connection -> do
-        beforeTerse <- setErrorVerbosity connection Lq.ErrorsTerse
-        beforeVerbose <- setErrorVerbosity connection Lq.ErrorsVerbose
-        beforeDefault <- setErrorVerbosity connection Lq.ErrorsDefault
+      differential adapter conninfo \connection -> do
+        beforeTerse <- connection.setErrorVerbosity Lq.ErrorsTerse
+        beforeVerbose <- connection.setErrorVerbosity Lq.ErrorsVerbose
+        beforeDefault <- connection.setErrorVerbosity Lq.ErrorsDefault
         pure (beforeTerse, beforeVerbose, beforeDefault)
diff --git a/src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs b/src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs
--- a/src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs
+++ b/src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs
@@ -5,25 +5,25 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (drainResults)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "setSingleRowMode" do
     it "splits a multi-row result into single-row results" \conninfo ->
-      differential proxy conninfo \connection -> do
-        sent <- sendQuery connection "select i from generate_series (1, 3) as i"
-        singleRow <- setSingleRowMode connection
+      differential adapter conninfo \connection -> do
+        sent <- connection.sendQuery "select i from generate_series (1, 3) as i"
+        singleRow <- connection.setSingleRowMode
         results <- drainResults connection
         pure (sent, singleRow, results)
 
     it "handles an empty result" \conninfo ->
-      differential proxy conninfo \connection -> do
-        sent <- sendQuery connection "select 1 where false"
-        singleRow <- setSingleRowMode connection
+      differential adapter conninfo \connection -> do
+        sent <- connection.sendQuery "select 1 where false"
+        singleRow <- connection.setSingleRowMode
         results <- drainResults connection
         pure (sent, singleRow, results)
diff --git a/src/library/Pqi/Conformance/Operation/Setnonblocking.hs b/src/library/Pqi/Conformance/Operation/Setnonblocking.hs
--- a/src/library/Pqi/Conformance/Operation/Setnonblocking.hs
+++ b/src/library/Pqi/Conformance/Operation/Setnonblocking.hs
@@ -5,18 +5,18 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "setnonblocking" do
     it "turns the non-blocking flag on and off" \conninfo ->
-      differential proxy conninfo \connection -> do
-        setOn <- setnonblocking connection True
-        nowOn <- isnonblocking connection
-        setOff <- setnonblocking connection False
-        nowOff <- isnonblocking connection
+      differential adapter conninfo \connection -> do
+        setOn <- connection.setnonblocking True
+        nowOn <- connection.isnonblocking
+        setOff <- connection.setnonblocking False
+        nowOff <- connection.isnonblocking
         pure (setOn, nowOn, setOff, nowOff)
diff --git a/src/library/Pqi/Conformance/Operation/Socket.hs b/src/library/Pqi/Conformance/Operation/Socket.hs
--- a/src/library/Pqi/Conformance/Operation/Socket.hs
+++ b/src/library/Pqi/Conformance/Operation/Socket.hs
@@ -7,14 +7,14 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "socket" do
     it "is present on an open connection" \conninfo ->
-      differential proxy conninfo \connection ->
-        isJust <$> socket connection
+      differential adapter conninfo \connection ->
+        isJust <$> connection.socket
diff --git a/src/library/Pqi/Conformance/Operation/Status.hs b/src/library/Pqi/Conformance/Operation/Status.hs
--- a/src/library/Pqi/Conformance/Operation/Status.hs
+++ b/src/library/Pqi/Conformance/Operation/Status.hs
@@ -4,13 +4,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "status" do
     it "reports a ready connection as OK" \conninfo ->
-      differential proxy conninfo status
+      differential adapter conninfo (.status)
diff --git a/src/library/Pqi/Conformance/Operation/TransactionStatus.hs b/src/library/Pqi/Conformance/Operation/TransactionStatus.hs
--- a/src/library/Pqi/Conformance/Operation/TransactionStatus.hs
+++ b/src/library/Pqi/Conformance/Operation/TransactionStatus.hs
@@ -5,21 +5,21 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "transactionStatus" do
     it "tracks status through a transaction" \conninfo ->
-      differential proxy conninfo \connection -> do
-        idle <- transactionStatus connection
-        _ <- exec connection "begin"
-        inTransaction <- transactionStatus connection
-        _ <- exec connection "select 1 / 0"
-        inError <- transactionStatus connection
-        _ <- exec connection "rollback"
-        afterRollback <- transactionStatus connection
+      differential adapter conninfo \connection -> do
+        idle <- connection.transactionStatus
+        _ <- connection.exec "begin"
+        inTransaction <- connection.transactionStatus
+        _ <- connection.exec "select 1 / 0"
+        inError <- connection.transactionStatus
+        _ <- connection.exec "rollback"
+        afterRollback <- connection.transactionStatus
         pure (idle, inTransaction, inError, afterRollback)
diff --git a/src/library/Pqi/Conformance/Operation/UnescapeBytea.hs b/src/library/Pqi/Conformance/Operation/UnescapeBytea.hs
new file mode 100644
--- /dev/null
+++ b/src/library/Pqi/Conformance/Operation/UnescapeBytea.hs
@@ -0,0 +1,49 @@
+-- | Coverage for 'Pqi.unescapeBytea': decoding the textual representation of
+-- a @bytea@ value, in both the hex and legacy escape formats, including
+-- malformed input.
+module Pqi.Conformance.Operation.UnescapeBytea
+  ( spec,
+  )
+where
+
+import qualified Pqi
+import Pqi.Conformance.Prelude
+import qualified Pqi.Conformance.Reference as Reference
+import Test.Hspec
+
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
+  describe "unescapeBytea" do
+    for_ cases \input ->
+      it (show input) \_ -> do
+        candidate <- adapter.unescapeBytea input
+        reference <- Reference.adapter.unescapeBytea input
+        candidate `shouldBe` reference
+  where
+    cases =
+      [ "",
+        "\\x",
+        "\\x00",
+        "\\x00ff",
+        "\\x48656c6c6f",
+        "\\X48656C6C6F",
+        "\\xAbCd",
+        "Hello, world",
+        "h\233llo bytes",
+        "\\\\",
+        "\\001\\002\\003",
+        "a\\010b",
+        "\\x4",
+        "\\x4g",
+        "\\xzz",
+        "\\x61 62",
+        "\\377",
+        "\\400",
+        "\\000",
+        "\\1",
+        "\\18",
+        "\\8",
+        "a\\b",
+        "trailing\\",
+        "mixed\\134text"
+      ]
diff --git a/src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs b/src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs
--- a/src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs
+++ b/src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs
@@ -10,17 +10,17 @@
   )
 where
 
-import Pqi (IsConnection (..), IsResult (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Pqi.Conformance.Scenario (execScenario)
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "unsafeFreeResult" do
     it "leaves the connection usable" \conninfo ->
-      differential proxy conninfo \connection -> do
-        result <- exec connection "select 1"
-        traverse_ unsafeFreeResult result
+      differential adapter conninfo \connection -> do
+        result <- connection.exec "select 1"
+        traverse_ (.unsafeFreeResult) result
         execScenario "select 2" connection
diff --git a/src/library/Pqi/Conformance/Operation/User.hs b/src/library/Pqi/Conformance/Operation/User.hs
--- a/src/library/Pqi/Conformance/Operation/User.hs
+++ b/src/library/Pqi/Conformance/Operation/User.hs
@@ -4,13 +4,13 @@
   )
 where
 
-import Pqi (IsConnection (..))
+import qualified Pqi
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Prelude
 import Test.Hspec
 
-spec :: (IsConnection c) => Proxy c -> SpecWith ByteString
-spec proxy =
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
   describe "user" do
     it "reports the user name from the conninfo" \conninfo ->
-      differential proxy conninfo user
+      differential adapter conninfo (.user)
diff --git a/src/library/Pqi/Conformance/Prelude.hs b/src/library/Pqi/Conformance/Prelude.hs
--- a/src/library/Pqi/Conformance/Prelude.hs
+++ b/src/library/Pqi/Conformance/Prelude.hs
@@ -11,7 +11,6 @@
 import Data.Foldable as Exports (for_, traverse_)
 import Data.Int as Exports
 import Data.Maybe as Exports
-import Data.Proxy as Exports (Proxy (..))
 import Data.Text as Exports (Text)
 import Data.Traversable as Exports (for)
 import Data.Word as Exports
diff --git a/src/library/Pqi/Conformance/Reference.hs b/src/library/Pqi/Conformance/Reference.hs
--- a/src/library/Pqi/Conformance/Reference.hs
+++ b/src/library/Pqi/Conformance/Reference.hs
@@ -1,159 +1,151 @@
--- | The reference connection: a direct @postgresql-libpq@ wrapper used as the
+-- | The reference adapter: a direct @postgresql-libpq@ wrapper used as the
 -- ground truth in differential tests. It is intentionally independent of the
--- @pqi-ffi@ adapter so that adapter test suites can depend on
--- @pqi-conformance@ without a circular dependency through @pqi-ffi@.
+-- @pqi-ffi@ package (despite producing byte-identical output) so that
+-- adapter test suites can depend on @pqi-conformance@ without a circular
+-- dependency through @pqi-ffi@.
 module Pqi.Conformance.Reference
-  ( Reference (..),
-    RefResult (..),
-    RefCancel (..),
+  ( adapter,
   )
 where
 
 import qualified Database.PostgreSQL.LibPQ as LibPQ
-import Pqi
-  ( IsCancel (..),
-    IsConnection (..),
-    IsResult (..),
-  )
 import qualified Pqi
 import Pqi.Conformance.Prelude
 
--- | A direct wrapper over a @postgresql-libpq@ connection, used as the
--- differential-testing reference.
-newtype Reference = Reference LibPQ.Connection
-
--- | A result backed by a C @PGresult@.
-newtype RefResult = RefResult LibPQ.Result
-
--- | A cancellation handle backed by a C @PGcancel@.
-newtype RefCancel = RefCancel LibPQ.Cancel
-
-instance IsResult RefResult where
-  resultStatus (RefResult r) = fromExecStatus <$> LibPQ.resultStatus r
-  resultErrorMessage (RefResult r) = LibPQ.resultErrorMessage r
-  resultErrorField (RefResult r) field = LibPQ.resultErrorField r (toFieldCode field)
-  unsafeFreeResult (RefResult r) = LibPQ.unsafeFreeResult r
-  ntuples (RefResult r) = fromRow <$> LibPQ.ntuples r
-  nfields (RefResult r) = fromColumn <$> LibPQ.nfields r
-  fname (RefResult r) column = LibPQ.fname r (toColumn column)
-  fnumber (RefResult r) name = fmap fromColumn <$> LibPQ.fnumber r name
-  ftable (RefResult r) column = fromOid <$> LibPQ.ftable r (toColumn column)
-  ftablecol (RefResult r) column = fromColumn <$> LibPQ.ftablecol r (toColumn column)
-  fformat (RefResult r) column = fromFormat <$> LibPQ.fformat r (toColumn column)
-  ftype (RefResult r) column = fromOid <$> LibPQ.ftype r (toColumn column)
-  fmod (RefResult r) column = LibPQ.fmod r (toColumn column)
-  fsize (RefResult r) column = LibPQ.fsize r (toColumn column)
-  getvalue (RefResult r) row column = LibPQ.getvalue r (toRow row) (toColumn column)
-  getvalue' (RefResult r) row column = LibPQ.getvalue' r (toRow row) (toColumn column)
-  getisnull (RefResult r) row column = LibPQ.getisnull r (toRow row) (toColumn column)
-  getlength (RefResult r) row column = LibPQ.getlength r (toRow row) (toColumn column)
-  nparams (RefResult r) = fromIntegral <$> LibPQ.nparams r
-  paramtype (RefResult r) index = fromOid <$> LibPQ.paramtype r (fromIntegral index)
-  cmdStatus (RefResult r) = LibPQ.cmdStatus r
-  cmdTuples (RefResult r) = LibPQ.cmdTuples r
-
-instance IsCancel RefCancel where
-  cancel (RefCancel handle) = LibPQ.cancel handle
-
-instance IsConnection Reference where
-  type ResultOf Reference = RefResult
-  type CancelOf Reference = RefCancel
-
-  connectdb conninfo = Reference <$> LibPQ.connectdb conninfo
-  connectStart conninfo = Reference <$> LibPQ.connectStart conninfo
-  connectPoll (Reference c) = fromPollingStatus <$> LibPQ.connectPoll c
-  newNullConnection = Reference <$> LibPQ.newNullConnection
-  isNullConnection (Reference c) = LibPQ.isNullConnection c
-  finish (Reference c) = LibPQ.finish c
-  reset (Reference c) = LibPQ.reset c
-  resetStart (Reference c) = LibPQ.resetStart c
-  resetPoll (Reference c) = fromPollingStatus <$> LibPQ.resetPoll c
-  db (Reference c) = LibPQ.db c
-  user (Reference c) = LibPQ.user c
-  pass (Reference c) = LibPQ.pass c
-  host (Reference c) = LibPQ.host c
-  port (Reference c) = LibPQ.port c
-  options (Reference c) = LibPQ.options c
-  status (Reference c) = fromConnStatus <$> LibPQ.status c
-  transactionStatus (Reference c) = fromTransactionStatus <$> LibPQ.transactionStatus c
-  parameterStatus (Reference c) name = LibPQ.parameterStatus c name
-  protocolVersion (Reference c) = LibPQ.protocolVersion c
-  serverVersion (Reference c) = LibPQ.serverVersion c
-  errorMessage (Reference c) = LibPQ.errorMessage c
-  socket (Reference c) = LibPQ.socket c
-  backendPID (Reference c) = fromIntegral <$> LibPQ.backendPID c
-  connectionNeedsPassword (Reference c) = LibPQ.connectionNeedsPassword c
-  connectionUsedPassword (Reference c) = LibPQ.connectionUsedPassword c
-
-  exec (Reference c) sql =
-    fmap RefResult <$> LibPQ.exec c sql
-  execParams (Reference c) sql params resultFormat =
-    fmap RefResult <$> LibPQ.execParams c sql (fmap (fmap toParam) params) (toFormat resultFormat)
-  prepare (Reference c) name sql paramTypes =
-    fmap RefResult <$> LibPQ.prepare c name sql (fmap (fmap toOid) paramTypes)
-  execPrepared (Reference c) name params resultFormat =
-    fmap RefResult <$> LibPQ.execPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat)
-  describePrepared (Reference c) name =
-    fmap RefResult <$> LibPQ.describePrepared c name
-  describePortal (Reference c) name =
-    fmap RefResult <$> LibPQ.describePortal c name
-
-  escapeStringConn (Reference c) = LibPQ.escapeStringConn c
-  escapeByteaConn (Reference c) = LibPQ.escapeByteaConn c
-  escapeIdentifier (Reference c) = LibPQ.escapeIdentifier c
-
-  sendQuery (Reference c) sql = LibPQ.sendQuery c sql
-  sendQueryParams (Reference c) sql params resultFormat =
-    LibPQ.sendQueryParams c sql (fmap (fmap toParam) params) (toFormat resultFormat)
-  sendPrepare (Reference c) name sql paramTypes =
-    LibPQ.sendPrepare c name sql (fmap (fmap toOid) paramTypes)
-  sendQueryPrepared (Reference c) name params resultFormat =
-    LibPQ.sendQueryPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat)
-  sendDescribePrepared (Reference c) name = LibPQ.sendDescribePrepared c name
-  sendDescribePortal (Reference c) name = LibPQ.sendDescribePortal c name
-  getResult (Reference c) = fmap RefResult <$> LibPQ.getResult c
-  consumeInput (Reference c) = LibPQ.consumeInput c
-  isBusy (Reference c) = LibPQ.isBusy c
-  setnonblocking (Reference c) nonBlocking = LibPQ.setnonblocking c nonBlocking
-  isnonblocking (Reference c) = LibPQ.isnonblocking c
-  setSingleRowMode (Reference c) = LibPQ.setSingleRowMode c
-  flush (Reference c) = fromFlushStatus <$> LibPQ.flush c
-
-  pipelineStatus (Reference c) = fromPipelineStatus <$> LibPQ.pipelineStatus c
-  enterPipelineMode (Reference c) = LibPQ.enterPipelineMode c
-  exitPipelineMode (Reference c) = LibPQ.exitPipelineMode c
-  pipelineSync (Reference c) = LibPQ.pipelineSync c
-  sendFlushRequest (Reference c) = LibPQ.sendFlushRequest c
-
-  getCancel (Reference c) = fmap RefCancel <$> LibPQ.getCancel c
-
-  notifies (Reference c) = fmap fromNotify <$> LibPQ.notifies c
-  disableNoticeReporting (Reference c) = LibPQ.disableNoticeReporting c
-  enableNoticeReporting (Reference c) = LibPQ.enableNoticeReporting c
-  getNotice (Reference c) = LibPQ.getNotice c
+-- | The reference adapter.
+adapter :: Pqi.Adapter
+adapter =
+  Pqi.Adapter
+    { Pqi.name = "postgresql-libpq (reference)",
+      Pqi.connectdb = \conninfo -> mkConnection <$> LibPQ.connectdb conninfo,
+      Pqi.connectStart = \conninfo -> mkConnection <$> LibPQ.connectStart conninfo,
+      Pqi.newNullConnection = mkConnection <$> LibPQ.newNullConnection,
+      Pqi.unescapeBytea = \input -> LibPQ.unescapeBytea input
+    }
 
-  putCopyData (Reference c) value = fromCopyInResult <$> LibPQ.putCopyData c value
-  putCopyEnd (Reference c) reason = fromCopyInResult <$> LibPQ.putCopyEnd c reason
-  getCopyData (Reference c) nonBlocking = fromCopyOutResult <$> LibPQ.getCopyData c nonBlocking
+-- | Build a 'Pqi.Connection' whose fields close over the given
+-- @postgresql-libpq@ connection handle.
+mkConnection :: LibPQ.Connection -> Pqi.Connection
+mkConnection c =
+  Pqi.Connection
+    { Pqi.connectPoll = fromPollingStatus <$> LibPQ.connectPoll c,
+      Pqi.isNullConnection = LibPQ.isNullConnection c,
+      Pqi.finish = LibPQ.finish c,
+      Pqi.reset = LibPQ.reset c,
+      Pqi.resetStart = LibPQ.resetStart c,
+      Pqi.resetPoll = fromPollingStatus <$> LibPQ.resetPoll c,
+      Pqi.db = LibPQ.db c,
+      Pqi.user = LibPQ.user c,
+      Pqi.pass = LibPQ.pass c,
+      Pqi.host = LibPQ.host c,
+      Pqi.port = LibPQ.port c,
+      Pqi.options = LibPQ.options c,
+      Pqi.status = fromConnStatus <$> LibPQ.status c,
+      Pqi.transactionStatus = fromTransactionStatus <$> LibPQ.transactionStatus c,
+      Pqi.parameterStatus = \name -> LibPQ.parameterStatus c name,
+      Pqi.protocolVersion = LibPQ.protocolVersion c,
+      Pqi.serverVersion = LibPQ.serverVersion c,
+      Pqi.errorMessage = LibPQ.errorMessage c,
+      Pqi.socket = LibPQ.socket c,
+      Pqi.backendPID = fromIntegral <$> LibPQ.backendPID c,
+      Pqi.connectionNeedsPassword = LibPQ.connectionNeedsPassword c,
+      Pqi.connectionUsedPassword = LibPQ.connectionUsedPassword c,
+      Pqi.exec = \sql -> fmap mkResult <$> LibPQ.exec c sql,
+      Pqi.execParams = \sql params resultFormat ->
+        fmap mkResult <$> LibPQ.execParams c sql (fmap (fmap toParam) params) (toFormat resultFormat),
+      Pqi.prepare = \name sql paramTypes ->
+        fmap mkResult <$> LibPQ.prepare c name sql (fmap (fmap toOid) paramTypes),
+      Pqi.execPrepared = \name params resultFormat ->
+        fmap mkResult <$> LibPQ.execPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat),
+      Pqi.describePrepared = \name -> fmap mkResult <$> LibPQ.describePrepared c name,
+      Pqi.describePortal = \name -> fmap mkResult <$> LibPQ.describePortal c name,
+      Pqi.escapeStringConn = \s -> LibPQ.escapeStringConn c s,
+      Pqi.escapeByteaConn = \s -> LibPQ.escapeByteaConn c s,
+      Pqi.escapeIdentifier = \s -> LibPQ.escapeIdentifier c s,
+      Pqi.sendQuery = \sql -> LibPQ.sendQuery c sql,
+      Pqi.sendQueryParams = \sql params resultFormat ->
+        LibPQ.sendQueryParams c sql (fmap (fmap toParam) params) (toFormat resultFormat),
+      Pqi.sendPrepare = \name sql paramTypes ->
+        LibPQ.sendPrepare c name sql (fmap (fmap toOid) paramTypes),
+      Pqi.sendQueryPrepared = \name params resultFormat ->
+        LibPQ.sendQueryPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat),
+      Pqi.sendDescribePrepared = \name -> LibPQ.sendDescribePrepared c name,
+      Pqi.sendDescribePortal = \name -> LibPQ.sendDescribePortal c name,
+      Pqi.getResult = fmap mkResult <$> LibPQ.getResult c,
+      Pqi.consumeInput = LibPQ.consumeInput c,
+      Pqi.isBusy = LibPQ.isBusy c,
+      Pqi.setnonblocking = \nonBlocking -> LibPQ.setnonblocking c nonBlocking,
+      Pqi.isnonblocking = LibPQ.isnonblocking c,
+      Pqi.setSingleRowMode = LibPQ.setSingleRowMode c,
+      Pqi.flush = fromFlushStatus <$> LibPQ.flush c,
+      Pqi.pipelineStatus = fromPipelineStatus <$> LibPQ.pipelineStatus c,
+      Pqi.enterPipelineMode = LibPQ.enterPipelineMode c,
+      Pqi.exitPipelineMode = LibPQ.exitPipelineMode c,
+      Pqi.pipelineSync = LibPQ.pipelineSync c,
+      Pqi.sendFlushRequest = LibPQ.sendFlushRequest c,
+      Pqi.getCancel = fmap mkCancel <$> LibPQ.getCancel c,
+      Pqi.notifies = fmap fromNotify <$> LibPQ.notifies c,
+      Pqi.disableNoticeReporting = LibPQ.disableNoticeReporting c,
+      Pqi.enableNoticeReporting = LibPQ.enableNoticeReporting c,
+      Pqi.getNotice = LibPQ.getNotice c,
+      Pqi.putCopyData = \value -> fromCopyInResult <$> LibPQ.putCopyData c value,
+      Pqi.putCopyEnd = \reason -> fromCopyInResult <$> LibPQ.putCopyEnd c reason,
+      Pqi.getCopyData = \nonBlocking -> fromCopyOutResult <$> LibPQ.getCopyData c nonBlocking,
+      Pqi.loCreat = fmap fromOid <$> LibPQ.loCreat c,
+      Pqi.loCreate = \oid -> fmap fromOid <$> LibPQ.loCreate c (toOid oid),
+      Pqi.loImport = \path -> fmap fromOid <$> LibPQ.loImport c path,
+      Pqi.loImportWithOid = \path oid -> fmap fromOid <$> LibPQ.loImportWithOid c path (toOid oid),
+      Pqi.loExport = \oid path -> LibPQ.loExport c (toOid oid) path,
+      Pqi.loOpen = \oid mode -> fmap fromLibPQLoFd <$> LibPQ.loOpen c (toOid oid) mode,
+      Pqi.loWrite = \fd value -> LibPQ.loWrite c (toLibPQLoFd fd) value,
+      Pqi.loRead = \fd len -> LibPQ.loRead c (toLibPQLoFd fd) len,
+      Pqi.loSeek = \fd mode offset -> LibPQ.loSeek c (toLibPQLoFd fd) mode offset,
+      Pqi.loTell = \fd -> LibPQ.loTell c (toLibPQLoFd fd),
+      Pqi.loTruncate = \fd len -> LibPQ.loTruncate c (toLibPQLoFd fd) len,
+      Pqi.loClose = \fd -> LibPQ.loClose c (toLibPQLoFd fd),
+      Pqi.loUnlink = \oid -> LibPQ.loUnlink c (toOid oid),
+      Pqi.clientEncoding = LibPQ.clientEncoding c,
+      Pqi.setClientEncoding = \encoding -> LibPQ.setClientEncoding c encoding,
+      Pqi.setErrorVerbosity = \verbosity ->
+        fromVerbosity <$> LibPQ.setErrorVerbosity c (toVerbosity verbosity)
+    }
 
-  loCreat (Reference c) = fmap fromOid <$> LibPQ.loCreat c
-  loCreate (Reference c) oid = fmap fromOid <$> LibPQ.loCreate c (toOid oid)
-  loImport (Reference c) path = fmap fromOid <$> LibPQ.loImport c path
-  loImportWithOid (Reference c) path oid = fmap fromOid <$> LibPQ.loImportWithOid c path (toOid oid)
-  loExport (Reference c) oid path = LibPQ.loExport c (toOid oid) path
-  loOpen (Reference c) oid mode = fmap fromLibPQLoFd <$> LibPQ.loOpen c (toOid oid) mode
-  loWrite (Reference c) fd value = LibPQ.loWrite c (toLibPQLoFd fd) value
-  loRead (Reference c) fd len = LibPQ.loRead c (toLibPQLoFd fd) len
-  loSeek (Reference c) fd mode offset = LibPQ.loSeek c (toLibPQLoFd fd) mode offset
-  loTell (Reference c) fd = LibPQ.loTell c (toLibPQLoFd fd)
-  loTruncate (Reference c) fd len = LibPQ.loTruncate c (toLibPQLoFd fd) len
-  loClose (Reference c) fd = LibPQ.loClose c (toLibPQLoFd fd)
-  loUnlink (Reference c) oid = LibPQ.loUnlink c (toOid oid)
+-- | Build a 'Pqi.Result' whose fields close over the given
+-- @postgresql-libpq@ result handle.
+mkResult :: LibPQ.Result -> Pqi.Result
+mkResult r =
+  Pqi.Result
+    { Pqi.resultStatus = fromExecStatus <$> LibPQ.resultStatus r,
+      Pqi.resultErrorMessage = LibPQ.resultErrorMessage r,
+      Pqi.resultErrorField = \field -> LibPQ.resultErrorField r (toFieldCode field),
+      Pqi.unsafeFreeResult = LibPQ.unsafeFreeResult r,
+      Pqi.ntuples = fromRow <$> LibPQ.ntuples r,
+      Pqi.nfields = fromColumn <$> LibPQ.nfields r,
+      Pqi.fname = \column -> LibPQ.fname r (toColumn column),
+      Pqi.fnumber = \name -> fmap fromColumn <$> LibPQ.fnumber r name,
+      Pqi.ftable = \column -> fromOid <$> LibPQ.ftable r (toColumn column),
+      Pqi.ftablecol = \column -> fromColumn <$> LibPQ.ftablecol r (toColumn column),
+      Pqi.fformat = \column -> fromFormat <$> LibPQ.fformat r (toColumn column),
+      Pqi.ftype = \column -> fromOid <$> LibPQ.ftype r (toColumn column),
+      Pqi.fmod = \column -> LibPQ.fmod r (toColumn column),
+      Pqi.fsize = \column -> LibPQ.fsize r (toColumn column),
+      Pqi.getvalue = \row column -> LibPQ.getvalue' r (toRow row) (toColumn column),
+      Pqi.getvalue' = \row column -> LibPQ.getvalue' r (toRow row) (toColumn column),
+      Pqi.getisnull = \row column -> LibPQ.getisnull r (toRow row) (toColumn column),
+      Pqi.getlength = \row column -> LibPQ.getlength r (toRow row) (toColumn column),
+      Pqi.nparams = fromIntegral <$> LibPQ.nparams r,
+      Pqi.paramtype = \index -> fromOid <$> LibPQ.paramtype r (fromIntegral index),
+      Pqi.cmdStatus = LibPQ.cmdStatus r,
+      Pqi.cmdTuples = LibPQ.cmdTuples r
+    }
 
-  clientEncoding (Reference c) = LibPQ.clientEncoding c
-  setClientEncoding (Reference c) encoding = LibPQ.setClientEncoding c encoding
-  setErrorVerbosity (Reference c) verbosity =
-    fromVerbosity <$> LibPQ.setErrorVerbosity c (toVerbosity verbosity)
+-- | Build a 'Pqi.Cancel' whose field closes over the given
+-- @postgresql-libpq@ cancellation handle.
+mkCancel :: LibPQ.Cancel -> Pqi.Cancel
+mkCancel handle =
+  Pqi.Cancel
+    { Pqi.cancel = LibPQ.cancel handle
+    }
 
 -- * Type conversions
 
diff --git a/src/library/Pqi/Conformance/Scenario.hs b/src/library/Pqi/Conformance/Scenario.hs
--- a/src/library/Pqi/Conformance/Scenario.hs
+++ b/src/library/Pqi/Conformance/Scenario.hs
@@ -35,55 +35,49 @@
   )
 where
 
-import Pqi
-  ( CopyOutResult (..),
-    FlushStatus (..),
-    IsConnection (..),
-    PollingStatus (..),
-  )
-import qualified Pqi as Lq
+import Pqi (CopyOutResult (..), FlushStatus (..), PollingStatus (..))
+import qualified Pqi
 import Pqi.Conformance.Observation
 import Pqi.Conformance.Prelude
 
 -- | Run 'Pqi.exec' and observe its result (if any).
-execScenario :: (IsConnection c) => ByteString -> c -> IO (Maybe ResultObservation)
-execScenario sql connection = exec connection sql >>= traverse observeResult
+execScenario :: ByteString -> Pqi.Connection -> IO (Maybe ResultObservation)
+execScenario sql connection = connection.exec sql >>= traverse observeResult
 
 -- | Run a sequence of statements with 'Pqi.exec', observing every result.
-execAllScenario :: (IsConnection c) => [ByteString] -> c -> IO [Maybe ResultObservation]
+execAllScenario :: [ByteString] -> Pqi.Connection -> IO [Maybe ResultObservation]
 execAllScenario sqls connection = traverse (`execScenario` connection) sqls
 
 -- | Run 'Pqi.execParams' and observe its result (if any).
 observed ::
-  (IsConnection c) =>
   ByteString ->
-  [Maybe (Word32, ByteString, Lq.Format)] ->
-  Lq.Format ->
-  c ->
+  [Maybe (Word32, ByteString, Pqi.Format)] ->
+  Pqi.Format ->
+  Pqi.Connection ->
   IO (Maybe ResultObservation)
 observed sql params resultFormat connection =
-  execParams connection sql params resultFormat >>= traverse observeResult
+  connection.execParams sql params resultFormat >>= traverse observeResult
 
 -- | Collect and observe results with 'Pqi.getResult' until it reports
 -- completion with 'Nothing'.
-drainResults :: (IsConnection c) => c -> IO [ResultObservation]
+drainResults :: Pqi.Connection -> IO [ResultObservation]
 drainResults connection = go []
   where
     go acc =
-      getResult connection >>= \case
+      connection.getResult >>= \case
         Nothing -> pure (reverse acc)
         Just result -> do
           observation <- observeResult result
           go (observation : acc)
 
 -- | One 'Pqi.getResult' step, observed.
-takeResult :: (IsConnection c) => c -> IO (Maybe ResultObservation)
-takeResult connection = getResult connection >>= traverse observeResult
+takeResult :: Pqi.Connection -> IO (Maybe ResultObservation)
+takeResult connection = connection.getResult >>= traverse observeResult
 
 -- | The results of one pipelined command: its result and the 'Nothing'
 -- separator that ends it.
 takeCommandResults ::
-  (IsConnection c) => c -> IO (Maybe ResultObservation, Maybe ResultObservation)
+  Pqi.Connection -> IO (Maybe ResultObservation, Maybe ResultObservation)
 takeCommandResults connection = do
   result <- takeResult connection
   separator <- takeResult connection
@@ -92,12 +86,12 @@
 -- | Collect blocking 'Pqi.getCopyData' outcomes until the stream reports
 -- anything other than a row (normally 'CopyOutDone'), including that final
 -- outcome.
-collectCopyOut :: (IsConnection c) => c -> IO [CopyOutResult]
+collectCopyOut :: Pqi.Connection -> IO [CopyOutResult]
 collectCopyOut connection = go (1000 :: Int) []
   where
     go 0 acc = pure (reverse acc)
     go n acc =
-      getCopyData connection False >>= \case
+      connection.getCopyData False >>= \case
         CopyOutRow row -> go (n - 1) (CopyOutRow row : acc)
         terminal -> pure (reverse (terminal : acc))
 
@@ -126,11 +120,11 @@
 
 -- | Run an action between @begin@ and @commit@. Large-object operations must
 -- run inside a transaction block.
-inTransaction :: (IsConnection c) => c -> IO a -> IO a
+inTransaction :: Pqi.Connection -> IO a -> IO a
 inTransaction connection action = do
-  _ <- exec connection "begin"
+  _ <- connection.exec "begin"
   result <- action
-  _ <- exec connection "commit"
+  _ <- connection.exec "commit"
   pure result
 
 boolOid :: Word32
