@@ -0,0 +1,155 @@
3+------------------------------------------------------------------------------
5+-- Module: Database.PostgreSQL.Simple.Copy
6+-- Copyright: (c) 2013 Leon P Smith
8+-- Maintainer: Leon P Smith <leon@melding-monads.com>
9+-- Stability: experimental
11+-- mid-level support for COPY IN and COPY OUT.
13+------------------------------------------------------------------------------
15+module Database.PostgreSQL.Simple.Copy
24+import Control.Applicative
25+import Control.Concurrent ( threadWaitRead, threadWaitWrite )
26+import Control.Exception ( throwIO )
27+import qualified Data.Attoparsec.ByteString.Char8 as P
28+import Data.Int(Int64)
29+import qualified Data.ByteString.Char8 as B
30+import qualified Database.PostgreSQL.LibPQ as PQ
31+import Database.PostgreSQL.Simple hiding
32+ ( fold, fold_, forEach, forEach_ )
33+import Database.PostgreSQL.Simple.Types
34+import Database.PostgreSQL.Simple.Internal
36+copy :: ( ToRow params ) => Connection -> Query -> params -> IO ()
37+copy conn template qs = do
38+ q <- formatQuery conn template qs
39+ doCopy "Database.PostgreSQL.Simple.Copy.copy" conn template q
41+copy_ :: Connection -> Query -> IO ()
42+copy_ conn (Query q) = do
43+ doCopy "Database.PostgreSQL.Simple.Copy.copy_" conn (Query q) q
45+doCopy :: B.ByteString -> Connection -> Query -> B.ByteString -> IO ()
46+doCopy funcName conn template q = do
47+ result <- exec conn q
48+ status <- PQ.resultStatus result
49+ let err = throwIO $ QueryError
50+ (B.unpack funcName ++ " " ++ show status)
56+ PQ.CopyOut -> return ()
57+ PQ.CopyIn -> return ()
58+ PQ.BadResponse -> throwResultError funcName result status
59+ PQ.NonfatalError -> throwResultError funcName result status
60+ PQ.FatalError -> throwResultError funcName result status
64+ = CopyOutRow !B.ByteString -- ^ Data representing exactly one row
66+ | CopyOutDone {-# UNPACK #-} !Int64 -- ^ No more rows, and a count of the
67+ -- number of rows returned.
69+getCopyData :: Connection -> IO CopyOutResult
70+getCopyData conn = withConnection conn loop
72+ funcName = "Database.PostgreSQL.Simple.Copy.getCopyData"
73+ errCmdStatus = B.unpack funcName ++ ": failed to fetch command status"
74+ errCmdStatusFmt = B.unpack funcName ++ ": failed to parse command status"
76+#if defined(mingw32_HOST_OS)
77+ row <- PQ.getCopyData pqconn False
79+ row <- PQ.getCopyData pqconn True
82+ PQ.CopyOutRow rowdata -> return (CopyOutRow rowdata)
84+ result <- maybe (fail errCmdStatus) return =<< PQ.getResult pqconn
85+ cmdStat <- maybe (fail errCmdStatus) return =<< PQ.cmdStatus result
86+ let rowCount = P.string "COPY " *> P.decimal
87+ case P.parseOnly rowCount cmdStat of
88+ Left _ -> fail errCmdStatusFmt
89+ Right n -> return (CopyOutDone n)
90+#if defined(mingw32_HOST_OS)
91+ PQ.CopyOutWouldBlock -> do
92+ fail (B.unpack funcName ++ ": the impossible happened")
94+ PQ.CopyOutWouldBlock -> do
95+ mfd <- PQ.socket pqconn
97+ Nothing -> throwIO (fdError funcName)
100+ _ <- PQ.consumeInput pqconn
103+ PQ.CopyOutError -> do
104+ mmsg <- PQ.errorMessage pqconn
107+ sqlExecStatus = FatalError,
108+ sqlErrorMsg = maybe "" id mmsg,
109+ sqlErrorDetail = "",
110+ sqlErrorHint = funcName
113+putCopyData :: Connection -> B.ByteString -> IO ()
114+putCopyData conn dat =
115+ doCopyIn "Database.PostgreSQL.Simple.Copy.putCopyData"
116+ (\c -> PQ.putCopyData c dat)
119+putCopyEnd :: Connection -> IO ()
121+ doCopyIn "Database.PostgreSQL.Simple.Copy.putCopyEnd"
122+ (\c -> PQ.putCopyEnd c Nothing)
125+putCopyError :: Connection -> B.ByteString -> IO ()
126+putCopyError conn err = do
127+ doCopyIn "Database.PostgreSQL.Simple.Copy.putCopyError"
128+ (\c -> PQ.putCopyEnd c (Just err))
131+doCopyIn :: B.ByteString -> (PQ.Connection -> IO PQ.CopyInResult)
132+ -> Connection -> IO ()
133+doCopyIn funcName action conn = withConnection conn loop
136+ stat <- action pqconn
138+ PQ.CopyInOk -> return ()
139+ PQ.CopyInError -> do
140+ mmsg <- PQ.errorMessage pqconn
143+ sqlExecStatus = FatalError,
144+ sqlErrorMsg = maybe "" id mmsg,
145+ sqlErrorDetail = "",
146+ sqlErrorHint = funcName
148+ PQ.CopyInWouldBlock -> do
149+ mfd <- PQ.socket pqconn
151+ Nothing -> throwIO (fdError funcName)
155+{-# INLINE doCopyIn #-}