preql-0.1: src/Preql/PQResultUtils.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | taken verbatim from Database.PostgreSQL.Simple.Internal.PQResultUtils
module Preql.PQResultUtils where
import Control.Exception as E
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Strict
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Foldable (for_)
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 qualified Database.PostgreSQL.LibPQ as PQ
import Database.PostgreSQL.Simple.FromField (ResultError (..))
import Database.PostgreSQL.Simple.Internal as Base hiding (result,
row)
import Database.PostgreSQL.Simple.Ok
import Database.PostgreSQL.Simple.TypeInfo
import Database.PostgreSQL.Simple.Types (Query (..))
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"
#if MIN_VERSION_postgresql_libpq(0,9,3)
PQ.CopyBoth -> queryErr "query: COPY BOTH is not supported"
#endif
#if MIN_VERSION_postgresql_libpq(0,9,2)
PQ.SingleTuple -> queryErr "query: single-row mode is not supported"
#endif
PQ.BadResponse -> throwResultError "query" result status
PQ.NonfatalError -> throwResultError "query" result status
PQ.FatalError -> throwResultError "query" 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' #-}