packages feed

pqi-ffi 0.0.1.1 → 0.1.0.0

raw patch · 6 files changed

+155/−149 lines, 6 filesdep ~pqidep ~pqi-conformancePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: pqi, pqi-conformance

API changes (from Hackage documentation)

- Pqi.Ffi: data Connection
- Pqi.Ffi: instance Pqi.IsCancel Pqi.Ffi.Cancel
- Pqi.Ffi: instance Pqi.IsConnection Pqi.Ffi.Connection
- Pqi.Ffi: instance Pqi.IsResult Pqi.Ffi.Result
+ Pqi.Ffi: adapter :: Adapter

Files

CHANGELOG.md view
@@ -0,0 +1,5 @@+# v0.1.0.0++## Breaking++- Migrate to `pqi` 0.1's record-of-functions redesign and switch to exporting the `adapter` value instead of the `Connection` type.
README.md view
@@ -7,11 +7,11 @@ backed by [`postgresql-libpq`](https://hackage.haskell.org/package/postgresql-libpq) (a binding to the C `libpq` library). -`Pqi.Ffi.Connection` wraps a C-backed `PGconn` and provides an-`IsConnection` instance whose every method is a near-mechanical delegation to-the matching `Database.PostgreSQL.LibPQ` function. The only work is converting-between this family's portable types (OIDs as `Word32`, indices as `Int32`,-the shared enums) and `postgresql-libpq`'s C-specific newtypes.+`Pqi.Ffi.adapter` produces `Pqi.Connection` values whose fields close over a+C-backed `PGconn`, each a near-mechanical delegation to the matching+`Database.PostgreSQL.LibPQ` function. The only work is converting between+this family's portable types (OIDs as `Word32`, indices as `Int32`, the+shared enums) and `postgresql-libpq`'s C-specific newtypes.  This adapter is the **fidelity reference** for the `pqi` family: it is the oracle against which all other adapters are tested. The
pqi-ffi.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-ffi-version: 0.0.1.1+version: 0.1.0.0 category: Database, PostgreSQL synopsis: FFI adapter for pqi, backed by postgresql-libpq description:@@ -95,7 +95,7 @@     base >=4.11 && <5,     bytestring >=0.10 && <0.13,     postgresql-libpq >=0.11 && <0.12,-    pqi ^>=0,+    pqi ^>=0.1,  test-suite ffi-test   import: test@@ -105,5 +105,5 @@   build-depends:     base >=4.11 && <5,     hspec >=2.11 && <2.12,-    pqi-conformance,+    pqi-conformance ^>=0.1,     pqi-ffi,
src/library/Pqi/Ffi.hs view
@@ -1,13 +1,16 @@ -- | The FFI adapter, backed by @postgresql-libpq@. ----- 'Connection' wraps the C-backed @PGconn@.+-- 'adapter' bundles the three functions that produce a 'Pqi.Connection'+-- whose fields are closures over the underlying C-backed @PGconn@.+-- 'Pqi.Result' and 'Pqi.Cancel' values are constructed the same way, closing+-- over the underlying @PGresult@\/@PGcancel@. ----- Each method is a near-mechanical delegation to the matching+-- Each closure is a near-mechanical delegation to the matching -- @Database.PostgreSQL.LibPQ@ function, with the only work being the -- conversion between this family's portable types (OIDs as 'Word32', indices -- as 'Int32', the shared enums) and @postgresql-libpq@'s C-specific newtypes. module Pqi.Ffi-  ( Connection,+  ( adapter,   ) where @@ -15,141 +18,140 @@ import qualified Pqi import Pqi.Ffi.Prelude --- | A handle to a PostgreSQL connection backed by the C @libpq@ library.-newtype Connection = Connection Pq.Connection---- | A result handle backed by a C @PGresult@.-newtype Result = Result Pq.Result---- | A cancellation handle backed by a C @PGcancel@.-newtype Cancel = Cancel Pq.Cancel--instance Pqi.IsResult Result where-  resultStatus (Result r) = fromExecStatus <$> Pq.resultStatus r-  resultErrorMessage (Result r) = Pq.resultErrorMessage r-  resultErrorField (Result r) field = Pq.resultErrorField r (toFieldCode field)-  unsafeFreeResult (Result r) = Pq.unsafeFreeResult r-  ntuples (Result r) = fromRow <$> Pq.ntuples r-  nfields (Result r) = fromColumn <$> Pq.nfields r-  fname (Result r) column = Pq.fname r (toColumn column)-  fnumber (Result r) name = fmap fromColumn <$> Pq.fnumber r name-  ftable (Result r) column = fromOid <$> Pq.ftable r (toColumn column)-  ftablecol (Result r) column = fromColumn <$> Pq.ftablecol r (toColumn column)-  fformat (Result r) column = fromFormat <$> Pq.fformat r (toColumn column)-  ftype (Result r) column = fromOid <$> Pq.ftype r (toColumn column)-  fmod (Result r) column = Pq.fmod r (toColumn column)-  fsize (Result r) column = Pq.fsize r (toColumn column)-  getvalue (Result r) row column = Pq.getvalue r (toRow row) (toColumn column)-  getvalue' (Result r) row column = Pq.getvalue' r (toRow row) (toColumn column)-  getisnull (Result r) row column = Pq.getisnull r (toRow row) (toColumn column)-  getlength (Result r) row column = Pq.getlength r (toRow row) (toColumn column)-  nparams (Result r) = fromIntegral <$> Pq.nparams r-  paramtype (Result r) index = fromOid <$> Pq.paramtype r (fromIntegral index)-  cmdStatus (Result r) = Pq.cmdStatus r-  cmdTuples (Result r) = Pq.cmdTuples r--instance Pqi.IsCancel Cancel where-  cancel (Cancel handle) = Pq.cancel handle--instance Pqi.IsConnection Connection where-  type ResultOf Connection = Result-  type CancelOf Connection = Cancel--  connectdb conninfo = Connection <$> Pq.connectdb conninfo-  connectStart conninfo = Connection <$> Pq.connectStart conninfo-  connectPoll (Connection c) = fromPollingStatus <$> Pq.connectPoll c-  newNullConnection = Connection <$> Pq.newNullConnection-  isNullConnection (Connection c) = Pq.isNullConnection c-  finish (Connection c) = Pq.finish c-  reset (Connection c) = Pq.reset c-  resetStart (Connection c) = Pq.resetStart c-  resetPoll (Connection c) = fromPollingStatus <$> Pq.resetPoll c-  db (Connection c) = Pq.db c-  user (Connection c) = Pq.user c-  pass (Connection c) = Pq.pass c-  host (Connection c) = Pq.host c-  port (Connection c) = Pq.port c-  options (Connection c) = Pq.options c-  status (Connection c) = fromConnStatus <$> Pq.status c-  transactionStatus (Connection c) = fromTransactionStatus <$> Pq.transactionStatus c-  parameterStatus (Connection c) name = Pq.parameterStatus c name-  protocolVersion (Connection c) = Pq.protocolVersion c-  serverVersion (Connection c) = Pq.serverVersion c-  errorMessage (Connection c) = Pq.errorMessage c-  socket (Connection c) = Pq.socket c-  backendPID (Connection c) = fromIntegral <$> Pq.backendPID c-  connectionNeedsPassword (Connection c) = Pq.connectionNeedsPassword c-  connectionUsedPassword (Connection c) = Pq.connectionUsedPassword c--  exec (Connection c) sql =-    fmap Result <$> Pq.exec c sql-  execParams (Connection c) sql params resultFormat =-    fmap Result <$> Pq.execParams c sql (fmap (fmap toParam) params) (toFormat resultFormat)-  prepare (Connection c) name sql paramTypes =-    fmap Result <$> Pq.prepare c name sql (fmap (fmap toOid) paramTypes)-  execPrepared (Connection c) name params resultFormat =-    fmap Result <$> Pq.execPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat)-  describePrepared (Connection c) name =-    fmap Result <$> Pq.describePrepared c name-  describePortal (Connection c) name =-    fmap Result <$> Pq.describePortal c name--  escapeStringConn (Connection c) = Pq.escapeStringConn c-  escapeByteaConn (Connection c) = Pq.escapeByteaConn c-  escapeIdentifier (Connection c) = Pq.escapeIdentifier c--  sendQuery (Connection c) sql = Pq.sendQuery c sql-  sendQueryParams (Connection c) sql params resultFormat =-    Pq.sendQueryParams c sql (fmap (fmap toParam) params) (toFormat resultFormat)-  sendPrepare (Connection c) name sql paramTypes =-    Pq.sendPrepare c name sql (fmap (fmap toOid) paramTypes)-  sendQueryPrepared (Connection c) name params resultFormat =-    Pq.sendQueryPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat)-  sendDescribePrepared (Connection c) name = Pq.sendDescribePrepared c name-  sendDescribePortal (Connection c) name = Pq.sendDescribePortal c name-  getResult (Connection c) = fmap Result <$> Pq.getResult c-  consumeInput (Connection c) = Pq.consumeInput c-  isBusy (Connection c) = Pq.isBusy c-  setnonblocking (Connection c) nonBlocking = Pq.setnonblocking c nonBlocking-  isnonblocking (Connection c) = Pq.isnonblocking c-  setSingleRowMode (Connection c) = Pq.setSingleRowMode c-  flush (Connection c) = fromFlushStatus <$> Pq.flush c--  pipelineStatus (Connection c) = fromPipelineStatus <$> Pq.pipelineStatus c-  enterPipelineMode (Connection c) = Pq.enterPipelineMode c-  exitPipelineMode (Connection c) = Pq.exitPipelineMode c-  pipelineSync (Connection c) = Pq.pipelineSync c-  sendFlushRequest (Connection c) = Pq.sendFlushRequest c--  getCancel (Connection c) = fmap Cancel <$> Pq.getCancel c--  notifies (Connection c) = fmap fromNotify <$> Pq.notifies c-  disableNoticeReporting (Connection c) = Pq.disableNoticeReporting c-  enableNoticeReporting (Connection c) = Pq.enableNoticeReporting c-  getNotice (Connection c) = Pq.getNotice c+-- | The FFI adapter.+adapter :: Pqi.Adapter+adapter =+  Pqi.Adapter+    { Pqi.name = "pqi-ffi",+      Pqi.connectdb = \conninfo -> mkConnection <$> Pq.connectdb conninfo,+      Pqi.connectStart = \conninfo -> mkConnection <$> Pq.connectStart conninfo,+      Pqi.newNullConnection = mkConnection <$> Pq.newNullConnection,+      Pqi.unescapeBytea = \input -> Pq.unescapeBytea input+    } -  putCopyData (Connection c) value = fromCopyInResult <$> Pq.putCopyData c value-  putCopyEnd (Connection c) reason = fromCopyInResult <$> Pq.putCopyEnd c reason-  getCopyData (Connection c) nonBlocking = fromCopyOutResult <$> Pq.getCopyData c nonBlocking+-- | Build a 'Pqi.Connection' whose fields close over the given+-- @postgresql-libpq@ connection handle.+mkConnection :: Pq.Connection -> Pqi.Connection+mkConnection c =+  Pqi.Connection+    { Pqi.connectPoll = fromPollingStatus <$> Pq.connectPoll c,+      Pqi.isNullConnection = Pq.isNullConnection c,+      Pqi.finish = Pq.finish c,+      Pqi.reset = Pq.reset c,+      Pqi.resetStart = Pq.resetStart c,+      Pqi.resetPoll = fromPollingStatus <$> Pq.resetPoll c,+      Pqi.db = Pq.db c,+      Pqi.user = Pq.user c,+      Pqi.pass = Pq.pass c,+      Pqi.host = Pq.host c,+      Pqi.port = Pq.port c,+      Pqi.options = Pq.options c,+      Pqi.status = fromConnStatus <$> Pq.status c,+      Pqi.transactionStatus = fromTransactionStatus <$> Pq.transactionStatus c,+      Pqi.parameterStatus = \name -> Pq.parameterStatus c name,+      Pqi.protocolVersion = Pq.protocolVersion c,+      Pqi.serverVersion = Pq.serverVersion c,+      Pqi.errorMessage = Pq.errorMessage c,+      Pqi.socket = Pq.socket c,+      Pqi.backendPID = fromIntegral <$> Pq.backendPID c,+      Pqi.connectionNeedsPassword = Pq.connectionNeedsPassword c,+      Pqi.connectionUsedPassword = Pq.connectionUsedPassword c,+      Pqi.exec = \sql -> fmap mkResult <$> Pq.exec c sql,+      Pqi.execParams = \sql params resultFormat ->+        fmap mkResult <$> Pq.execParams c sql (fmap (fmap toParam) params) (toFormat resultFormat),+      Pqi.prepare = \name sql paramTypes ->+        fmap mkResult <$> Pq.prepare c name sql (fmap (fmap toOid) paramTypes),+      Pqi.execPrepared = \name params resultFormat ->+        fmap mkResult <$> Pq.execPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat),+      Pqi.describePrepared = \name -> fmap mkResult <$> Pq.describePrepared c name,+      Pqi.describePortal = \name -> fmap mkResult <$> Pq.describePortal c name,+      Pqi.escapeStringConn = \s -> Pq.escapeStringConn c s,+      Pqi.escapeByteaConn = \s -> Pq.escapeByteaConn c s,+      Pqi.escapeIdentifier = \s -> Pq.escapeIdentifier c s,+      Pqi.sendQuery = \sql -> Pq.sendQuery c sql,+      Pqi.sendQueryParams = \sql params resultFormat ->+        Pq.sendQueryParams c sql (fmap (fmap toParam) params) (toFormat resultFormat),+      Pqi.sendPrepare = \name sql paramTypes ->+        Pq.sendPrepare c name sql (fmap (fmap toOid) paramTypes),+      Pqi.sendQueryPrepared = \name params resultFormat ->+        Pq.sendQueryPrepared c name (fmap (fmap toBoundParam) params) (toFormat resultFormat),+      Pqi.sendDescribePrepared = \name -> Pq.sendDescribePrepared c name,+      Pqi.sendDescribePortal = \name -> Pq.sendDescribePortal c name,+      Pqi.getResult = fmap mkResult <$> Pq.getResult c,+      Pqi.consumeInput = Pq.consumeInput c,+      Pqi.isBusy = Pq.isBusy c,+      Pqi.setnonblocking = \nonBlocking -> Pq.setnonblocking c nonBlocking,+      Pqi.isnonblocking = Pq.isnonblocking c,+      Pqi.setSingleRowMode = Pq.setSingleRowMode c,+      Pqi.flush = fromFlushStatus <$> Pq.flush c,+      Pqi.pipelineStatus = fromPipelineStatus <$> Pq.pipelineStatus c,+      Pqi.enterPipelineMode = Pq.enterPipelineMode c,+      Pqi.exitPipelineMode = Pq.exitPipelineMode c,+      Pqi.pipelineSync = Pq.pipelineSync c,+      Pqi.sendFlushRequest = Pq.sendFlushRequest c,+      Pqi.getCancel = fmap mkCancel <$> Pq.getCancel c,+      Pqi.notifies = fmap fromNotify <$> Pq.notifies c,+      Pqi.disableNoticeReporting = Pq.disableNoticeReporting c,+      Pqi.enableNoticeReporting = Pq.enableNoticeReporting c,+      Pqi.getNotice = Pq.getNotice c,+      Pqi.putCopyData = \value -> fromCopyInResult <$> Pq.putCopyData c value,+      Pqi.putCopyEnd = \reason -> fromCopyInResult <$> Pq.putCopyEnd c reason,+      Pqi.getCopyData = \nonBlocking -> fromCopyOutResult <$> Pq.getCopyData c nonBlocking,+      Pqi.loCreat = fmap fromOid <$> Pq.loCreat c,+      Pqi.loCreate = \oid -> fmap fromOid <$> Pq.loCreate c (toOid oid),+      Pqi.loImport = \path -> fmap fromOid <$> Pq.loImport c path,+      Pqi.loImportWithOid = \path oid -> fmap fromOid <$> Pq.loImportWithOid c path (toOid oid),+      Pqi.loExport = \oid path -> Pq.loExport c (toOid oid) path,+      Pqi.loOpen = \oid mode -> fmap fromLibPQLoFd <$> Pq.loOpen c (toOid oid) mode,+      Pqi.loWrite = \fd value -> Pq.loWrite c (toLibPQLoFd fd) value,+      Pqi.loRead = \fd len -> Pq.loRead c (toLibPQLoFd fd) len,+      Pqi.loSeek = \fd mode offset -> Pq.loSeek c (toLibPQLoFd fd) mode offset,+      Pqi.loTell = \fd -> Pq.loTell c (toLibPQLoFd fd),+      Pqi.loTruncate = \fd len -> Pq.loTruncate c (toLibPQLoFd fd) len,+      Pqi.loClose = \fd -> Pq.loClose c (toLibPQLoFd fd),+      Pqi.loUnlink = \oid -> Pq.loUnlink c (toOid oid),+      Pqi.clientEncoding = Pq.clientEncoding c,+      Pqi.setClientEncoding = \encoding -> Pq.setClientEncoding c encoding,+      Pqi.setErrorVerbosity = \verbosity ->+        fromVerbosity <$> Pq.setErrorVerbosity c (toVerbosity verbosity)+    } -  loCreat (Connection c) = fmap fromOid <$> Pq.loCreat c-  loCreate (Connection c) oid = fmap fromOid <$> Pq.loCreate c (toOid oid)-  loImport (Connection c) path = fmap fromOid <$> Pq.loImport c path-  loImportWithOid (Connection c) path oid = fmap fromOid <$> Pq.loImportWithOid c path (toOid oid)-  loExport (Connection c) oid path = Pq.loExport c (toOid oid) path-  loOpen (Connection c) oid mode = fmap fromLibPQLoFd <$> Pq.loOpen c (toOid oid) mode-  loWrite (Connection c) fd value = Pq.loWrite c (toLibPQLoFd fd) value-  loRead (Connection c) fd len = Pq.loRead c (toLibPQLoFd fd) len-  loSeek (Connection c) fd mode offset = Pq.loSeek c (toLibPQLoFd fd) mode offset-  loTell (Connection c) fd = Pq.loTell c (toLibPQLoFd fd)-  loTruncate (Connection c) fd len = Pq.loTruncate c (toLibPQLoFd fd) len-  loClose (Connection c) fd = Pq.loClose c (toLibPQLoFd fd)-  loUnlink (Connection c) oid = Pq.loUnlink c (toOid oid)+-- | Build a 'Pqi.Result' whose fields close over the given+-- @postgresql-libpq@ result handle.+mkResult :: Pq.Result -> Pqi.Result+mkResult r =+  Pqi.Result+    { Pqi.resultStatus = fromExecStatus <$> Pq.resultStatus r,+      Pqi.resultErrorMessage = Pq.resultErrorMessage r,+      Pqi.resultErrorField = \field -> Pq.resultErrorField r (toFieldCode field),+      Pqi.unsafeFreeResult = Pq.unsafeFreeResult r,+      Pqi.ntuples = fromRow <$> Pq.ntuples r,+      Pqi.nfields = fromColumn <$> Pq.nfields r,+      Pqi.fname = \column -> Pq.fname r (toColumn column),+      Pqi.fnumber = \name -> fmap fromColumn <$> Pq.fnumber r name,+      Pqi.ftable = \column -> fromOid <$> Pq.ftable r (toColumn column),+      Pqi.ftablecol = \column -> fromColumn <$> Pq.ftablecol r (toColumn column),+      Pqi.fformat = \column -> fromFormat <$> Pq.fformat r (toColumn column),+      Pqi.ftype = \column -> fromOid <$> Pq.ftype r (toColumn column),+      Pqi.fmod = \column -> Pq.fmod r (toColumn column),+      Pqi.fsize = \column -> Pq.fsize r (toColumn column),+      Pqi.getvalue = \row column -> Pq.getvalue' r (toRow row) (toColumn column),+      Pqi.getvalue' = \row column -> Pq.getvalue' r (toRow row) (toColumn column),+      Pqi.getisnull = \row column -> Pq.getisnull r (toRow row) (toColumn column),+      Pqi.getlength = \row column -> Pq.getlength r (toRow row) (toColumn column),+      Pqi.nparams = fromIntegral <$> Pq.nparams r,+      Pqi.paramtype = \index -> fromOid <$> Pq.paramtype r (fromIntegral index),+      Pqi.cmdStatus = Pq.cmdStatus r,+      Pqi.cmdTuples = Pq.cmdTuples r+    } -  clientEncoding (Connection c) = Pq.clientEncoding c-  setClientEncoding (Connection c) encoding = Pq.setClientEncoding c encoding-  setErrorVerbosity (Connection c) verbosity =-    fromVerbosity <$> Pq.setErrorVerbosity c (toVerbosity verbosity)+-- | Build a 'Pqi.Cancel' whose field closes over the given+-- @postgresql-libpq@ cancellation handle.+mkCancel :: Pq.Cancel -> Pqi.Cancel+mkCancel handle =+  Pqi.Cancel+    { Pqi.cancel = Pq.cancel handle+    }  -- * Type conversions 
src/library/Pqi/Ffi/Prelude.hs view
@@ -17,11 +17,11 @@ import Data.Ord as Exports import Data.Traversable as Exports import Data.Word as Exports-import System.IO as Exports (IO)-import Text.Show as Exports import Prelude as Exports   ( Enum (..),     Integral (..),     Num (..),     fromIntegral,   )+import System.IO as Exports (IO)+import Text.Show as Exports
src/test/Spec.hs view
@@ -4,11 +4,10 @@ -- and tears the container down again. module Main (main) where -import Data.Proxy (Proxy (..)) import Pqi.Conformance (specs)-import Pqi.Ffi (Connection)-import Test.Hspec+import qualified Pqi.Ffi import Prelude+import Test.Hspec  main :: IO ()-main = hspec (specs (Proxy @Connection))+main = hspec (specs Pqi.Ffi.adapter)