postgresql-libpq 0.5 → 0.6
raw patch · 2 files changed
+124/−42 lines, 2 files
Files
- Database/PostgreSQL/LibPQ.hsc +115/−33
- postgresql-libpq.cabal +9/−9
Database/PostgreSQL/LibPQ.hsc view
@@ -14,8 +14,8 @@ -- server and to receive the results of these queries. -- -- This is intended to be a very low-level interface to libpq. It--- provides memory management and a somewhat more consistent interface --- to error conditions. Application code should typically use a +-- provides memory management and a somewhat more consistent interface+-- to error conditions. Application code should typically use a -- higher-level PostgreSQL binding. -- -- This interface is not safe, because libpq unfortunately conflates@@ -177,6 +177,8 @@ , trace , untrace + -- * Large Objects+ -- $largeobjects , LoFd(..) , loCreat , loCreate@@ -2041,6 +2043,8 @@ -- where -- finalizer = touchForeignPtr fp +-- $largeobjects+ -- | LoFd is a Large Object (pseudo) File Descriptor. It is understood by -- libpq but not by operating system calls. @@ -2049,41 +2053,91 @@ loMode :: IOMode -> CInt loMode mode = case mode of ReadMode -> (#const INV_READ)- _ -> (#const INV_READ) .|. (#const INV_WRITE)+ WriteMode -> (#const INV_WRITE)+ ReadWriteMode -> (#const INV_READ) .|. (#const INV_WRITE)+ AppendMode -> (#const INV_WRITE) toMaybeOid :: Oid -> Maybe Oid toMaybeOid oid | oid == invalidOid = Nothing | otherwise = Just oid {-# INLINE toMaybeOid #-} +nonnegInt :: CInt -> Maybe Int+nonnegInt x = if x < 0 then Nothing else Just (fromIntegral x)+{-# INLINE nonnegInt #-}++negError :: CInt -> Maybe ()+negError x = if x < 0 then Nothing else Just ()+{-# INLINE negError #-}++-- | Creates a new large object, returns the Object ID of the newly created+-- object.+ loCreat :: Connection -> IO (Maybe Oid) loCreat connection = withConn connection $ \c -> do toMaybeOid `fmap` c_lo_creat c (loMode ReadMode) +-- | Creates a new large object with a particular Object ID. Returns+-- 'Nothing' if the requested Object ID is already in use by some other+-- large object or other failure. If 'invalidOid' is used as a parameter,+-- then 'loCreate' will assign an unused 'Oid'.+ loCreate :: Connection -> Oid -> IO (Maybe Oid) loCreate connection oid = withConn connection $ \c -> do toMaybeOid `fmap` c_lo_create c oid +-- | Imports an operating system file as a large object. Note that the+-- file is read by the client interface library, not by the server; so it+-- must exist in the client file system and be readable by the client+-- application.+ loImport :: Connection -> FilePath -> IO (Maybe Oid) loImport connection filepath = withConn connection $ \c -> do withCString filepath $ \f -> do toMaybeOid `fmap` c_lo_import c f +-- | Imports an operating system file as a large object with the given+-- Object ID. Combines the behavior of 'loImport' and 'loCreate'+ loImportWithOid :: Connection -> FilePath -> Oid -> IO (Maybe Oid) loImportWithOid connection filepath oid = withConn connection $ \c -> do withCString filepath $ \f -> do toMaybeOid `fmap` c_lo_import_with_oid c f oid -loExport :: Connection -> Oid -> FilePath -> IO CInt+-- | Exports a large object into a operating system file. Note that+-- the file is written by the client interface library, not the server.+-- Returns 'Just ()' on success, 'Nothing' on failure.++loExport :: Connection -> Oid -> FilePath -> IO (Maybe ()) loExport connection oid filepath = withConn connection $ \c -> do withCString filepath $ \f -> do- c_lo_export c oid f+ negError `fmap` c_lo_export c oid f +-- | Opens an existing large object for reading or writing. The Oid specifies+-- the large object to open. A large object cannot be opened before it is+-- created. A large object descriptor is returned for later use in 'loRead',+-- 'loWrite', 'loSeek', 'loTell', and 'loClose'. The descriptor is only valid+-- for the duration of the current transation. On failure, 'Nothing' is+-- returned.+--+-- The server currently does not distinguish between 'WriteMode' and+-- 'ReadWriteMode'; write-only modes are not enforced. However there+-- is a significant difference between 'ReadMode' and the other modes:+-- with 'ReadMode' you cannot write on the descriptor, and the data read+-- from it will reflect the contents of the large object at the time of+-- the transaction snapshot that was active when 'loOpen' was executed,+-- regardless of later writes by this or other transactions. Reading from+-- a descriptor opened in 'WriteMode', 'ReadWriteMode', or 'AppendMode'+-- returns data that reflects all writes of other committed transactions+-- as well as the writes of the current transaction. This is similar to+-- the behavior of @REPEATABLE READ@ versus @READ COMMITTED@ transaction+-- modes for ordinary SQL @SELECT@ commands.+ loOpen :: Connection -> Oid -> IOMode -> IO (Maybe LoFd) loOpen connection oid mode = withConn connection $ \c -> do@@ -2095,9 +2149,10 @@ -- The Large Object API does not directly support AppendMode, -- so we emulate it. - -- FIXME: review this emulation as it and/or the error handling is- -- likely to be slightly wrong. Start by reading the source- -- of lo_open, lo_lseek, and lo_close.+ -- FIXME: review this emulation as it and/or the error+ -- handling is likely to be slightly wrong. Start by+ -- reading the source of lo_open, lo_lseek, and+ -- lo_close. err <- c_lo_lseek c fd 0 (#const SEEK_END) case err of -1 -> do@@ -2110,56 +2165,83 @@ return Nothing _ -> return (Just (LoFd fd)) +-- | @loWrite conn fd buf@ writes the bytestring @buf@ to the large object+-- descriptor @fd@. The number of bytes actually written is returned.+-- In the event of an error, 'Nothing' is returned. -loWrite :: Connection -> LoFd -> B.ByteString -> IO Int+loWrite :: Connection -> LoFd -> B.ByteString -> IO (Maybe Int) loWrite connection (LoFd fd) bytes = withConn connection $ \c -> do B.unsafeUseAsCStringLen bytes $ \(byteptr,len) -> do- nbytes_written <- c_lo_write c fd byteptr (fromIntegral len)- return (fromIntegral nbytes_written)+ nonnegInt `fmap` c_lo_write c fd byteptr (fromIntegral len) -loRead :: Connection -> LoFd -> Int -> IO B.ByteString-loRead connection (LoFd fd) maxlen+-- | @loRead conn fd len@ reads up to @len@ bytes from the large object+-- descriptor @fd@. In the event of an error, 'Nothing' is returned.++loRead :: Connection -> LoFd -> Int -> IO (Maybe B.ByteString)+loRead connection (LoFd !fd) !maxlen = withConn connection $ \c -> do- allocaBytes maxlen $ \(buf :: CString) -> do- len <- c_lo_read c fd buf (fromIntegral maxlen)- B.packCStringLen (buf,fromIntegral len)+ buf <- mallocBytes maxlen+ len_ <- c_lo_read c fd buf (fromIntegral maxlen)+ let len = fromIntegral len_+ if len < 0+ then do+ free buf+ return Nothing+ else do+ bufre <- reallocBytes buf len+ buffp <- newForeignPtr finalizerFree bufre+ return (Just (B.fromForeignPtr buffp 0 len)) -loSeek :: Connection -> LoFd -> SeekMode -> Int -> IO Bool+-- | Changes the current read or write location associated with+-- a large object descriptor. The return value is the new location+-- pointer, or 'Nothing' on error.++loSeek :: Connection -> LoFd -> SeekMode -> Int -> IO (Maybe Int) loSeek connection (LoFd fd) seekmode delta = withConn connection $ \c -> do let d = fromIntegral delta- err <- c_lo_lseek c fd d $ case seekmode of+ pos <- c_lo_lseek c fd d $ case seekmode of AbsoluteSeek -> #const SEEK_SET RelativeSeek -> #const SEEK_CUR SeekFromEnd -> #const SEEK_END- return (err >= 0)+ return (nonnegInt pos) +-- | Obtains the current read or write location of a large object descriptor.+ loTell :: Connection -> LoFd -> IO (Maybe Int) loTell connection (LoFd fd) = withConn connection $ \c -> do- pos <- c_lo_tell c fd- if pos >= 0- then return (Just (fromIntegral pos))- else return Nothing+ nonnegInt `fmap` c_lo_tell c fd -loTruncate :: Connection -> LoFd -> Int -> IO Bool+-- | Truncates a large object to a given length. If the length is greater+-- than the current large object, then the large object is extended with+-- null bytes. ('\x00')+--+-- The file offest is not changed.+--+-- 'loTruncate' is new as of PostgreSQL 8.3; if this function is run against+-- an older server version, it will fail and return 'Nothing'++loTruncate :: Connection -> LoFd -> Int -> IO (Maybe ()) loTruncate connection (LoFd fd) size = withConn connection $ \c -> do- err <- c_lo_truncate c fd (fromIntegral size)- return (err >= 0)+ negError `fmap` c_lo_truncate c fd (fromIntegral size) -loClose :: Connection -> LoFd -> IO Bool+-- | Closes a large object descriptor. Any large object descriptors that+-- remain open at the end of a transaction will be closed automatically.++loClose :: Connection -> LoFd -> IO (Maybe ()) loClose connection (LoFd fd) = withConn connection $ \c -> do- err <- c_lo_close c fd- return (err >= 0)+ negError `fmap` c_lo_close c fd -loUnlink :: Connection -> Oid -> IO Bool+-- | Removes a large object from the database.++loUnlink :: Connection -> Oid -> IO (Maybe ()) loUnlink connection oid = withConn connection $ \c -> do- err <- c_lo_unlink c oid- return (err >= 0)+ negError `fmap` c_lo_unlink c oid foreign import ccall "libpq-fe.h PQconnectdb" c_PQconnectdb :: CString ->IO (Ptr PGconn)@@ -2453,7 +2535,7 @@ c_lo_write :: Ptr PGconn -> CFd -> CString -> CSize -> IO CInt foreign import ccall "libpq-fs.h lo_read"- c_lo_read :: Ptr PGconn -> CFd -> CString -> CSize -> IO CInt+ c_lo_read :: Ptr PGconn -> CFd -> Ptr Word8 -> CSize -> IO CInt foreign import ccall "libpq-fs.h lo_lseek" c_lo_lseek :: Ptr PGconn -> CFd -> CInt -> CInt -> IO CInt
postgresql-libpq.cabal view
@@ -1,6 +1,6 @@ Name: postgresql-libpq-Version: 0.5-Synopsis: libpq binding for Haskell+Version: 0.6+Synopsis: low-level binding to libpq Description: This is a binding to libpq: the C application programmer's interface to PostgreSQL. libpq is a@@ -9,12 +9,13 @@ backend server and to receive the results of these queries. -Homepage: http://github.com/lpsmith/haskell-libpq+Homepage: http://github.com/lpsmith/postgresql-libpq License: BSD3 License-file: LICENSE Author: Grant Monroe, Leon P Smith-Maintainer: leon@melding-monads.com--- Copyright:+Maintainer: Leon P Smith <leon@melding-monads.com>+Copyright: (c) 2010 Grant Monroe+ (c) 2011 Leon P Smith Category: Database Build-type: Custom -- Extra-source-files:@@ -33,10 +34,9 @@ source-repository head type: git- location: http://github.com/lpsmith/haskell-libpq+ location: http://github.com/lpsmith/postgresql-libpq source-repository this type: git- location: http://github.com/lpsmith/haskell-libpq- tag: v0.5-+ location: http://github.com/lpsmith/postgresql-libpq+ tag: v0.6