{-# 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)