preql-0.5: src/Preql/Wire/Decode.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Decoding values from Postgres wire format to Haskell.
module Preql.Wire.Decode where
import Preql.Wire.Errors
import Preql.Wire.Internal
import Control.Exception (try)
import Control.Monad.Except
import Data.IORef (newIORef)
import GHC.TypeNats
import Preql.Imports
import qualified Data.Vector as V
import qualified Data.Vector.Sized as VS
import qualified Database.PostgreSQL.LibPQ as PQ
decodeVector :: KnownNat n =>
(PgType -> IO (Either QueryError PQ.Oid)) -> RowDecoder n a -> PQ.Result -> IO (Either QueryError (Vector a))
decodeVector lookupType rd@(RowDecoder pgtypes _parsers) result = do
mismatches <- fmap (catMaybes . VS.toList) $ for (VS.zip (VS.enumFromN 0) pgtypes) $ \(column@(PQ.Col cint), expected) -> do
actual <- PQ.ftype result column
e_expectedOid <- lookupType expected
case e_expectedOid of
Right oid | actual == oid -> return Nothing
_ -> do
m_name <- liftIO $ PQ.fname result column
let columnName = decodeUtf8With lenientDecode <$> m_name
return $ Just (TypeMismatch{column = fromIntegral cint, ..})
if not (null mismatches)
then return (Left (PgTypeMismatch mismatches))
else do
(PQ.Row ntuples) <- liftIO $ PQ.ntuples result
ref <- newIORef (DecoderState result 0 0)
fmap (first DecoderError) . try $
V.replicateM (fromIntegral ntuples) (decodeRow ref rd result)