    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Database.PostgreSQL.Simple.LargeObjects
    -- Copyright   :  (c) 2011-2012 Leon P Smith
    -- License     :  BSD3
    --
    -- Maintainer  :  leon@melding-monads.com
    --
    -- Support for PostgreSQL's Large Objects;  see
    -- <https://www.postgresql.org/docs/9.5/static/largeobjects.html> for more
    -- information.
    --
    -- Note that Large Object File Descriptors are only valid within a single
    -- database transaction,  so if you are interested in using anything beyond
    -- 'loCreat', 'loCreate', and 'loUnlink',  you will need to run the entire
    -- sequence of functions in a transaction.   As 'loImport' and 'loExport'
    -- are simply C functions that call 'loCreat', 'loOpen', 'loRead', and
    -- 'loWrite',  and do not perform any transaction handling themselves,
    -- they also need to be wrapped in an explicit transaction.
    --
    -----------------------------------------------------------------------------

    module Database.PostgreSQL.Simple.LargeObjects
         ( loCreat
         , loCreate
         , loImport
         , loImportWithOid
         , loExport
         , loOpen
         , loWrite
         , loRead
         , loSeek
         , loTell
         , loTruncate
         , loClose
         , loUnlink
         , Oid(..)
         , LoFd
         , IOMode(..)
         , SeekMode(..)
         ) where

    import           Control.Applicative ((<$>))
    import           Control.Exception (throwIO)
    import qualified Data.ByteString as B
    import           Database.PostgreSQL.LibPQ (Oid(..),LoFd(..))
    import qualified Database.PostgreSQL.LibPQ as PQ
    import           Database.PostgreSQL.Simple.Internal
    import           System.IO (IOMode(..),SeekMode(..))

    liftPQ :: B.ByteString -> Connection -> (PQ.Connection -> IO (Maybe a)) -> IO a
    liftPQ :: forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    str Connection
    conn Connection -> IO (Maybe a)
    m = Connection -> (Connection -> IO a) -> IO a
    forall a. Connection -> (Connection -> IO a) -> IO a
    withConnection Connection
    conn ((Connection -> IO a) -> IO a) -> (Connection -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ \Connection
    c -> do
        res <- Connection -> IO (Maybe a)
    m Connection
    c
        case res of
          Maybe a
    Nothing -> do
              msg <- ByteString
    -> (ByteString -> ByteString) -> Maybe ByteString -> ByteString
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ByteString
    str ByteString -> ByteString
    forall a. a -> a
    id (Maybe ByteString -> ByteString)
    -> IO (Maybe ByteString) -> IO ByteString
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Connection -> IO (Maybe ByteString)
    PQ.errorMessage Connection
    c
              throwIO $ fatalError msg
          Just  a
    x -> a -> IO a
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    x

    loCreat :: Connection -> IO Oid
    loCreat :: Connection -> IO Oid
    loCreat Connection
    conn = ByteString
    -> Connection -> (Connection -> IO (Maybe Oid)) -> IO Oid
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loCreat" Connection
    conn (\Connection
    c -> Connection -> IO (Maybe Oid)
    PQ.loCreat Connection
    c)

    loCreate :: Connection -> Oid -> IO Oid
    loCreate :: Connection -> Oid -> IO Oid
    loCreate Connection
    conn Oid
    oid = ByteString
    -> Connection -> (Connection -> IO (Maybe Oid)) -> IO Oid
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loCreate" Connection
    conn (\Connection
    c -> Connection -> Oid -> IO (Maybe Oid)
    PQ.loCreate Connection
    c Oid
    oid)

    loImport :: Connection -> FilePath -> IO Oid
    loImport :: Connection -> String -> IO Oid
    loImport Connection
    conn String
    path = ByteString
    -> Connection -> (Connection -> IO (Maybe Oid)) -> IO Oid
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loImport" Connection
    conn (\Connection
    c -> Connection -> String -> IO (Maybe Oid)
    PQ.loImport Connection
    c String
    path)

    loImportWithOid :: Connection -> FilePath -> Oid -> IO Oid
    loImportWithOid :: Connection -> String -> Oid -> IO Oid
    loImportWithOid Connection
    conn String
    path Oid
    oid = ByteString
    -> Connection -> (Connection -> IO (Maybe Oid)) -> IO Oid
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loImportWithOid" Connection
    conn (\Connection
    c -> Connection -> String -> Oid -> IO (Maybe Oid)
    PQ.loImportWithOid Connection
    c String
    path Oid
    oid)

    loExport :: Connection -> Oid -> FilePath -> IO ()
    loExport :: Connection -> Oid -> String -> IO ()
    loExport Connection
    conn Oid
    oid String
    path = ByteString -> Connection -> (Connection -> IO (Maybe ())) -> IO ()
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loExport" Connection
    conn (\Connection
    c -> Connection -> Oid -> String -> IO (Maybe ())
    PQ.loExport Connection
    c Oid
    oid String
    path)

    loOpen :: Connection -> Oid -> IOMode -> IO LoFd
    loOpen :: Connection -> Oid -> IOMode -> IO LoFd
    loOpen Connection
    conn Oid
    oid IOMode
    mode  = ByteString
    -> Connection -> (Connection -> IO (Maybe LoFd)) -> IO LoFd
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loOpen" Connection
    conn (\Connection
    c -> Connection -> Oid -> IOMode -> IO (Maybe LoFd)
    PQ.loOpen Connection
    c Oid
    oid IOMode
    mode )

    loWrite :: Connection -> LoFd -> B.ByteString -> IO Int
    loWrite :: Connection -> LoFd -> ByteString -> IO Int
    loWrite Connection
    conn LoFd
    fd ByteString
    dat = ByteString
    -> Connection -> (Connection -> IO (Maybe Int)) -> IO Int
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loWrite" Connection
    conn (\Connection
    c -> Connection -> LoFd -> ByteString -> IO (Maybe Int)
    PQ.loWrite Connection
    c LoFd
    fd ByteString
    dat)

    loRead :: Connection -> LoFd -> Int -> IO B.ByteString
    loRead :: Connection -> LoFd -> Int -> IO ByteString
    loRead Connection
    conn LoFd
    fd Int
    maxlen = ByteString
    -> Connection
    -> (Connection -> IO (Maybe ByteString))
    -> IO ByteString
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loRead" Connection
    conn (\Connection
    c -> Connection -> LoFd -> Int -> IO (Maybe ByteString)
    PQ.loRead Connection
    c LoFd
    fd Int
    maxlen)

    loSeek :: Connection -> LoFd -> SeekMode -> Int -> IO Int
    loSeek :: Connection -> LoFd -> SeekMode -> Int -> IO Int
    loSeek Connection
    conn LoFd
    fd SeekMode
    seekmode Int
    offset = ByteString
    -> Connection -> (Connection -> IO (Maybe Int)) -> IO Int
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loSeek" Connection
    conn (\Connection
    c -> Connection -> LoFd -> SeekMode -> Int -> IO (Maybe Int)
    PQ.loSeek Connection
    c LoFd
    fd SeekMode
    seekmode Int
    offset)

    loTell :: Connection -> LoFd -> IO Int
    loTell :: Connection -> LoFd -> IO Int
    loTell Connection
    conn LoFd
    fd = ByteString
    -> Connection -> (Connection -> IO (Maybe Int)) -> IO Int
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loTell" Connection
    conn (\Connection
    c -> Connection -> LoFd -> IO (Maybe Int)
    PQ.loTell Connection
    c LoFd
    fd)

    loTruncate :: Connection -> LoFd -> Int -> IO ()
    loTruncate :: Connection -> LoFd -> Int -> IO ()
    loTruncate Connection
    conn LoFd
    fd Int
    len = ByteString -> Connection -> (Connection -> IO (Maybe ())) -> IO ()
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loTruncate" Connection
    conn (\Connection
    c -> Connection -> LoFd -> Int -> IO (Maybe ())
    PQ.loTruncate Connection
    c LoFd
    fd Int
    len)

    loClose :: Connection -> LoFd -> IO ()
    loClose :: Connection -> LoFd -> IO ()
    loClose Connection
    conn LoFd
    fd = ByteString -> Connection -> (Connection -> IO (Maybe ())) -> IO ()
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loClose" Connection
    conn (\Connection
    c -> Connection -> LoFd -> IO (Maybe ())
    PQ.loClose Connection
    c LoFd
    fd)

    loUnlink :: Connection -> Oid -> IO ()
    loUnlink :: Connection -> Oid -> IO ()
    loUnlink Connection
    conn Oid
    oid = ByteString -> Connection -> (Connection -> IO (Maybe ())) -> IO ()
    forall a.
    ByteString -> Connection -> (Connection -> IO (Maybe a)) -> IO a
    liftPQ ByteString
    "loUnlink" Connection
    conn (\Connection
    c -> Connection -> Oid -> IO (Maybe ())
    PQ.loUnlink Connection
    c Oid
    oid)