-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathPQResultUtils.hs
More file actions
124 lines (113 loc) · 5.02 KB
/
PQResultUtils.hs
File metadata and controls
124 lines (113 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
{-# LANGUAGE CPP #-}
------------------------------------------------------------------------------
-- |
-- Module: Database.PostgreSQL.Simple.Internal.PQResultUtils
-- Copyright: (c) 2011 MailRank, Inc.
-- (c) 2011-2012 Leon P Smith
-- License: BSD3
-- Maintainer: Leon P Smith <leon@melding-monads.com>
-- Stability: experimental
--
------------------------------------------------------------------------------
module Database.PostgreSQL.Simple.Internal.PQResultUtils
( finishQueryWith
, finishQueryWithV
, finishQueryWithVU
, getRowWith
) where
import Control.Exception as E
import Data.ByteString (ByteString)
import Data.Foldable (for_)
import Database.PostgreSQL.Simple.FromField (ResultError(..))
import Database.PostgreSQL.Simple.Ok
import Database.PostgreSQL.Simple.Types (Query(..))
import Database.PostgreSQL.Simple.Internal as Base hiding (result, row)
import Database.PostgreSQL.Simple.TypeInfo
import qualified Database.PostgreSQL.LibPQ as PQ
import qualified Data.ByteString.Char8 as B
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as MVU
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Strict
finishQueryWith :: RowParser r -> Connection -> Query -> PQ.Result -> IO [r]
finishQueryWith parser conn q result = finishQueryWith' q result $ do
nrows <- PQ.ntuples result
ncols <- PQ.nfields result
forM' 0 (nrows-1) $ \row ->
getRowWith parser row ncols conn result
finishQueryWithV :: RowParser r -> Connection -> Query -> PQ.Result -> IO (V.Vector r)
finishQueryWithV parser conn q result = finishQueryWith' q result $ do
nrows <- PQ.ntuples result
let PQ.Row nrows' = nrows
ncols <- PQ.nfields result
mv <- MV.unsafeNew (fromIntegral nrows')
for_ [ 0 .. nrows-1 ] $ \row -> do
let PQ.Row row' = row
value <- getRowWith parser row ncols conn result
MV.unsafeWrite mv (fromIntegral row') value
V.unsafeFreeze mv
finishQueryWithVU :: VU.Unbox r => RowParser r -> Connection -> Query -> PQ.Result -> IO (VU.Vector r)
finishQueryWithVU parser conn q result = finishQueryWith' q result $ do
nrows <- PQ.ntuples result
let PQ.Row nrows' = nrows
ncols <- PQ.nfields result
mv <- MVU.unsafeNew (fromIntegral nrows')
for_ [ 0 .. nrows-1 ] $ \row -> do
let PQ.Row row' = row
value <- getRowWith parser row ncols conn result
MVU.unsafeWrite mv (fromIntegral row') value
VU.unsafeFreeze mv
finishQueryWith' :: Query -> PQ.Result -> IO a -> IO a
finishQueryWith' q result k = do
status <- PQ.resultStatus result
case status of
PQ.TuplesOk -> k
PQ.EmptyQuery -> queryErr "query: Empty query"
PQ.CommandOk -> queryErr "query resulted in a command response (did you mean to use `execute` or forget a RETURNING?)"
PQ.CopyOut -> queryErr "query: COPY TO is not supported"
PQ.CopyIn -> queryErr "query: COPY FROM is not supported"
PQ.CopyBoth -> queryErr "query: COPY BOTH is not supported"
PQ.SingleTuple -> queryErr "query: single-row mode is not supported"
PQ.BadResponse -> throwResultError "query" result status
PQ.NonfatalError -> throwResultError "query" result status
PQ.FatalError -> throwResultError "query" result status
_ -> throwResultError "query: TODO" result status
where
queryErr msg = throwIO $ QueryError msg q
getRowWith :: RowParser r -> PQ.Row -> PQ.Column -> Connection -> PQ.Result -> IO r
getRowWith parser row ncols conn result = do
let rw = Row row result
let unCol (PQ.Col x) = fromIntegral x :: Int
okvc <- runConversion (runStateT (runReaderT (unRP parser) rw) 0) conn
case okvc of
Ok (val,col) | col == ncols -> return val
| otherwise -> do
vals <- forM' 0 (ncols-1) $ \c -> do
tinfo <- getTypeInfo conn =<< PQ.ftype result c
v <- PQ.getvalue result row c
return ( tinfo
, fmap ellipsis v )
throw (ConversionFailed
(show (unCol ncols) ++ " values: " ++ show vals)
Nothing
""
(show (unCol col) ++ " slots in target type")
"mismatch between number of columns to convert and number in target type")
Errors [] -> throwIO $ ConversionFailed "" Nothing "" "" "unknown error"
Errors [x] -> throwIO x
Errors xs -> throwIO $ ManyErrors xs
ellipsis :: ByteString -> ByteString
ellipsis bs
| B.length bs > 15 = B.take 10 bs `B.append` "[...]"
| otherwise = bs
forM' :: (Ord n, Num n) => n -> n -> (n -> IO a) -> IO [a]
forM' lo hi m = loop hi []
where
loop !n !as
| n < lo = return as
| otherwise = do
a <- m n
loop (n-1) (a:as)
{-# INLINE forM' #-}