1
- {-# language RecordWildCards #-}
2
-
3
1
module Database.PostgreSQL.Protocol.Decoders
4
2
(
5
3
-- * High-lever decoder
@@ -8,24 +6,16 @@ module Database.PostgreSQL.Protocol.Decoders
8
6
, decodeAuthResponse
9
7
, decodeHeader
10
8
, decodeServerMessage
11
- -- * Helpers
12
- , parseServerVersion
13
- , parseIntegerDatetimes
14
- , parseErrorDesc
15
9
) where
16
10
17
- import Data.Monoid ((<>) )
18
- import Data.Maybe (fromMaybe )
19
11
import Data.Char (chr )
20
- import Data.Word (Word8 , Word16 , Word32 )
21
- import Text.Read (readMaybe )
22
12
import qualified Data.Vector as V
23
13
import qualified Data.ByteString as B
24
- import Data.ByteString.Char8 as BS (readInteger , readInt , unpack , pack )
25
- import qualified Data.HashMap.Strict as HM
14
+ import Data.ByteString.Char8 as BS (unpack )
26
15
27
16
import Database.PostgreSQL.Protocol.Types
28
17
import Database.PostgreSQL.Protocol.Store.Decode
18
+ import Database.PostgreSQL.Protocol.Parsers
29
19
30
20
-- | Parses and dispatches all server messages except `DataRow`.
31
21
decodeNextServerMessage
@@ -139,141 +129,6 @@ decodeFormat = getInt16BE >>= \f ->
139
129
1 -> pure Binary
140
130
_ -> fail " Unknown field format"
141
131
142
- -----------------------------
143
- -- Helper parsers that work with B.ByteString, not Decode type
144
-
145
- -- Helper to parse, not used by decoder itself
146
- parseServerVersion :: B. ByteString -> Either B. ByteString ServerVersion
147
- parseServerVersion bs =
148
- let (numbersStr, desc) = B. span isDigitDot bs
149
- numbers = readMaybe . BS. unpack <$> B. split 46 numbersStr
150
- in case numbers ++ repeat (Just 0 ) of
151
- (Just major : Just minor : Just rev : _) ->
152
- Right $ ServerVersion major minor rev desc
153
- _ -> Left $ " Unknown server version" <> bs
154
- where
155
- isDigitDot c | c == 46 = True -- dot
156
- | c >= 48 && c < 58 = True -- digits
157
- | otherwise = False
158
-
159
- -- Helper to parse, not used by decoder itself
160
- parseIntegerDatetimes :: B. ByteString -> Either B. ByteString Bool
161
- parseIntegerDatetimes bs
162
- | bs == " on" || bs == " yes" || bs == " 1" = Right True
163
- | otherwise = Right False
164
-
165
- parseCommandResult :: B. ByteString -> Either B. ByteString CommandResult
166
- parseCommandResult s =
167
- let (command, rest) = B. break (== space) s
168
- in case command of
169
- -- format: `INSERT oid rows`
170
- " INSERT" ->
171
- maybe (Left " Invalid format in INSERT command result" ) Right $ do
172
- (oid, r) <- readInteger $ B. dropWhile (== space) rest
173
- (rows, _) <- readInteger $ B. dropWhile (== space) r
174
- Just $ InsertCompleted (Oid $ fromInteger oid)
175
- (RowsCount $ fromInteger rows)
176
- " DELETE" -> DeleteCompleted <$> readRows rest
177
- " UPDATE" -> UpdateCompleted <$> readRows rest
178
- " SELECT" -> SelectCompleted <$> readRows rest
179
- " MOVE" -> MoveCompleted <$> readRows rest
180
- " FETCH" -> FetchCompleted <$> readRows rest
181
- " COPY" -> CopyCompleted <$> readRows rest
182
- _ -> Right CommandOk
183
- where
184
- space = 32
185
- readRows = maybe (Left " Invalid rows format in command result" )
186
- (pure . RowsCount . fromInteger . fst )
187
- . readInteger . B. dropWhile (== space)
188
-
189
- parseErrorNoticeFields
190
- :: B. ByteString -> Either B. ByteString (HM. HashMap Char B. ByteString )
191
- parseErrorNoticeFields = Right . HM. fromList
192
- . fmap (\ s -> (chr . fromIntegral $ B. head s, B. tail s))
193
- . filter (not . B. null ) . B. split 0
194
-
195
- parseErrorSeverity :: B. ByteString -> Either B. ByteString ErrorSeverity
196
- parseErrorSeverity bs = Right $ case bs of
197
- " ERROR" -> SeverityError
198
- " FATAL" -> SeverityFatal
199
- " PANIC" -> SeverityPanic
200
- _ -> UnknownErrorSeverity
201
-
202
- parseNoticeSeverity :: B. ByteString -> Either B. ByteString NoticeSeverity
203
- parseNoticeSeverity bs = Right $ case bs of
204
- " WARNING" -> SeverityWarning
205
- " NOTICE" -> SeverityNotice
206
- " DEBUG" -> SeverityDebug
207
- " INFO" -> SeverityInfo
208
- " LOG" -> SeverityLog
209
- _ -> UnknownNoticeSeverity
210
-
211
- parseErrorDesc :: B. ByteString -> Either B. ByteString ErrorDesc
212
- parseErrorDesc s = do
213
- hm <- parseErrorNoticeFields s
214
- errorSeverityOld <- lookupKey ' S' hm
215
- errorCode <- lookupKey ' C' hm
216
- errorMessage <- lookupKey ' M' hm
217
- -- This is identical to the S field except that the contents are
218
- -- never localized. This is present only in messages generated by
219
- -- PostgreSQL versions 9.6 and later.
220
- let errorSeverityNew = HM. lookup ' V' hm
221
- errorSeverity <- parseErrorSeverity $
222
- fromMaybe errorSeverityOld errorSeverityNew
223
- let
224
- errorDetail = HM. lookup ' D' hm
225
- errorHint = HM. lookup ' H' hm
226
- errorPosition = HM. lookup ' P' hm >>= fmap fst . readInt
227
- errorInternalPosition = HM. lookup ' p' hm >>= fmap fst . readInt
228
- errorInternalQuery = HM. lookup ' q' hm
229
- errorContext = HM. lookup ' W' hm
230
- errorSchema = HM. lookup ' s' hm
231
- errorTable = HM. lookup ' t' hm
232
- errorColumn = HM. lookup ' c' hm
233
- errorDataType = HM. lookup ' d' hm
234
- errorConstraint = HM. lookup ' n' hm
235
- errorSourceFilename = HM. lookup ' F' hm
236
- errorSourceLine = HM. lookup ' L' hm >>= fmap fst . readInt
237
- errorSourceRoutine = HM. lookup ' R' hm
238
- Right ErrorDesc {.. }
239
- where
240
- lookupKey c = maybe (Left $ " Neccessary key " <> BS. pack (show c) <>
241
- " is not presented in ErrorResponse message" )
242
- Right . HM. lookup c
243
-
244
- parseNoticeDesc :: B. ByteString -> Either B. ByteString NoticeDesc
245
- parseNoticeDesc s = do
246
- hm <- parseErrorNoticeFields s
247
- noticeSeverityOld <- lookupKey ' S' hm
248
- noticeCode <- lookupKey ' C' hm
249
- noticeMessage <- lookupKey ' M' hm
250
- -- This is identical to the S field except that the contents are
251
- -- never localized. This is present only in messages generated by
252
- -- PostgreSQL versions 9.6 and later.
253
- let noticeSeverityNew = HM. lookup ' V' hm
254
- noticeSeverity <- parseNoticeSeverity $
255
- fromMaybe noticeSeverityOld noticeSeverityNew
256
- let
257
- noticeDetail = HM. lookup ' D' hm
258
- noticeHint = HM. lookup ' H' hm
259
- noticePosition = HM. lookup ' P' hm >>= fmap fst . readInt
260
- noticeInternalPosition = HM. lookup ' p' hm >>= fmap fst . readInt
261
- noticeInternalQuery = HM. lookup ' q' hm
262
- noticeContext = HM. lookup ' W' hm
263
- noticeSchema = HM. lookup ' s' hm
264
- noticeTable = HM. lookup ' t' hm
265
- noticeColumn = HM. lookup ' c' hm
266
- noticeDataType = HM. lookup ' d' hm
267
- noticeConstraint = HM. lookup ' n' hm
268
- noticeSourceFilename = HM. lookup ' F' hm
269
- noticeSourceLine = HM. lookup ' L' hm >>= fmap fst . readInt
270
- noticeSourceRoutine = HM. lookup ' R' hm
271
- Right NoticeDesc {.. }
272
- where
273
- lookupKey c = maybe (Left $ " Neccessary key " <> BS. pack (show c) <>
274
- " is not presented in NoticeResponse message" )
275
- Right . HM. lookup c
276
-
277
132
-- | Helper to lift Either in Decode
278
133
eitherToDecode :: Either B. ByteString a -> Decode a
279
134
eitherToDecode = either (fail . BS. unpack) pure
0 commit comments