@@ -20,24 +20,31 @@
21module Database.PostgreSQL.Simple.Internal where
21module Database.PostgreSQL.Simple.Internal where
23+import Blaze.ByteString.Builder
24+ ( Builder, fromByteString, toByteString )
23import Control.Applicative
25import Control.Applicative
24import Control.Exception
26import Control.Exception
25import Control.Concurrent.MVar
27import Control.Concurrent.MVar
26import Control.Monad(MonadPlus(..))
28import Control.Monad(MonadPlus(..))
27import Data.ByteString(ByteString)
29import Data.ByteString(ByteString)
30+import qualified Data.ByteString as B
28import qualified Data.ByteString.Char8 as B8
31import qualified Data.ByteString.Char8 as B8
29import Data.Char (ord)
32import Data.Char (ord)
30import Data.Int (Int64)
33import Data.Int (Int64)
31import qualified Data.IntMap as IntMap
34import qualified Data.IntMap as IntMap
32import Data.IORef
35import Data.IORef
33import Data.Maybe(fromMaybe)
36import Data.Maybe(fromMaybe)
34import Data.String
38import Data.String
39+import qualified Data.Text as T
40+import qualified Data.Text.Encoding as TE
35import Data.Typeable
41import Data.Typeable
36import Data.Word
42import Data.Word
37import Database.PostgreSQL.LibPQ(Oid(..))
43import Database.PostgreSQL.LibPQ(Oid(..))
38import qualified Database.PostgreSQL.LibPQ as PQ
44import qualified Database.PostgreSQL.LibPQ as PQ
39import Database.PostgreSQL.LibPQ(ExecStatus(..))
45import Database.PostgreSQL.LibPQ(ExecStatus(..))
40import Database.PostgreSQL.Simple.Ok
46import Database.PostgreSQL.Simple.Ok
47+import Database.PostgreSQL.Simple.ToField (Action(..), inQuotes)
41import Database.PostgreSQL.Simple.Types (Query(..))
48import Database.PostgreSQL.Simple.Types (Query(..))
42import Database.PostgreSQL.Simple.TypeInfo.Types(TypeInfo)
49import Database.PostgreSQL.Simple.TypeInfo.Types(TypeInfo)
43import Control.Monad.Trans.State.Strict
50import Control.Monad.Trans.State.Strict
@@ -92,6 +99,17 @@ data QueryError = QueryError {
93instance Exception QueryError
100instance Exception QueryError
102+-- | Exception thrown if a 'Query' could not be formatted correctly.
103+-- This may occur if the number of \'@?@\' characters in the query
104+-- string does not match the number of parameters provided.
105+data FormatError = FormatError {
106+ fmtMessage :: String
108+ , fmtParams :: [ByteString]
109+ } deriving (Eq, Show, Typeable)
111+instance Exception FormatError
95data ConnectInfo = ConnectInfo {
113data ConnectInfo = ConnectInfo {
96 connectHost :: String
114 connectHost :: String
97 , connectPort :: Word16
115 , connectPort :: Word16
@@ -502,6 +520,39 @@ throwLibPQError conn default_desc = do
502 msg <- maybe default_desc id <$> PQ.errorMessage conn
520 msg <- maybe default_desc id <$> PQ.errorMessage conn
503 throwIO $! libPQError msg
521 throwIO $! libPQError msg
524+fmtError :: String -> Query -> [Action] -> a
525+fmtError msg q xs = throw FormatError {
528+ , fmtParams = map twiddle xs
530+ where twiddle (Plain b) = toByteString b
531+ twiddle (Escape s) = s
532+ twiddle (EscapeByteA s) = s
533+ twiddle (EscapeIdentifier s) = s
534+ twiddle (Many ys) = B.concat (map twiddle ys)
536+fmtErrorBs :: Query -> [Action] -> ByteString -> a
537+fmtErrorBs q xs msg = fmtError (T.unpack $ TE.decodeUtf8 msg) q xs
539+-- | Quote bytestring or throw 'FormatError'
540+quote :: Query -> [Action] -> Either ByteString ByteString -> Builder
541+quote q xs = either (fmtErrorBs q xs) (inQuotes . fromByteString)
543+buildAction :: Connection -- ^ Connection for string escaping
544+ -> Query -- ^ Query for message error
545+ -> [Action] -- ^ List of parameters for message error
546+ -> Action -- ^ Action to build
548+buildAction _ _ _ (Plain b) = pure b
549+buildAction conn q xs (Escape s) = quote q xs <$> escapeStringConn conn s
550+buildAction conn q xs (EscapeByteA s) = quote q xs <$> escapeByteaConn conn s
551+buildAction conn q xs (EscapeIdentifier s) =
552+ either (fmtErrorBs q xs) fromByteString <$> escapeIdentifier conn s
553+buildAction conn q xs (Many ys) =
554+ mconcat <$> mapM (buildAction conn q xs) ys
505checkError :: PQ.Connection -> Maybe a -> IO (Either ByteString a)
556checkError :: PQ.Connection -> Maybe a -> IO (Either ByteString a)
506checkError _ (Just x) = return $ Right x
557checkError _ (Just x) = return $ Right x
507checkError c Nothing = Left . maybe "" id <$> PQ.errorMessage c
558checkError c Nothing = Left . maybe "" id <$> PQ.errorMessage c