pqi-conformance 0.0.1.2 → 0.1.0.0
raw patch · 111 files changed
+1333/−1241 lines, 111 filesdep ~pqiPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: pqi
API changes (from Hackage documentation)
- Pqi.Conformance: specs :: IsConnection c => Proxy c -> Spec
+ Pqi.Conformance: specs :: Adapter -> Spec
Files
- CHANGELOG.md +10/−0
- pqi-conformance.cabal +4/−2
- src/library/Pqi/Conformance.hs +106/−104
- src/library/Pqi/Conformance/Harness.hs +20/−22
- src/library/Pqi/Conformance/Observation.hs +46/−47
- src/library/Pqi/Conformance/Operation/BackendPID.hs +5/−5
- src/library/Pqi/Conformance/Operation/Cancel.hs +16/−16
- src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs +15/−15
- src/library/Pqi/Conformance/Operation/Cancel/Stale.hs +24/−26
- src/library/Pqi/Conformance/Operation/ClientEncoding.hs +9/−9
- src/library/Pqi/Conformance/Operation/CmdStatus.hs +6/−6
- src/library/Pqi/Conformance/Operation/CmdTuples.hs +6/−6
- src/library/Pqi/Conformance/Operation/ConnectPoll.hs +8/−8
- src/library/Pqi/Conformance/Operation/ConnectStart.hs +7/−7
- src/library/Pqi/Conformance/Operation/Connectdb.hs +29/−29
- src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs +4/−4
- src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs +4/−4
- src/library/Pqi/Conformance/Operation/ConsumeInput.hs +7/−7
- src/library/Pqi/Conformance/Operation/Db.hs +4/−4
- src/library/Pqi/Conformance/Operation/DescribePortal.hs +9/−10
- src/library/Pqi/Conformance/Operation/DescribePrepared.hs +11/−11
- src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs +9/−9
- src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs +8/−8
- src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs +9/−9
- src/library/Pqi/Conformance/Operation/ErrorMessage.hs +24/−24
- src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs +5/−5
- src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs +7/−7
- src/library/Pqi/Conformance/Operation/EscapeStringConn.hs +9/−9
- src/library/Pqi/Conformance/Operation/Exec.hs +4/−4
- src/library/Pqi/Conformance/Operation/ExecParams.hs +17/−18
- src/library/Pqi/Conformance/Operation/ExecPrepared.hs +16/−17
- src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs +40/−41
- src/library/Pqi/Conformance/Operation/Fformat.hs +6/−7
- src/library/Pqi/Conformance/Operation/Finish.hs +10/−10
- src/library/Pqi/Conformance/Operation/Flush.hs +8/−8
- src/library/Pqi/Conformance/Operation/Fmod.hs +8/−8
- src/library/Pqi/Conformance/Operation/Fname.hs +8/−8
- src/library/Pqi/Conformance/Operation/Fnumber.hs +6/−6
- src/library/Pqi/Conformance/Operation/Fsize.hs +8/−8
- src/library/Pqi/Conformance/Operation/Ftable.hs +7/−7
- src/library/Pqi/Conformance/Operation/Ftablecol.hs +7/−7
- src/library/Pqi/Conformance/Operation/Ftype.hs +8/−8
- src/library/Pqi/Conformance/Operation/GetCancel.hs +5/−5
- src/library/Pqi/Conformance/Operation/GetCopyData.hs +4/−4
- src/library/Pqi/Conformance/Operation/GetNotice.hs +8/−8
- src/library/Pqi/Conformance/Operation/GetResult.hs +5/−5
- src/library/Pqi/Conformance/Operation/Getisnull.hs +8/−8
- src/library/Pqi/Conformance/Operation/Getlength.hs +7/−7
- src/library/Pqi/Conformance/Operation/Getvalue.hs +9/−9
- src/library/Pqi/Conformance/Operation/GetvalueCopy.hs +8/−8
- src/library/Pqi/Conformance/Operation/Host.hs +4/−4
- src/library/Pqi/Conformance/Operation/IsBusy.hs +8/−8
- src/library/Pqi/Conformance/Operation/IsNullConnection.hs +10/−10
- src/library/Pqi/Conformance/Operation/Isnonblocking.hs +7/−7
- src/library/Pqi/Conformance/Operation/LoClose.hs +9/−9
- src/library/Pqi/Conformance/Operation/LoCreat.hs +6/−6
- src/library/Pqi/Conformance/Operation/LoCreate.hs +7/−7
- src/library/Pqi/Conformance/Operation/LoExport.hs +7/−7
- src/library/Pqi/Conformance/Operation/LoImport.hs +6/−6
- src/library/Pqi/Conformance/Operation/LoImportWithOid.hs +7/−7
- src/library/Pqi/Conformance/Operation/LoOpen.hs +9/−9
- src/library/Pqi/Conformance/Operation/LoRead.hs +11/−11
- src/library/Pqi/Conformance/Operation/LoSeek.hs +12/−12
- src/library/Pqi/Conformance/Operation/LoTell.hs +12/−12
- src/library/Pqi/Conformance/Operation/LoTruncate.hs +11/−11
- src/library/Pqi/Conformance/Operation/LoUnlink.hs +7/−7
- src/library/Pqi/Conformance/Operation/LoWrite.hs +9/−9
- src/library/Pqi/Conformance/Operation/NewNullConnection.hs +8/−8
- src/library/Pqi/Conformance/Operation/Nfields.hs +5/−5
- src/library/Pqi/Conformance/Operation/Notifies.hs +25/−24
- src/library/Pqi/Conformance/Operation/Nparams.hs +11/−11
- src/library/Pqi/Conformance/Operation/Ntuples.hs +5/−5
- src/library/Pqi/Conformance/Operation/Options.hs +4/−4
- src/library/Pqi/Conformance/Operation/ParameterStatus.hs +11/−11
- src/library/Pqi/Conformance/Operation/Paramtype.hs +13/−13
- src/library/Pqi/Conformance/Operation/Pass.hs +4/−4
- src/library/Pqi/Conformance/Operation/PipelineStatus.hs +12/−12
- src/library/Pqi/Conformance/Operation/PipelineSync.hs +19/−17
- src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs +52/−0
- src/library/Pqi/Conformance/Operation/Port.hs +4/−4
- src/library/Pqi/Conformance/Operation/Prepare.hs +8/−8
- src/library/Pqi/Conformance/Operation/ProtocolVersion.hs +4/−4
- src/library/Pqi/Conformance/Operation/PutCopyData.hs +12/−12
- src/library/Pqi/Conformance/Operation/PutCopyEnd.hs +11/−11
- src/library/Pqi/Conformance/Operation/Reset.hs +9/−9
- src/library/Pqi/Conformance/Operation/ResetPoll.hs +8/−8
- src/library/Pqi/Conformance/Operation/ResetStart.hs +7/−7
- src/library/Pqi/Conformance/Operation/ResultErrorField.hs +6/−6
- src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs +8/−8
- src/library/Pqi/Conformance/Operation/ResultStatus.hs +5/−5
- src/library/Pqi/Conformance/Operation/SendDescribePortal.hs +7/−7
- src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs +6/−6
- src/library/Pqi/Conformance/Operation/SendFlushRequest.hs +9/−9
- src/library/Pqi/Conformance/Operation/SendPrepare.hs +5/−5
- src/library/Pqi/Conformance/Operation/SendQuery.hs +5/−5
- src/library/Pqi/Conformance/Operation/SendQueryParams.hs +5/−6
- src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs +6/−6
- src/library/Pqi/Conformance/Operation/ServerVersion.hs +4/−4
- src/library/Pqi/Conformance/Operation/SetClientEncoding.hs +7/−7
- src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs +7/−7
- src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs +9/−9
- src/library/Pqi/Conformance/Operation/Setnonblocking.hs +8/−8
- src/library/Pqi/Conformance/Operation/Socket.hs +5/−5
- src/library/Pqi/Conformance/Operation/Status.hs +4/−4
- src/library/Pqi/Conformance/Operation/TransactionStatus.hs +11/−11
- src/library/Pqi/Conformance/Operation/UnescapeBytea.hs +49/−0
- src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs +6/−6
- src/library/Pqi/Conformance/Operation/User.hs +4/−4
- src/library/Pqi/Conformance/Prelude.hs +0/−1
- src/library/Pqi/Conformance/Reference.hs +136/−144
- src/library/Pqi/Conformance/Scenario.hs +19/−25
CHANGELOG.md view
@@ -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
pqi-conformance.cabal view
@@ -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,
src/library/Pqi/Conformance.hs view
@@ -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
src/library/Pqi/Conformance/Harness.hs view
@@ -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
src/library/Pqi/Conformance/Observation.hs view
@@ -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 {..}
src/library/Pqi/Conformance/Operation/BackendPID.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Cancel.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Cancel/Stale.hs view
@@ -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)))
src/library/Pqi/Conformance/Operation/ClientEncoding.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/CmdStatus.hs view
@@ -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"
src/library/Pqi/Conformance/Operation/CmdTuples.hs view
@@ -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"
src/library/Pqi/Conformance/Operation/ConnectPoll.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ConnectStart.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Connectdb.hs view
@@ -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
src/library/Pqi/Conformance/Operation/ConnectionNeedsPassword.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ConnectionUsedPassword.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ConsumeInput.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Db.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/DescribePortal.hs view
@@ -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
src/library/Pqi/Conformance/Operation/DescribePrepared.hs view
@@ -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
src/library/Pqi/Conformance/Operation/DisableNoticeReporting.hs view
@@ -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
src/library/Pqi/Conformance/Operation/EnableNoticeReporting.hs view
@@ -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
src/library/Pqi/Conformance/Operation/EnterPipelineMode.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ErrorMessage.hs view
@@ -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
src/library/Pqi/Conformance/Operation/EscapeByteaConn.hs view
@@ -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 = [ "",
src/library/Pqi/Conformance/Operation/EscapeIdentifier.hs view
@@ -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
src/library/Pqi/Conformance/Operation/EscapeStringConn.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Exec.hs view
@@ -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"
src/library/Pqi/Conformance/Operation/ExecParams.hs view
@@ -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"
src/library/Pqi/Conformance/Operation/ExecPrepared.hs view
@@ -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
src/library/Pqi/Conformance/Operation/ExitPipelineMode.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Fformat.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Finish.hs view
@@ -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 ()
src/library/Pqi/Conformance/Operation/Flush.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Fmod.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Fname.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Fnumber.hs view
@@ -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"]
src/library/Pqi/Conformance/Operation/Fsize.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Ftable.hs view
@@ -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]
src/library/Pqi/Conformance/Operation/Ftablecol.hs view
@@ -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]
src/library/Pqi/Conformance/Operation/Ftype.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/GetCancel.hs view
@@ -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
src/library/Pqi/Conformance/Operation/GetCopyData.hs view
@@ -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
src/library/Pqi/Conformance/Operation/GetNotice.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/GetResult.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Getisnull.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Getlength.hs view
@@ -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]
src/library/Pqi/Conformance/Operation/Getvalue.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/GetvalueCopy.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Host.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/IsBusy.hs view
@@ -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
src/library/Pqi/Conformance/Operation/IsNullConnection.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Isnonblocking.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/LoClose.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoCreat.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/LoCreate.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoExport.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoImport.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/LoImportWithOid.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoOpen.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoRead.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoSeek.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoTell.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoTruncate.hs view
@@ -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
src/library/Pqi/Conformance/Operation/LoUnlink.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/LoWrite.hs view
@@ -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
src/library/Pqi/Conformance/Operation/NewNullConnection.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Nfields.hs view
@@ -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)"
src/library/Pqi/Conformance/Operation/Notifies.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Nparams.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Ntuples.hs view
@@ -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)"
src/library/Pqi/Conformance/Operation/Options.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ParameterStatus.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Paramtype.hs view
@@ -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]
src/library/Pqi/Conformance/Operation/Pass.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/PipelineStatus.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/PipelineSync.hs view
@@ -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)
+ src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Port.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Prepare.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ProtocolVersion.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/PutCopyData.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/PutCopyEnd.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Reset.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ResetPoll.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ResetStart.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ResultErrorField.hs view
@@ -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"]
src/library/Pqi/Conformance/Operation/ResultErrorMessage.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ResultStatus.hs view
@@ -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 ""
src/library/Pqi/Conformance/Operation/SendDescribePortal.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SendDescribePrepared.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SendFlushRequest.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SendPrepare.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SendQuery.hs view
@@ -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"
src/library/Pqi/Conformance/Operation/SendQueryParams.hs view
@@ -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
src/library/Pqi/Conformance/Operation/SendQueryPrepared.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/ServerVersion.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SetClientEncoding.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SetErrorVerbosity.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/SetSingleRowMode.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Setnonblocking.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/Socket.hs view
@@ -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
src/library/Pqi/Conformance/Operation/Status.hs view
@@ -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)
src/library/Pqi/Conformance/Operation/TransactionStatus.hs view
@@ -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)
+ src/library/Pqi/Conformance/Operation/UnescapeBytea.hs view
@@ -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"+ ]
src/library/Pqi/Conformance/Operation/UnsafeFreeResult.hs view
@@ -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
src/library/Pqi/Conformance/Operation/User.hs view
@@ -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)
src/library/Pqi/Conformance/Prelude.hs view
@@ -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
src/library/Pqi/Conformance/Reference.hs view
@@ -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
src/library/Pqi/Conformance/Scenario.hs view
@@ -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