packages feed

pqi 0.1.0.2 → 1.0.0.0

raw patch · 4 files changed

+86/−114 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Pqi: [resStatus] :: Adapter -> ExecStatus -> IO ByteString
- Pqi: Adapter :: Text -> (ByteString -> IO Connection) -> (ByteString -> IO Connection) -> IO Connection -> (ByteString -> IO (Maybe ByteString)) -> Adapter
+ Pqi: Adapter :: Text -> (ByteString -> IO Connection) -> (ByteString -> IO Connection) -> IO Connection -> (ByteString -> IO (Maybe ByteString)) -> (ExecStatus -> IO ByteString) -> Adapter

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+# v1.0.0.0++## Breaking++- Add the `resStatus` field to `Adapter`+ # v0.1.0.2  Polish the docs.
README.md view
@@ -94,10 +94,9 @@ - OIDs are a plain `Word32` and row/column/parameter indices are a plain   `Int32`, instead of the C-specific newtypes of the original. - There's no `invalidOid` constant. It's just `0`.-- Ambiguous, rarely-useful helpers (e.g. `resStatus`) are omitted, as is-  `libpqVersion`.-- `unescapeBytea` is a field of `Adapter` rather than a-  connection-independent top-level function, since its implementation is+- `libpqVersion` is omitted.+- `unescapeBytea` and `resStatus` are fields of `Adapter` rather than+  connection-independent top-level functions, since their implementations are   adapter-specific.  [libpq]: https://www.postgresql.org/docs/current/libpq.html
pqi.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-version: 0.1.0.2+version: 1.0.0.0 category: Database, PostgreSQL synopsis: Driver-agnostic interface to the PostgreSQL libpq API description:
src/library/Pqi.hs view
@@ -1,48 +1,36 @@--- | A driver-agnostic reproduction of the [@postgresql-libpq@](https://hackage.haskell.org/package/postgresql-libpq) API (version--- @0.11@, the pipelining-capable release).+-- | A driver-agnostic reproduction of the [@postgresql-libpq@](https://hackage.haskell.org/package/postgresql-libpq) @0.11@ API+-- (the pipelining-capable release). ----- The connection is reified as a single concrete record type, 'Connection',--- whose fields are the closures that implement each capability. Result--- accessors live in the independent 'Result' record, and cancellation--- handles in the 'Cancel' record. A 'Connection' produces 'Result's and--- 'Cancel's directly (via its 'exec', 'getResult', 'getCancel', etc.--- fields) — there is no type-level indirection: the whole package defines--- exactly one 'Connection', one 'Result', and one 'Cancel' type.+-- t'Connection', t'Result' and t'Cancel' are records of @IO@ closures, each+-- already closed over the handle it needs (a C @PGconn@ pointer, a native+-- socket, ...). Exactly one of each type exists in the package: no classes,+-- no type parameters. ----- Each field of these records is a closure that has already captured--- whatever underlying handle (e.g. a C @PGconn@ pointer, or a native--- socket) it needs; from the caller's perspective a 'Connection' is simply--- a bundle of @IO@ actions. This trades the old class-based polymorphism--- for a concrete, monomorphic value that can be passed around, stored, and--- constructed by whichever adapter package is in use.+-- Connections come from adapter packages — @pqi-ffi@ (C @libpq@ via+-- @postgresql-libpq@) and @pqi-native@ (pure-Haskell wire protocol) — each+-- exporting one top-level t'Adapter' value. An adapter must be byte-identical+-- to @libpq@ on every protocol-derived value; @pqi-conformance@ enforces that+-- differentially. ----- Adapter packages (e.g. @pqi-ffi@, @pqi-native@) are responsible for--- constructing 'Connection' values — this package does not provide any--- @connectdb@\/@connectStart@\/@newNullConnection@-style constructors of--- its own, since those don't have a connection to close over yet. Instead,--- each adapter package exports a single top-level value of type 'Adapter',--- bundling its connection-establishing functions together so that callers--- who need to be adapter-agnostic (e.g. a differential test harness, or a--- consumer that lets its own users pick an adapter) can hold onto one value--- rather than a family of adapter-qualified functions.+-- == Differences from @postgresql-libpq@ ----- Function names, argument order, and semantics mirror the API of the C library binding--- @postgresql-libpq@.--- The only deliberate departures are:+-- Everything else — names, argument order, semantics — mirrors+-- @Database.PostgreSQL.LibPQ@. ----- * @Connection@, @Result@, and @Cancel@ are plain records of closures---   rather than a class-parameterised type and its associated types.+-- * Connection acquisition lives in t'Adapter': @connectdb@, @connectStart@+--   and @newNullConnection@ are its fields, not top-level functions. ----- * OIDs are a plain 'Word32' and row\/column\/parameter indices and LoFds are a---   plain 'Int32', rather than the C-specific newtypes of the original.+-- * @unescapeBytea@ and @resStatus@ are t'Adapter' fields too. They take no+--   connection, but their implementations are adapter-specific. ----- * Ambiguous, rarely-useful helpers (e.g. @resStatus@) are omitted,---   @libpqVersion@ is omitted too.+-- * t'Connection', t'Result' and t'Cancel' are records of closures rather than+--   opaque handles fed to top-level functions. Call sites are unchanged:+--   @exec connection sql@ selects a field and applies it. ----- * There's no @invalidOid@ constant. It's just 0.+-- * @Oid@ is 'Word32'; @Row@, @Column@, @LoFd@ are 'Int32'. No @invalidOid@+--   constant — it is @0@. ----- * @unescapeBytea@ is a field of 'Adapter' rather than a connection-independent---   top-level function, since its implementation is adapter-specific.+-- * @libpqVersion@ is omitted. module Pqi   ( -- * Adapter     Adapter (..),@@ -115,17 +103,17 @@     NonfatalError   | -- | A fatal error occurred.     FatalError-  | -- | The @'Result'@ contains a single result tuple from the current command.+  | -- | The t'Result' contains a single result tuple from the current command.     -- This status occurs only when single-row mode has been selected for the     -- query.     SingleTuple-  | -- | The @'Result'@ represents a synchronization point in pipeline mode,-    -- requested by @'pipelineSync'@. This status occurs only in pipeline mode.+  | -- | The t'Result' represents a synchronization point in pipeline mode,+    -- requested by 'pipelineSync'. This status occurs only in pipeline mode.     PipelineSync-  | -- | The @'Result'@ represents a pipeline that has received an error from-    -- the server. @'getResult'@ must be called repeatedly, and each time it will+  | -- | The t'Result' represents a pipeline that has received an error from+    -- the server. 'getResult' must be called repeatedly, and each time it will     -- return this status code until the end of the current pipeline, at which-    -- point it will return @'PipelineSync'@ and normal processing can resume.+    -- point it will return 'PipelineSync' and normal processing can resume.     PipelineAbort   deriving stock (Eq, Ord, Show, Enum, Bounded) @@ -239,25 +227,22 @@  -- * Result inspection --- | Result-accessor closures, independent of the connection that produced--- the result. This allows row decoders and projection functions (such as--- 'observeResult' in @pqi-conformance@) to operate on any result value--- without knowing the originating connection or adapter.+-- | Result-accessor closures, closed over an adapter's own result+-- representation (e.g. a C @PGresult@ pointer). ----- There is exactly one 'Result' type in the whole @pqi@ package; adapters--- construct values of this type by closing each field over their own--- underlying result representation (e.g. a C @PGresult@ pointer).+-- Carries no reference to the connection that produced it, so decoders can+-- consume a result without knowing its origin. data Result = Result   { -- | The status of the result.     resultStatus :: IO ExecStatus,-    -- | The flat error message associated with the result, if any. Best-effort;-    -- see the note on 'errorMessage'.+    -- | The flat error message of the result, if any. Formatted locally by the+    -- driver, so adapters are not expected to agree byte for byte; use+    -- 'resultErrorField' where exactness matters.     resultErrorMessage :: IO (Maybe ByteString),     -- | A single structured field of the result's error report.     resultErrorField :: FieldCode -> IO (Maybe ByteString),-    -- | Free the result. Adapters that manage results with the garbage collector-    -- may implement this as a no-op; for the C-backed adapter it frees the-    -- underlying @PGresult@, after which the result must not be used.+    -- | Free the result, after which it must not be used. A no-op in adapters+    -- that leave results to the garbage collector.     unsafeFreeResult :: IO (),     -- | Number of rows (tuples) in the result.     ntuples :: IO Int32,@@ -281,14 +266,10 @@     -- | Server-side storage size of the given column's type, or a negative value     -- for variable size.     fsize :: Int32 -> IO Int,-    -- | Value at @(row, column)@, or @'Nothing'@ for SQL @NULL@. In the old-    -- class-based API this delegated to @'getvalue''@ by default; since a-    -- record has no notion of default methods, adapters that want that-    -- behaviour should set this field to the same closure as 'getvalue''-    -- when constructing the 'Result'.+    -- | Value at @(row, column)@, or 'Nothing' for SQL @NULL@. May alias the+    -- result's storage, which 'unsafeFreeResult' invalidates.     getvalue :: Int32 -> Int32 -> IO (Maybe ByteString),-    -- | Like 'getvalue', but always returns a copy that remains valid after the-    -- result is freed.+    -- | Like 'getvalue', but always a copy, valid after the result is freed.     getvalue' :: Int32 -> Int32 -> IO (Maybe ByteString),     -- | Whether the value at @(row, column)@ is SQL @NULL@.     getisnull :: Int32 -> Int32 -> IO Bool,@@ -307,11 +288,8 @@  -- * Cancellation --- | A cancellation handle, isolated from the connection that produced it.------ There is exactly one 'Cancel' type in the whole @pqi@ package; adapters--- construct values of this type by closing over their own underlying--- cancellation handle.+-- | A cancellation handle, isolated from the connection that produced it,+-- hence usable from another thread while that connection is busy. data Cancel = Cancel   { -- | Request cancellation of the in-progress command via the handle.     cancel :: IO (Either ByteString ())@@ -319,18 +297,13 @@  -- * Connection --- | The single flat capability record: closing, inspecting, querying,--- escaping, async commands, pipelining, cancellation handle creation,--- notifications, copy, large objects, and control.------ There is exactly one 'Connection' type in the whole @pqi@ package.--- Adapter packages (e.g. @pqi-ffi@, @pqi-native@) construct values of this--- type from their own top-level @connectdb@\/@connectStart@ functions,--- closing each field over their own underlying connection representation--- (e.g. a C @PGconn@ pointer, or a native socket). This module only--- defines the shape; it does not construct any connections.+-- | One flat capability record: closing, inspecting, querying, escaping,+-- async commands, pipelining, cancellation handles, notifications, copy,+-- large objects, control. ----- See the field-level documentation for the semantics of each capability.+-- Produced by the 'connectdb'\/'connectStart'\/'newNullConnection' fields of+-- an t'Adapter', which close each field over their own connection+-- representation (a C @PGconn@ pointer, a native socket, ...). data Connection = Connection   { -- | Drive an asynchronous connection attempt forward.     connectPoll :: IO PollingStatus,@@ -366,11 +339,8 @@     protocolVersion :: IO Int,     -- | The server version, as an integer of the form @MMmmpp@.     serverVersion :: IO Int,-    -- | The most recent error message, if any.-    ---    -- Note: unlike the structured fields available via @'resultErrorField'@, the-    -- flat message text is formatted locally by the driver, so adapters are not-    -- expected to produce byte-identical strings.+    -- | The most recent error message, if any. Formatted locally by the driver,+    -- so adapters are not expected to agree byte for byte.     errorMessage :: IO (Maybe ByteString),     -- | The file descriptor of the connection socket.     socket :: IO (Maybe Fd),@@ -384,18 +354,18 @@     -- | Submit a command and wait for the result.     exec :: ByteString -> IO (Maybe Result),     -- | Submit a parameterized command. Each parameter is given as-    -- @(type oid, value, format)@, or @'Nothing'@ for SQL @NULL@. The final-    -- @'Format'@ selects the result format.+    -- @(type oid, value, format)@, or 'Nothing' for SQL @NULL@. The final+    -- 'Format' selects the result format.     execParams ::       ByteString ->       [Maybe (Word32, ByteString, Format)] ->       Format ->       IO (Maybe Result),     -- | Prepare a named statement. The OID list, when supplied, fixes parameter-    -- types; @'Nothing'@ leaves them to be inferred.+    -- types; 'Nothing' leaves them to be inferred.     prepare :: ByteString -> ByteString -> Maybe [Word32] -> IO (Maybe Result),     -- | Execute a previously prepared statement. Each parameter is-    -- @(value, format)@, or @'Nothing'@ for SQL @NULL@.+    -- @(value, format)@, or 'Nothing' for SQL @NULL@.     execPrepared ::       ByteString ->       [Maybe (ByteString, Format)] ->@@ -414,21 +384,21 @@     escapeIdentifier :: ByteString -> IO (Maybe ByteString),     -- | Submit a command without waiting for the result.     sendQuery :: ByteString -> IO Bool,-    -- | Asynchronous @'execParams'@.+    -- | Asynchronous 'execParams'.     sendQueryParams :: ByteString -> [Maybe (Word32, ByteString, Format)] -> Format -> IO Bool,-    -- | Asynchronous @'prepare'@.+    -- | Asynchronous 'prepare'.     sendPrepare :: ByteString -> ByteString -> Maybe [Word32] -> IO Bool,-    -- | Asynchronous @'execPrepared'@.+    -- | Asynchronous 'execPrepared'.     sendQueryPrepared :: ByteString -> [Maybe (ByteString, Format)] -> Format -> IO Bool,-    -- | Asynchronous @'describePrepared'@.+    -- | Asynchronous 'describePrepared'.     sendDescribePrepared :: ByteString -> IO Bool,-    -- | Asynchronous @'describePortal'@.+    -- | Asynchronous 'describePortal'.     sendDescribePortal :: ByteString -> IO Bool,     -- | Collect the next result from an asynchronous command.     getResult :: IO (Maybe Result),     -- | Read input from the server into the driver's buffer.     consumeInput :: IO Bool,-    -- | Whether a command is busy (a @'getResult'@ would block).+    -- | Whether a command is busy (a 'getResult' would block).     isBusy :: IO Bool,     -- | Set the non-blocking flag of the connection.     setnonblocking :: Bool -> IO Bool,@@ -452,17 +422,17 @@     getCancel :: IO (Maybe Cancel),     -- | Return the next notification from the queue, if any.     notifies :: IO (Maybe Notify),-    -- | Stop accumulating notices for retrieval via @'getNotice'@.+    -- | Stop accumulating notices for retrieval via 'getNotice'.     disableNoticeReporting :: IO (),-    -- | Start accumulating notices for retrieval via @'getNotice'@.+    -- | Start accumulating notices for retrieval via 'getNotice'.     enableNoticeReporting :: IO (),     -- | Retrieve the next accumulated notice, if any.     getNotice :: IO (Maybe ByteString),     -- | Send data on a @COPY FROM STDIN@ connection.     putCopyData :: ByteString -> IO CopyInResult,-    -- | Signal the end of @COPY FROM STDIN@; @'Just'@ aborts with the given error.+    -- | Signal the end of @COPY FROM STDIN@; 'Just' aborts with the given error.     putCopyEnd :: Maybe ByteString -> IO CopyInResult,-    -- | Receive data on a @COPY TO STDOUT@ connection. The @'Bool'@ selects+    -- | Receive data on a @COPY TO STDOUT@ connection. The 'Bool' selects     -- non-blocking mode.     getCopyData :: Bool -> IO CopyOutResult,     -- | Create a new large object.@@ -502,20 +472,14 @@ -- * Adapter  -- | An adapter package's connection-establishing functions, bundled into one--- value.+-- value. Each adapter package (e.g. @pqi-ffi@, @pqi-native@) exports exactly+-- one value of this type, conventionally named @adapter@. ----- 'Connection', 'Result', and 'Cancel' are per-connection and per-result;--- they carry no information about which adapter produced them, so they--- cannot themselves stand in for "the FFI adapter" or "the native adapter"--- the way a driver-parameterised type could. 'Adapter' fills that gap: it is--- the one value that names an adapter and knows how to bring a 'Connection'--- into being, so a caller that must remain adapter-agnostic (a differential--- test harness comparing two adapters, or a library that lets its users pick--- an adapter at runtime) can hold onto a single 'Adapter' value instead of a+-- This is the only value that identifies an adapter: t'Connection', t'Result'+-- and t'Cancel' say nothing about which adapter produced them. Callers that+-- must stay adapter-agnostic — a differential test harness, a library letting+-- its users pick a driver at runtime — pass an t'Adapter' around instead of a -- family of adapter-qualified top-level functions.------ Each adapter package (e.g. @pqi-ffi@, @pqi-native@) exports exactly one--- top-level value of this type, conventionally named @adapter@. data Adapter = Adapter   { -- | A short identifier for the adapter (e.g. @\"pqi-ffi\"@), for use in     -- test descriptions, logs, and error messages.@@ -528,5 +492,8 @@     newNullConnection :: IO Connection,     -- | Convert the textual representation of a @bytea@ value, as produced     -- by the server, back into raw bytes, as @PQunescapeBytea@.-    unescapeBytea :: ByteString -> IO (Maybe ByteString)+    unescapeBytea :: ByteString -> IO (Maybe ByteString),+    -- | Render an 'ExecStatus' as the string describing its status code+    -- (e.g. @\"PGRES_TUPLES_OK\"@), as @PQresStatus@.+    resStatus :: ExecStatus -> IO ByteString   }