git.iko.soy/typeable/postgresql-simple
on master help sign in
Rename Param and QueryParams, Move Field into FromField
9014819Leon P Smith
postgresql-simple.cabal+4−5
@@ -1,5 +1,5 @@
1Name: postgresql-simple
1Name: postgresql-simple
2−Version: 0.0.99
2+Version: 0.1
3Synopsis: Mid-Level PostgreSQL client library
3Synopsis: Mid-Level PostgreSQL client library
4Description:
4Description:
5 Mid-Level PostgreSQL client library, forked from mysql-simple.
5 Mid-Level PostgreSQL client library, forked from mysql-simple.
@@ -19,15 +19,14 @@ Library
19 Exposed-modules:
19 Exposed-modules:
20 Database.PostgreSQL.Simple
20 Database.PostgreSQL.Simple
21 Database.PostgreSQL.Simple.BuiltinTypes
21 Database.PostgreSQL.Simple.BuiltinTypes
22− Database.PostgreSQL.Simple.Field
23 Database.PostgreSQL.Simple.FromField
22 Database.PostgreSQL.Simple.FromField
24 Database.PostgreSQL.Simple.FromRow
23 Database.PostgreSQL.Simple.FromRow
25 Database.PostgreSQL.Simple.LargeObjects
24 Database.PostgreSQL.Simple.LargeObjects
26 Database.PostgreSQL.Simple.Notification
25 Database.PostgreSQL.Simple.Notification
27 Database.PostgreSQL.Simple.Ok
26 Database.PostgreSQL.Simple.Ok
28− Database.PostgreSQL.Simple.Param
29− Database.PostgreSQL.Simple.QueryParams
30 Database.PostgreSQL.Simple.SqlQQ
27 Database.PostgreSQL.Simple.SqlQQ
28+ Database.PostgreSQL.Simple.ToField
29+ Database.PostgreSQL.Simple.ToRow
31 Database.PostgreSQL.Simple.Types
30 Database.PostgreSQL.Simple.Types
32-- Other-modules:
31-- Other-modules:
33 Database.PostgreSQL.Simple.Internal
32 Database.PostgreSQL.Simple.Internal
@@ -63,4 +62,4 @@ source-repository head
63source-repository this
62source-repository this
64 type: git
63 type: git
65 location: http://github.com/lpsmith/postgresql-simple
64 location: http://github.com/lpsmith/postgresql-simple
66− tag: v0.0.5
65+ tag: v0.1
src/Database/PostgreSQL/Simple.hs+12−12
@@ -125,8 +125,8 @@ import Database.PostgreSQL.Simple.Compat ( mask )
125import Database.PostgreSQL.Simple.FromField (ResultError(..))
125import Database.PostgreSQL.Simple.FromField (ResultError(..))
126import Database.PostgreSQL.Simple.FromRow (FromRow(..))
126import Database.PostgreSQL.Simple.FromRow (FromRow(..))
127import Database.PostgreSQL.Simple.Ok
127import Database.PostgreSQL.Simple.Ok
128−import Database.PostgreSQL.Simple.Param (Action(..), inQuotes)
128+import Database.PostgreSQL.Simple.ToField (Action(..), inQuotes)
129−import Database.PostgreSQL.Simple.QueryParams (QueryParams(..))
129+import Database.PostgreSQL.Simple.ToRow (ToRow(..))
130import Database.PostgreSQL.Simple.Types
130import Database.PostgreSQL.Simple.Types
131 ( Binary(..), In(..), Only(..), Query(..) )
131 ( Binary(..), In(..), Only(..), Query(..) )
132import Database.PostgreSQL.Simple.Internal as Base
132import Database.PostgreSQL.Simple.Internal as Base
@@ -167,11 +167,11 @@ instance Exception QueryError
167--
167--
168-- Throws 'FormatError' if the query string could not be formatted
168-- Throws 'FormatError' if the query string could not be formatted
169-- correctly.
169-- correctly.
170−formatQuery :: QueryParams q => Connection -> Query -> q -> IO ByteString
170+formatQuery :: ToRow q => Connection -> Query -> q -> IO ByteString
171formatQuery conn q@(Query template) qs
171formatQuery conn q@(Query template) qs
172 | null xs && '?' `B.notElem` template = return template
172 | null xs && '?' `B.notElem` template = return template
173 | otherwise = toByteString <$> buildQuery conn q template xs
173 | otherwise = toByteString <$> buildQuery conn q template xs
174− where xs = renderParams qs
174+ where xs = toRow qs
175
175
176-- | Format a query string with a variable number of rows.
176-- | Format a query string with a variable number of rows.
177--
177--
@@ -186,12 +186,12 @@ formatQuery conn q@(Query template) qs
186--
186--
187-- Throws 'FormatError' if the query string could not be formatted
187-- Throws 'FormatError' if the query string could not be formatted
188-- correctly.
188-- correctly.
189−formatMany :: (QueryParams q) => Connection -> Query -> [q] -> IO ByteString
189+formatMany :: (ToRow q) => Connection -> Query -> [q] -> IO ByteString
190formatMany _ q [] = fmtError "no rows supplied" q []
190formatMany _ q [] = fmtError "no rows supplied" q []
191formatMany conn q@(Query template) qs = do
191formatMany conn q@(Query template) qs = do
192 case match re template [] of
192 case match re template [] of
193 Just [_,before,qbits,after] -> do
193 Just [_,before,qbits,after] -> do
194− bs <- mapM (buildQuery conn q qbits . renderParams) qs
194+ bs <- mapM (buildQuery conn q qbits . toRow) qs
195 return . toByteString . mconcat $ fromByteString before :
195 return . toByteString . mconcat $ fromByteString before :
196 intersperse (fromChar ',') bs ++
196 intersperse (fromChar ',') bs ++
197 [fromByteString after]
197 [fromByteString after]
@@ -226,7 +226,7 @@ buildQuery conn q template xs = zipParams (split template) <$> mapM sub xs
226-- Returns the number of rows affected.
226-- Returns the number of rows affected.
227--
227--
228-- Throws 'FormatError' if the query could not be formatted correctly.
228-- Throws 'FormatError' if the query could not be formatted correctly.
229−execute :: (QueryParams q) => Connection -> Query -> q -> IO Int64
229+execute :: (ToRow q) => Connection -> Query -> q -> IO Int64
230execute conn template qs = do
230execute conn template qs = do
231 result <- exec conn =<< formatQuery conn template qs
231 result <- exec conn =<< formatQuery conn template qs
232 finishExecute conn template result
232 finishExecute conn template result
@@ -243,7 +243,7 @@ execute_ conn q@(Query stmt) = do
243-- Returns the number of rows affected.
243-- Returns the number of rows affected.
244--
244--
245-- Throws 'FormatError' if the query could not be formatted correctly.
245-- Throws 'FormatError' if the query could not be formatted correctly.
246−executeMany :: (QueryParams q) => Connection -> Query -> [q] -> IO Int64
246+executeMany :: (ToRow q) => Connection -> Query -> [q] -> IO Int64
247executeMany _ _ [] = return 0
247executeMany _ _ [] = return 0
248executeMany conn q qs = do
248executeMany conn q qs = do
249 result <- exec conn =<< formatMany conn q qs
249 result <- exec conn =<< formatMany conn q qs
@@ -302,7 +302,7 @@ finishExecute _conn q result = do
302-- using 'execute' instead of 'query').
302-- using 'execute' instead of 'query').
303--
303--
304-- * 'ResultError': result conversion failed.
304-- * 'ResultError': result conversion failed.
305−query :: (QueryParams q, FromRow r)
305+query :: (ToRow q, FromRow r)
306 => Connection -> Query -> q -> IO [r]
306 => Connection -> Query -> q -> IO [r]
307query conn template qs = do
307query conn template qs = do
308 result <- exec conn =<< formatQuery conn template qs
308 result <- exec conn =<< formatQuery conn template qs
@@ -333,7 +333,7 @@ query_ conn q@(Query que) = do
333--
333--
334-- * 'ResultError': result conversion failed.
334-- * 'ResultError': result conversion failed.
335
335
336−fold :: ( FromRow row, QueryParams params )
336+fold :: ( FromRow row, ToRow params )
337 => Connection
337 => Connection
338 -> Query
338 -> Query
339 -> params
339 -> params
@@ -358,7 +358,7 @@ defaultFoldOptions = FoldOptions {
358 transactionMode = TransactionMode ReadCommitted ReadOnly
358 transactionMode = TransactionMode ReadCommitted ReadOnly
359 }
359 }
360
360
361−foldWithOptions :: ( FromRow row, QueryParams params )
361+foldWithOptions :: ( FromRow row, ToRow params )
362 => FoldOptions
362 => FoldOptions
363 -> Connection
363 -> Connection
364 -> Query
364 -> Query
@@ -436,7 +436,7 @@ doFold FoldOptions{..} conn _template q a f = do
436 if null rs then return a else foldM f a rs >>= loop
436 if null rs then return a else foldM f a rs >>= loop
437
437
438-- | A version of 'fold' that does not transform a state value.
438-- | A version of 'fold' that does not transform a state value.
439−forEach :: (QueryParams q, FromRow r) =>
439+forEach :: (ToRow q, FromRow r) =>
440 Connection
440 Connection
441 -> Query -- ^ Query template.
441 -> Query -- ^ Query template.
442 -> q -- ^ Query parameters.
442 -> q -- ^ Query parameters.
src/Database/PostgreSQL/Simple/Field.hsdeleted−15
@@ -1,15 +0,0 @@
1−module Database.PostgreSQL.Simple.Field
2− ( Field
3− , typename
4− , name
5− , tableOid
6− , tableColumn
7− , format
8− , typeOid
9− , Oid
10− , Format(..)
11− , RawResult(..)
12− ) where
13−
14−import Database.PostgreSQL.Simple.Internal
15−import Database.PostgreSQL.LibPQ (Format(..), Oid)
src/Database/PostgreSQL/Simple/FromField.hs+33−20
@@ -27,32 +27,45 @@ module Database.PostgreSQL.Simple.FromField
27 FromField(..)
27 FromField(..)
28 , ResultError(..)
28 , ResultError(..)
29 , returnError
29 , returnError
30+
31+ , Field
32+ , typename
33+ , name
34+ , tableOid
35+ , tableColumn
36+ , format
37+ , typeOid
38+ , PQ.Oid(..)
39+ , PQ.Format(..)
30 ) where
40 ) where
31
41
32#include "MachDeps.h"
42#include "MachDeps.h"
33
43
34−import Control.Applicative (Applicative, (<|>), (<$>), (<*>), (<*), pure)
44+import Control.Applicative
45+ ( Applicative, (<|>), (<$>), (<*>), (<*), pure )
35−import Control.Exception (SomeException(..), Exception)
46+import Control.Exception (SomeException(..), Exception)
36−import Data.Attoparsec.Char8 hiding (Result)
47+import Data.Attoparsec.Char8 hiding (Result)
37−import Data.Bits ((.&.), (.|.), shiftL)
48+import Data.Bits ((.&.), (.|.), shiftL)
38−import Data.ByteString (ByteString)
49+import Data.ByteString (ByteString)
39import qualified Data.ByteString.Char8 as B
50import qualified Data.ByteString.Char8 as B
40−import Data.Int (Int16, Int32, Int64)
51+import Data.Int (Int16, Int32, Int64)
41−import Data.List (foldl')
52+import Data.List (foldl')
42−import Data.Ratio (Ratio)
53+import Data.Ratio (Ratio)
43−import Data.Time.Calendar (Day, fromGregorian)
54+import Data.Time.Calendar (Day, fromGregorian)
44−import Data.Time.Clock (UTCTime)
55+import Data.Time.Clock (UTCTime)
45−import Data.Time.Format (parseTime)
56+import Data.Time.Format (parseTime)
46−import Data.Time.LocalTime (TimeOfDay, makeTimeOfDayValid)
57+import Data.Time.LocalTime (TimeOfDay, makeTimeOfDayValid)
47−import Data.Typeable (Typeable, typeOf)
58+import Data.Typeable (Typeable, typeOf)
48−import Data.Word (Word64)
59+import Data.Word (Word64)
49−import Database.PostgreSQL.Simple.Internal
60+import Database.PostgreSQL.Simple.Internal
50−import Database.PostgreSQL.Simple.BuiltinTypes
61+import Database.PostgreSQL.Simple.BuiltinTypes
62+-- import qualified Database.PostgreSQ
51−import Database.PostgreSQL.Simple.Ok
63+import Database.PostgreSQL.Simple.Ok
52−import Database.PostgreSQL.Simple.Types (Binary(..), Null(..))
64+import Database.PostgreSQL.Simple.Types (Binary(..), Null(..))
53import qualified Database.PostgreSQL.LibPQ as PQ
65import qualified Database.PostgreSQL.LibPQ as PQ
66+-- import Database.PostgreSQL.LibPQ (Format(..), Oid(..))
54−import System.IO.Unsafe (unsafePerformIO)
67+import System.IO.Unsafe (unsafePerformIO)
55−import System.Locale (defaultTimeLocale)
68+import System.Locale (defaultTimeLocale)
56import qualified Data.ByteString as SB
69import qualified Data.ByteString as SB
57import qualified Data.ByteString.Char8 as B8
70import qualified Data.ByteString.Char8 as B8
58import qualified Data.ByteString.Lazy as LB
71import qualified Data.ByteString.Lazy as LB
src/Database/PostgreSQL/Simple/Param.hsdeleted−205
@@ -1,205 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, FlexibleInstances #-}
2−
3−-- |
4−-- Module: Database.PostgreSQL.Simple.Param
5−-- Copyright: (c) 2011 MailRank, Inc.
6−-- (c) 2011 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−-- Portability: portable
11−--
12−-- The 'Param' typeclass, for rendering a parameter to a SQL query.
13−
14−module Database.PostgreSQL.Simple.Param
15− (
16− Action(..)
17− , Param(..)
18− , inQuotes
19− ) where
20−
21−import Blaze.ByteString.Builder (Builder, fromByteString, fromLazyByteString,
22− toByteString)
23−import Blaze.ByteString.Builder.Char8 (fromChar)
24−import Blaze.Text (integral, double, float)
25−import Data.ByteString (ByteString)
26−import qualified Data.ByteString.Base16 as B16
27−import qualified Data.ByteString.Base16.Lazy as L16
28−import Data.Int (Int8, Int16, Int32, Int64)
29−import Data.List (intersperse)
30−import Data.Monoid (mappend)
31−import Data.Time.Calendar (Day, showGregorian)
32−import Data.Time.Clock (UTCTime)
33−import Data.Time.Format (formatTime)
34−import Data.Time.LocalTime (TimeOfDay)
35−import Data.Typeable (Typeable)
36−import Data.Word (Word, Word8, Word16, Word32, Word64)
37−import Database.PostgreSQL.Simple.Types (Binary(..), In(..), Null)
38−import System.Locale (defaultTimeLocale)
39−import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
40−import qualified Data.ByteString as SB
41−import qualified Data.ByteString.Lazy as LB
42−import qualified Data.Text as ST
43−import qualified Data.Text.Encoding as ST
44−import qualified Data.Text.Lazy as LT
45−import qualified Database.PostgreSQL.LibPQ as PQ
46−
47−-- | How to render an element when substituting it into a query.
48−data Action =
49− Plain Builder
50− -- ^ Render without escaping or quoting. Use for non-text types
51− -- such as numbers, when you are /certain/ that they will not
52− -- introduce formatting vulnerabilities via use of characters such
53− -- as spaces or \"@'@\".
54− | Escape ByteString
55− -- ^ Escape and enclose in quotes before substituting. Use for all
56− -- text-like types, and anything else that may contain unsafe
57− -- characters when rendered.
58− | Many [Action]
59− -- ^ Concatenate a series of rendering actions.
60− deriving (Typeable)
61−
62−instance Show Action where
63− show (Plain b) = "Plain " ++ show (toByteString b)
64− show (Escape b) = "Escape " ++ show b
65− show (Many b) = "Many " ++ show b
66−
67−-- | A type that may be used as a single parameter to a SQL query.
68−class Param a where
69− render :: a -> Action
70− -- ^ Prepare a value for substitution into a query string.
71−
72−instance Param Action where
73− render a = a
74− {-# INLINE render #-}
75−
76−instance (Param a) => Param (Maybe a) where
77− render Nothing = renderNull
78− render (Just a) = render a
79− {-# INLINE render #-}
80−
81−instance (Param a) => Param (In [a]) where
82− render (In []) = Plain $ fromByteString "(null)"
83− render (In xs) = Many $
84− Plain (fromChar '(') :
85− (intersperse (Plain (fromChar ',')) . map render $ xs) ++
86− [Plain (fromChar ')')]
87−
88−instance Param (Binary SB.ByteString) where
89− render (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
90− fromByteString (B16.encode bs) `mappend`
91− fromChar '\''
92−
93−instance Param (Binary LB.ByteString) where
94− render (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
95− fromLazyByteString (L16.encode bs) `mappend`
96− fromChar '\''
97−
98−renderNull :: Action
99−renderNull = Plain (fromByteString "null")
100−
101−instance Param Null where
102− render _ = renderNull
103− {-# INLINE render #-}
104−
105−instance Param Bool where
106− render True = Plain (fromByteString "true")
107− render False = Plain (fromByteString "false")
108− {-# INLINE render #-}
109−
110−instance Param Int8 where
111− render = Plain . integral
112− {-# INLINE render #-}
113−
114−instance Param Int16 where
115− render = Plain . integral
116− {-# INLINE render #-}
117−
118−instance Param Int32 where
119− render = Plain . integral
120− {-# INLINE render #-}
121−
122−instance Param Int where
123− render = Plain . integral
124− {-# INLINE render #-}
125−
126−instance Param Int64 where
127− render = Plain . integral
128− {-# INLINE render #-}
129−
130−instance Param Integer where
131− render = Plain . integral
132− {-# INLINE render #-}
133−
134−instance Param Word8 where
135− render = Plain . integral
136− {-# INLINE render #-}
137−
138−instance Param Word16 where
139− render = Plain . integral
140− {-# INLINE render #-}
141−
142−instance Param Word32 where
143− render = Plain . integral
144− {-# INLINE render #-}
145−
146−instance Param Word where
147− render = Plain . integral
148− {-# INLINE render #-}
149−
150−instance Param Word64 where
151− render = Plain . integral
152− {-# INLINE render #-}
153−
154−instance Param PQ.Oid where
155− render = Plain . integral . \(PQ.Oid x) -> x
156− {-# INLINE render #-}
157−
158−instance Param Float where
159− render v | isNaN v || isInfinite v = Plain (inQuotes (float v))
160− | otherwise = Plain (float v)
161− {-# INLINE render #-}
162−
163−instance Param Double where
164− render v | isNaN v || isInfinite v = Plain (inQuotes (double v))
165− | otherwise = Plain (double v)
166− {-# INLINE render #-}
167−
168−instance Param SB.ByteString where
169− render = Escape
170− {-# INLINE render #-}
171−
172−instance Param LB.ByteString where
173− render = render . SB.concat . LB.toChunks
174− {-# INLINE render #-}
175−
176−instance Param ST.Text where
177− render = Escape . ST.encodeUtf8
178− {-# INLINE render #-}
179−
180−instance Param [Char] where
181− render = Escape . toByteString . Utf8.fromString
182− {-# INLINE render #-}
183−
184−instance Param LT.Text where
185− render = render . LT.toStrict
186− {-# INLINE render #-}
187−
188−instance Param UTCTime where
189− render = Plain . Utf8.fromString . formatTime defaultTimeLocale "'%F %T%Q+00'"
190− {-# INLINE render #-}
191−
192−instance Param Day where
193− render = Plain . inQuotes . Utf8.fromString . showGregorian
194− {-# INLINE render #-}
195−
196−instance Param TimeOfDay where
197− render = Plain . inQuotes . Utf8.fromString . show
198− {-# INLINE render #-}
199−
200−-- | Surround a string with single-quote characters: \"@'@\"
201−--
202−-- This function /does not/ perform any other escaping.
203−inQuotes :: Builder -> Builder
204−inQuotes b = quote `mappend` b `mappend` quote
205− where quote = Utf8.fromChar '\''
src/Database/PostgreSQL/Simple/QueryParams.hsdeleted−85
@@ -1,85 +0,0 @@
1−-- |
2−-- Module: Database.PostgreSQL.Simple.QueryParams
3−-- Copyright: (c) 2011 MailRank, Inc.
4−-- (c) 2011 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−-- Portability: portable
9−--
10−-- The 'QueryParams' typeclass, for rendering a collection of
11−-- parameters to a SQL query.
12−--
13−-- Predefined instances are provided for tuples containing up to ten
14−-- elements.
15−
16−module Database.PostgreSQL.Simple.QueryParams
17− (
18− QueryParams(..)
19− ) where
20−
21−import Database.PostgreSQL.Simple.Param (Action(..), Param(..))
22−import Database.PostgreSQL.Simple.Types (Only(..))
23−
24−-- | A collection type that can be turned into a list of rendering
25−-- 'Action's.
26−--
27−-- Instances should use the 'render' method of the 'Param' class
28−-- to perform conversion of each element of the collection.
29−class QueryParams a where
30− renderParams :: a -> [Action]
31− -- ^ Render a collection of values.
32−
33−instance QueryParams () where
34− renderParams _ = []
35−
36−instance (Param a) => QueryParams (Only a) where
37− renderParams (Only v) = [render v]
38−
39−instance (Param a, Param b) => QueryParams (a,b) where
40− renderParams (a,b) = [render a, render b]
41−
42−instance (Param a, Param b, Param c) => QueryParams (a,b,c) where
43− renderParams (a,b,c) = [render a, render b, render c]
44−
45−instance (Param a, Param b, Param c, Param d) => QueryParams (a,b,c,d) where
46− renderParams (a,b,c,d) = [render a, render b, render c, render d]
47−
48−instance (Param a, Param b, Param c, Param d, Param e)
49− => QueryParams (a,b,c,d,e) where
50− renderParams (a,b,c,d,e) =
51− [render a, render b, render c, render d, render e]
52−
53−instance (Param a, Param b, Param c, Param d, Param e, Param f)
54− => QueryParams (a,b,c,d,e,f) where
55− renderParams (a,b,c,d,e,f) =
56− [render a, render b, render c, render d, render e, render f]
57−
58−instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g)
59− => QueryParams (a,b,c,d,e,f,g) where
60− renderParams (a,b,c,d,e,f,g) =
61− [render a, render b, render c, render d, render e, render f, render g]
62−
63−instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
64− Param h)
65− => QueryParams (a,b,c,d,e,f,g,h) where
66− renderParams (a,b,c,d,e,f,g,h) =
67− [render a, render b, render c, render d, render e, render f, render g,
68− render h]
69−
70−instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
71− Param h, Param i)
72− => QueryParams (a,b,c,d,e,f,g,h,i) where
73− renderParams (a,b,c,d,e,f,g,h,i) =
74− [render a, render b, render c, render d, render e, render f, render g,
75− render h, render i]
76−
77−instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
78− Param h, Param i, Param j)
79− => QueryParams (a,b,c,d,e,f,g,h,i,j) where
80− renderParams (a,b,c,d,e,f,g,h,i,j) =
81− [render a, render b, render c, render d, render e, render f, render g,
82− render h, render i, render j]
83−
84−instance (Param a) => QueryParams [a] where
85− renderParams = map render
src/Database/PostgreSQL/Simple/ToField.hsnew file+208
@@ -0,0 +1,208 @@
1+{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, FlexibleInstances #-}
2+
3+------------------------------------------------------------------------------
4+-- |
5+-- Module: Database.PostgreSQL.Simple.ToField
6+-- Copyright: (c) 2011 MailRank, Inc.
7+-- (c) 2011 Leon P Smith
8+-- License: BSD3
9+-- Maintainer: Leon P Smith <leon@melding-monads.com>
10+-- Stability: experimental
11+-- Portability: portable
12+--
13+-- The 'ToField' typeclass, for rendering a parameter to a SQL query.
14+--
15+------------------------------------------------------------------------------
16+
17+module Database.PostgreSQL.Simple.ToField
18+ (
19+ Action(..)
20+ , ToField(..)
21+ , inQuotes
22+ ) where
23+
24+import Blaze.ByteString.Builder (Builder, fromByteString, fromLazyByteString,
25+ toByteString)
26+import Blaze.ByteString.Builder.Char8 (fromChar)
27+import Blaze.Text (integral, double, float)
28+import Data.ByteString (ByteString)
29+import qualified Data.ByteString.Base16 as B16
30+import qualified Data.ByteString.Base16.Lazy as L16
31+import Data.Int (Int8, Int16, Int32, Int64)
32+import Data.List (intersperse)
33+import Data.Monoid (mappend)
34+import Data.Time.Calendar (Day, showGregorian)
35+import Data.Time.Clock (UTCTime)
36+import Data.Time.Format (formatTime)
37+import Data.Time.LocalTime (TimeOfDay)
38+import Data.Typeable (Typeable)
39+import Data.Word (Word, Word8, Word16, Word32, Word64)
40+import Database.PostgreSQL.Simple.Types (Binary(..), In(..), Null)
41+import System.Locale (defaultTimeLocale)
42+import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
43+import qualified Data.ByteString as SB
44+import qualified Data.ByteString.Lazy as LB
45+import qualified Data.Text as ST
46+import qualified Data.Text.Encoding as ST
47+import qualified Data.Text.Lazy as LT
48+import qualified Database.PostgreSQL.LibPQ as PQ
49+
50+-- | How to render an element when substituting it into a query.
51+data Action =
52+ Plain Builder
53+ -- ^ Render without escaping or quoting. Use for non-text types
54+ -- such as numbers, when you are /certain/ that they will not
55+ -- introduce formatting vulnerabilities via use of characters such
56+ -- as spaces or \"@'@\".
57+ | Escape ByteString
58+ -- ^ Escape and enclose in quotes before substituting. Use for all
59+ -- text-like types, and anything else that may contain unsafe
60+ -- characters when rendered.
61+ | Many [Action]
62+ -- ^ Concatenate a series of rendering actions.
63+ deriving (Typeable)
64+
65+instance Show Action where
66+ show (Plain b) = "Plain " ++ show (toByteString b)
67+ show (Escape b) = "Escape " ++ show b
68+ show (Many b) = "Many " ++ show b
69+
70+-- | A type that may be used as a single parameter to a SQL query.
71+class ToField a where
72+ toField :: a -> Action
73+ -- ^ Prepare a value for substitution into a query string.
74+
75+instance ToField Action where
76+ toField a = a
77+ {-# INLINE toField #-}
78+
79+instance (ToField a) => ToField (Maybe a) where
80+ toField Nothing = renderNull
81+ toField (Just a) = toField a
82+ {-# INLINE toField #-}
83+
84+instance (ToField a) => ToField (In [a]) where
85+ toField (In []) = Plain $ fromByteString "(null)"
86+ toField (In xs) = Many $
87+ Plain (fromChar '(') :
88+ (intersperse (Plain (fromChar ',')) . map toField $ xs) ++
89+ [Plain (fromChar ')')]
90+
91+instance ToField (Binary SB.ByteString) where
92+ toField (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
93+ fromByteString (B16.encode bs) `mappend`
94+ fromChar '\''
95+
96+instance ToField (Binary LB.ByteString) where
97+ toField (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
98+ fromLazyByteString (L16.encode bs) `mappend`
99+ fromChar '\''
100+
101+renderNull :: Action
102+renderNull = Plain (fromByteString "null")
103+
104+instance ToField Null where
105+ toField _ = renderNull
106+ {-# INLINE toField #-}
107+
108+instance ToField Bool where
109+ toField True = Plain (fromByteString "true")
110+ toField False = Plain (fromByteString "false")
111+ {-# INLINE toField #-}
112+
113+instance ToField Int8 where
114+ toField = Plain . integral
115+ {-# INLINE toField #-}
116+
117+instance ToField Int16 where
118+ toField = Plain . integral
119+ {-# INLINE toField #-}
120+
121+instance ToField Int32 where
122+ toField = Plain . integral
123+ {-# INLINE toField #-}
124+
125+instance ToField Int where
126+ toField = Plain . integral
127+ {-# INLINE toField #-}
128+
129+instance ToField Int64 where
130+ toField = Plain . integral
131+ {-# INLINE toField #-}
132+
133+instance ToField Integer where
134+ toField = Plain . integral
135+ {-# INLINE toField #-}
136+
137+instance ToField Word8 where
138+ toField = Plain . integral
139+ {-# INLINE toField #-}
140+
141+instance ToField Word16 where
142+ toField = Plain . integral
143+ {-# INLINE toField #-}
144+
145+instance ToField Word32 where
146+ toField = Plain . integral
147+ {-# INLINE toField #-}
148+
149+instance ToField Word where
150+ toField = Plain . integral
151+ {-# INLINE toField #-}
152+
153+instance ToField Word64 where
154+ toField = Plain . integral
155+ {-# INLINE toField #-}
156+
157+instance ToField PQ.Oid where
158+ toField = Plain . integral . \(PQ.Oid x) -> x
159+ {-# INLINE toField #-}
160+
161+instance ToField Float where
162+ toField v | isNaN v || isInfinite v = Plain (inQuotes (float v))
163+ | otherwise = Plain (float v)
164+ {-# INLINE toField #-}
165+
166+instance ToField Double where
167+ toField v | isNaN v || isInfinite v = Plain (inQuotes (double v))
168+ | otherwise = Plain (double v)
169+ {-# INLINE toField #-}
170+
171+instance ToField SB.ByteString where
172+ toField = Escape
173+ {-# INLINE toField #-}
174+
175+instance ToField LB.ByteString where
176+ toField = toField . SB.concat . LB.toChunks
177+ {-# INLINE toField #-}
178+
179+instance ToField ST.Text where
180+ toField = Escape . ST.encodeUtf8
181+ {-# INLINE toField #-}
182+
183+instance ToField [Char] where
184+ toField = Escape . toByteString . Utf8.fromString
185+ {-# INLINE toField #-}
186+
187+instance ToField LT.Text where
188+ toField = toField . LT.toStrict
189+ {-# INLINE toField #-}
190+
191+instance ToField UTCTime where
192+ toField = Plain . Utf8.fromString . formatTime defaultTimeLocale "'%F %T%Q+00'"
193+ {-# INLINE toField #-}
194+
195+instance ToField Day where
196+ toField = Plain . inQuotes . Utf8.fromString . showGregorian
197+ {-# INLINE toField #-}
198+
199+instance ToField TimeOfDay where
200+ toField = Plain . inQuotes . Utf8.fromString . show
201+ {-# INLINE toField #-}
202+
203+-- | Surround a string with single-quote characters: \"@'@\"
204+--
205+-- This function /does not/ perform any other escaping.
206+inQuotes :: Builder -> Builder
207+inQuotes b = quote `mappend` b `mappend` quote
208+ where quote = Utf8.fromChar '\''
src/Database/PostgreSQL/Simple/ToRow.hsnew file+90
@@ -0,0 +1,90 @@
1+------------------------------------------------------------------------------
2+-- |
3+-- Module: Database.PostgreSQL.Simple.QueryParams
4+-- Copyright: (c) 2011 MailRank, Inc.
5+-- (c) 2011 Leon P Smith
6+-- License: BSD3
7+-- Maintainer: Leon P Smith <leon@melding-monads.com>
8+-- Stability: experimental
9+-- Portability: portable
10+--
11+-- The 'QueryParams' typeclass, for rendering a collection of
12+-- parameters to a SQL query.
13+--
14+-- Predefined instances are provided for tuples containing up to ten
15+-- elements.
16+--
17+------------------------------------------------------------------------------
18+
19+module Database.PostgreSQL.Simple.ToRow
20+ (
21+ ToRow(..)
22+ ) where
23+
24+import Database.PostgreSQL.Simple.ToField (Action(..), ToField(..))
25+import Database.PostgreSQL.Simple.Types (Only(..))
26+
27+-- | A collection type that can be turned into a list of rendering
28+-- 'Action's.
29+--
30+-- Instances should use the 'render' method of the 'Param' class
31+-- to perform conversion of each element of the collection.
32+class ToRow a where
33+ toRow :: a -> [Action]
34+ -- ^ ToField a collection of values.
35+
36+instance ToRow () where
37+ toRow _ = []
38+
39+instance (ToField a) => ToRow (Only a) where
40+ toRow (Only v) = [toField v]
41+
42+instance (ToField a, ToField b) => ToRow (a,b) where
43+ toRow (a,b) = [toField a, toField b]
44+
45+instance (ToField a, ToField b, ToField c) => ToRow (a,b,c) where
46+ toRow (a,b,c) = [toField a, toField b, toField c]
47+
48+instance (ToField a, ToField b, ToField c, ToField d) => ToRow (a,b,c,d) where
49+ toRow (a,b,c,d) = [toField a, toField b, toField c, toField d]
50+
51+instance (ToField a, ToField b, ToField c, ToField d, ToField e)
52+ => ToRow (a,b,c,d,e) where
53+ toRow (a,b,c,d,e) =
54+ [toField a, toField b, toField c, toField d, toField e]
55+
56+instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f)
57+ => ToRow (a,b,c,d,e,f) where
58+ toRow (a,b,c,d,e,f) =
59+ [toField a, toField b, toField c, toField d, toField e, toField f]
60+
61+instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
62+ ToField g)
63+ => ToRow (a,b,c,d,e,f,g) where
64+ toRow (a,b,c,d,e,f,g) =
65+ [toField a, toField b, toField c, toField d, toField e, toField f,
66+ toField g]
67+
68+instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
69+ ToField g, ToField h)
70+ => ToRow (a,b,c,d,e,f,g,h) where
71+ toRow (a,b,c,d,e,f,g,h) =
72+ [toField a, toField b, toField c, toField d, toField e, toField f,
73+ toField g, toField h]
74+
75+instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
76+ ToField g, ToField h, ToField i)
77+ => ToRow (a,b,c,d,e,f,g,h,i) where
78+ toRow (a,b,c,d,e,f,g,h,i) =
79+ [toField a, toField b, toField c, toField d, toField e, toField f,
80+ toField g, toField h, toField i]
81+
82+instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
83+ ToField g, ToField h, ToField i, ToField j)
84+ => ToRow (a,b,c,d,e,f,g,h,i,j) where
85+ toRow (a,b,c,d,e,f,g,h,i,j) =
86+ [toField a, toField b, toField c, toField d, toField e, toField f,
87+ toField g, toField h, toField i, toField j]
88+
89+instance (ToField a) => ToRow [a] where
90+ toRow = map toField