packages feed

pqi-native 1.0.1.4 → 1.0.1.5

raw patch · 3 files changed

+70/−39 lines, 3 filesdep ~pqi-conformancePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: pqi-conformance

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+# v1.0.1.5++## Fixes++- `sendQueryParams`, `execParams`, `sendPrepare`, `prepare`, `sendQueryPrepared`, and `execPrepared` now reject a parameter list (or, for `prepare`\/`sendPrepare`, a parameter-type list) longer than 65535 locally, without writing anything to the socket, mirroring `libpq`'s `PQ_QUERY_PARAM_MAX_LIMIT` check. The `Parse`\/`Bind` messages encode their parameter count as a 16-bit field; past the limit `fromIntegral` silently wrapped it (65536 became 0), producing a malformed message the server rejected with `invalid message format` and leaving the connection desynchronized - inside a pipeline, every command dispatched before the failing one was left with its results undrained. Caught by the differential coverage added in `pqi-conformance` 1.0.5.0. Found via `hasql` issue #326.+ # v1.0.1.4  ## Fixes
pqi-native.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-native-version: 1.0.1.4+version: 1.0.1.5 category: Database, PostgreSQL synopsis: Native (pure-Haskell) adapter for pqi description:@@ -158,5 +158,5 @@   build-depends:     base >=4.11 && <5,     hspec >=2.11 && <2.12,-    pqi-conformance ^>=1.0.4,+    pqi-conformance ^>=1.0.5,     pqi-native,
src/library/Pqi/Native/Query.hs view
@@ -59,6 +59,19 @@     <> describePortalMessage ""     <> executeMessage "" 0 +-- * Parameter-count limit++-- | The largest parameter count the wire protocol's 16-bit count fields can+-- carry, matching libpq's @PQ_QUERY_PARAM_MAX_LIMIT@. 'Parse' and 'Bind'+-- messages encode their parameter count as an 'Int16'; past this limit that+-- encoding wraps around instead of overflowing, so the count must be+-- rejected locally before it corrupts the message.+maxParamCount :: Int+maxParamCount = 65535++tooManyParams :: [a] -> Bool+tooManyParams = (> maxParamCount) . length+ -- * Synchronous flows  -- | Simple query. Returns the last result, mirroring @PQexec@.@@ -69,21 +82,27 @@  -- | Parameterized query via the extended protocol. execParams :: Connection -> ByteString -> [Maybe (Word32, ByteString, Format)] -> Format -> IO (Maybe NativeResult)-execParams connection sql params resultFormat = withReady connection do-  sendMessage connection (paramsWrite sql params resultFormat)-  Just <$> collectExtended connection sql+execParams connection sql params resultFormat+  | tooManyParams params = pure Nothing+  | otherwise = withReady connection do+      sendMessage connection (paramsWrite sql params resultFormat)+      Just <$> collectExtended connection sql  -- | Prepare a named statement. prepare :: Connection -> ByteString -> ByteString -> Maybe [Word32] -> IO (Maybe NativeResult)-prepare connection name sql parameterTypes = withReady connection do-  sendMessage connection (prepareWrite name sql parameterTypes)-  Just <$> collectExtended connection sql+prepare connection name sql parameterTypes+  | maybe False tooManyParams parameterTypes = pure Nothing+  | otherwise = withReady connection do+      sendMessage connection (prepareWrite name sql parameterTypes)+      Just <$> collectExtended connection sql  -- | Execute a previously prepared statement. execPrepared :: Connection -> ByteString -> [Maybe (ByteString, Format)] -> Format -> IO (Maybe NativeResult)-execPrepared connection name params resultFormat = withReady connection do-  sendMessage connection (preparedWrite name params resultFormat)-  Just <$> collectExtended connection ""+execPrepared connection name params resultFormat+  | tooManyParams params = pure Nothing+  | otherwise = withReady connection do+      sendMessage connection (preparedWrite name params resultFormat)+      Just <$> collectExtended connection ""  -- * Asynchronous flows @@ -112,38 +131,44 @@ -- Extended query: include Sync when not in pipeline mode; omit Sync in -- pipeline mode (the caller drives sync boundaries via 'pipelineSync'). sendQueryParams :: Connection -> ByteString -> [Maybe (Word32, ByteString, Format)] -> Format -> IO Bool-sendQueryParams connection sql params resultFormat = do-  pipeline <- inPipeline connection-  ok <--    sendAsync connection sql-      $ if pipeline-        then asyncParamsWrite sql params resultFormat-        else paramsWrite sql params resultFormat-  -- 'sendQueryParams' drives the extended protocol and so always sends an-  -- unnamed @Parse@, whose @ParseComplete@ must fold into this command's own-  -- result (like 'sendQueryPrepared'). Record its origin so it is not mistaken-  -- for the terminal @ParseComplete@ of a pipelined 'sendPrepare'.-  when (ok && pipeline) $ modifyIORef' (pendingParseOrigins connection) (Seq.|> False)-  pure ok+sendQueryParams connection sql params resultFormat+  | tooManyParams params = pure False+  | otherwise = do+      pipeline <- inPipeline connection+      ok <-+        sendAsync connection sql+          $ if pipeline+            then asyncParamsWrite sql params resultFormat+            else paramsWrite sql params resultFormat+      -- 'sendQueryParams' drives the extended protocol and so always sends an+      -- unnamed @Parse@, whose @ParseComplete@ must fold into this command's own+      -- result (like 'sendQueryPrepared'). Record its origin so it is not mistaken+      -- for the terminal @ParseComplete@ of a pipelined 'sendPrepare'.+      when (ok && pipeline) $ modifyIORef' (pendingParseOrigins connection) (Seq.|> False)+      pure ok  sendPrepare :: Connection -> ByteString -> ByteString -> Maybe [Word32] -> IO Bool-sendPrepare connection name sql parameterTypes = do-  pipeline <- inPipeline connection-  ok <--    sendAsync connection sql-      $ if pipeline-        then parseMessage name sql (fromMaybe [] parameterTypes)-        else prepareWrite name sql parameterTypes-  when (ok && pipeline) $ modifyIORef' (pendingParseOrigins connection) (Seq.|> True)-  pure ok+sendPrepare connection name sql parameterTypes+  | maybe False tooManyParams parameterTypes = pure False+  | otherwise = do+      pipeline <- inPipeline connection+      ok <-+        sendAsync connection sql+          $ if pipeline+            then parseMessage name sql (fromMaybe [] parameterTypes)+            else prepareWrite name sql parameterTypes+      when (ok && pipeline) $ modifyIORef' (pendingParseOrigins connection) (Seq.|> True)+      pure ok  sendQueryPrepared :: Connection -> ByteString -> [Maybe (ByteString, Format)] -> Format -> IO Bool-sendQueryPrepared connection name params resultFormat = do-  pipeline <- inPipeline connection-  sendAsync connection ""-    $ if pipeline-      then asyncPreparedWrite name params resultFormat-      else preparedWrite name params resultFormat+sendQueryPrepared connection name params resultFormat+  | tooManyParams params = pure False+  | otherwise = do+      pipeline <- inPipeline connection+      sendAsync connection ""+        $ if pipeline+          then asyncPreparedWrite name params resultFormat+          else preparedWrite name params resultFormat  sendDescribePrepared :: Connection -> ByteString -> IO Bool sendDescribePrepared connection name = do