@@ -25,6 +25,10 @@ import Control.Applicative
25import Control.Exception
25import Control.Exception
26import Control.Concurrent.MVar
26import Control.Concurrent.MVar
27import Data.ByteString(ByteString)
27import Data.ByteString(ByteString)
28+import qualified Data.ByteString as B
29+import qualified Data.ByteString.Char8 as B8
30+import Data.Char (ord)
31+import Data.Int (Int64)
28import qualified Data.IntMap as IntMap
32import qualified Data.IntMap as IntMap
29import Data.String
33import Data.String
30import Data.Typeable
34import Data.Typeable
@@ -33,6 +37,7 @@ import Database.PostgreSQL.LibPQ(Oid(..))
33import qualified Database.PostgreSQL.LibPQ as PQ
37import qualified Database.PostgreSQL.LibPQ as PQ
34import Database.PostgreSQL.Simple.BuiltinTypes (BuiltinType)
38import Database.PostgreSQL.Simple.BuiltinTypes (BuiltinType)
35import Database.PostgreSQL.Simple.Ok
39import Database.PostgreSQL.Simple.Ok
40+import Database.PostgreSQL.Simple.Types (Query(..))
36import Control.Monad.Trans.State.Strict
41import Control.Monad.Trans.State.Strict
37import Control.Monad.Trans.Reader
42import Control.Monad.Trans.Reader
38import qualified Data.Vector as V
43import qualified Data.Vector as V
@@ -86,6 +91,15 @@ data SqlError = SqlError {
87instance Exception SqlError
92instance Exception SqlError
94+-- | Exception thrown if 'query' is used to perform an @INSERT@-like
95+-- operation, or 'execute' is used to perform a @SELECT@-like operation.
96+data QueryError = QueryError {
99+ } deriving (Eq, Show, Typeable)
101+instance Exception QueryError
89data ConnectInfo = ConnectInfo {
103data ConnectInfo = ConnectInfo {
90 connectHost :: String
104 connectHost :: String
91 , connectPort :: Word16
105 , connectPort :: Word16
@@ -138,6 +152,8 @@ connectPostgreSQL connstr = do
138 PQ.ConnectionOk -> do
152 PQ.ConnectionOk -> do
139 connectionHandle <- newMVar conn
153 connectionHandle <- newMVar conn
140 connectionObjects <- newMVar (IntMap.empty)
154 connectionObjects <- newMVar (IntMap.empty)
155+ let wconn = Connection{..}
156+ _ <- execute_ wconn "SET standard_conforming_strings TO on"
141 return Connection{..}
157 return Connection{..}
143 msg <- maybe "connectPostgreSQL error" id <$> PQ.errorMessage conn
159 msg <- maybe "connectPostgreSQL error" id <$> PQ.errorMessage conn
@@ -198,6 +214,49 @@ exec conn sql =
198 Just res -> do
214 Just res -> do
199 return res
215 return res
217+-- | A version of 'execute' that does not perform query substitution.
218+execute_ :: Connection -> Query -> IO Int64
219+execute_ conn q@(Query stmt) = do
220+ result <- exec conn stmt
221+ finishExecute conn q result
223+finishExecute :: Connection -> Query -> PQ.Result -> IO Int64
224+finishExecute _conn q result = do
225+ status <- PQ.resultStatus result
228+ ncols <- PQ.nfields result
230+ then throwIO $ QueryError ("execute resulted in " ++ show ncols ++
233+ nstr <- PQ.cmdTuples result
234+ return $ case nstr of
235+ Nothing -> 0 -- is this appropriate?
236+ Just str -> toInteger str
238+ ncols <- PQ.nfields result
239+ throwIO $ QueryError ("execute resulted in " ++ show ncols ++
241+ PQ.CopyIn -> fail "FIXME: postgresql-simple does not currently support COPY IN"
242+ PQ.CopyOut -> fail "FIXME: postgresql-simple does not currently support COPY OUT"
244+ errormsg <- maybe "" id <$> PQ.resultErrorMessage result
245+ statusmsg <- PQ.resStatus status
246+ state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
247+ throwIO $ SqlError { sqlState = state
248+ , sqlNativeError = fromEnum status
249+ , sqlErrorMsg = B.concat [ "execute: ", statusmsg
252+ toInteger str = B8.foldl' delta 0 str
255+ if '0' <= c && c <= '9'
256+ then 10 * acc + fromIntegral (ord c - ord '0')
257+ else error ("finishExecute: not an int: " ++ B8.unpack str)
201disconnectedError :: SqlError
260disconnectedError :: SqlError
202disconnectedError = SqlError {
261disconnectedError = SqlError {
203 sqlNativeError = -1,
262 sqlNativeError = -1,