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 +6/−0
- README.md +3/−4
- pqi.cabal +1/−1
- src/library/Pqi.hs +76/−109
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 }