git.iko.soy/typeable/postgresql-simple
on master help sign in
Add Values constructor
a1eb940Leon P Smith
src/Database/PostgreSQL/Simple/ToField.hs+57
@@ -33,6 +33,7 @@ import Data.Monoid (mappend)
33import Data.Time (Day, TimeOfDay, LocalTime, UTCTime, ZonedTime)
33import Data.Time (Day, TimeOfDay, LocalTime, UTCTime, ZonedTime)
34import Data.Typeable (Typeable)
34import Data.Typeable (Typeable)
35import Data.Word (Word, Word8, Word16, Word32, Word64)
35import Data.Word (Word, Word8, Word16, Word32, Word64)
36+import {-# SOURCE #-} Database.PostgreSQL.Simple.ToRow
36import Database.PostgreSQL.Simple.Types
37import Database.PostgreSQL.Simple.Types
37import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
38import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
38import qualified Data.ByteString as SB
39import qualified Data.ByteString as SB
@@ -279,3 +280,59 @@ toJSONField = toField . JSON.toJSON
279inQuotes :: Builder -> Builder
280inQuotes :: Builder -> Builder
280inQuotes b = quote `mappend` b `mappend` quote
281inQuotes b = quote `mappend` b `mappend` quote
281 where quote = Utf8.fromChar '\''
282 where quote = Utf8.fromChar '\''
283+
284+interleaveFoldr :: (a -> [b] -> [b]) -> b -> [b] -> [a] -> [b]
285+interleaveFoldr f b bs as = foldr (\a bs -> b : f a bs) bs as
286+{-# INLINE interleaveFoldr #-}
287+
288+instance ToRow a => ToField (Values a) where
289+ toField (Values types rows) =
290+ case rows of
291+ [] -> case types of
292+ [] -> error err
293+ (_:_) -> values $ typedRow (repeat (lit "null"))
294+ types
295+ [lit " LIMIT 0)"]
296+ (_:_) -> case types of
297+ [] -> values $ untypedRows rows [litC ')']
298+ (_:_) -> values $ typedRows rows types [litC ')']
299+ where
300+ err = "Database.PostgreSQL.Simple.toField :: Values -> Action either values or types must be non-empty"
301+ lit = Plain . fromByteString
302+ litC = Plain . fromChar
303+ values x = Many (lit "(VALUES ": x)
304+
305+ typedField :: (Action, QualifiedIdentifier) -> [Action] -> [Action]
306+ typedField (val,typ) rest = val : lit "::" : toField typ : rest
307+
308+ typedRow :: [Action] -> [QualifiedIdentifier] -> [Action] -> [Action]
309+ typedRow (val:vals) (typ:typs) rest =
310+ litC '(' :
311+ typedField (val,typ) ( interleaveFoldr
312+ typedField
313+ (litC ',')
314+ (litC ')' : rest)
315+ (zip vals typs) )
316+
317+ untypedRow :: [Action] -> [Action] -> [Action]
318+ untypedRow (val:vals) rest =
319+ litC '(' : val :
320+ interleaveFoldr
321+ (:)
322+ (litC ',')
323+ (litC ')' : rest)
324+ vals
325+
326+ typedRows :: ToRow a => [a] -> [QualifiedIdentifier] -> [Action] -> [Action]
327+ typedRows (val:vals) types rest =
328+ typedRow (toRow val) types (litC ',' : untypedRows vals rest)
329+
330+ untypedRows :: ToRow a => [a] -> [Action] -> [Action]
331+ untypedRows [] rest = rest
332+ untypedRows (val:vals) rest =
333+ untypedRow (toRow val) $
334+ interleaveFoldr
335+ (untypedRow . toRow)
336+ (litC ',')
337+ rest
338+ vals
src/Database/PostgreSQL/Simple/ToField.hs-boot+23
@@ -1,6 +1,29 @@
1module Database.PostgreSQL.Simple.ToField where
1module Database.PostgreSQL.Simple.ToField where
2
2
3import Database.PostgreSQL.Simple.Types
3import Database.PostgreSQL.Simple.Types
4+import Blaze.ByteString.Builder(Builder)
5+import Data.ByteString(ByteString)
6+
7+-- | How to render an element when substituting it into a query.
8+data Action =
9+ Plain Builder
10+ -- ^ Render without escaping or quoting. Use for non-text types
11+ -- such as numbers, when you are /certain/ that they will not
12+ -- introduce formatting vulnerabilities via use of characters such
13+ -- as spaces or \"@'@\".
14+ | Escape ByteString
15+ -- ^ Escape and enclose in quotes before substituting. Use for all
16+ -- text-like types, and anything else that may contain unsafe
17+ -- characters when rendered.
18+ | EscapeByteA ByteString
19+ -- ^ Escape binary data for use as a @bytea@ literal. Include surrounding
20+ -- quotes. This is used by the 'Binary' newtype wrapper.
21+ | EscapeIdentifier ByteString
22+ -- ^ Escape before substituting. Use for all sql identifiers like
23+ -- table, column names, etc. This is used by the 'Identifier' newtype
24+ -- wrapper.
25+ | Many [Action]
26+ -- ^ Concatenate a series of rendering actions.
4
27
5class ToField a
28class ToField a
6
29
src/Database/PostgreSQL/Simple/ToRow.hs-boot+2−1
@@ -3,6 +3,7 @@ module Database.PostgreSQL.Simple.ToRow where
3import Database.PostgreSQL.Simple.Types
3import Database.PostgreSQL.Simple.Types
4import {-# SOURCE #-} Database.PostgreSQL.Simple.ToField
4import {-# SOURCE #-} Database.PostgreSQL.Simple.ToField
5
5
6−class ToRow a
6+class ToRow a where
7+ toRow :: a -> [Action]
7
8
8instance ToField a => ToRow (Only a)
9instance ToField a => ToRow (Only a)
src/Database/PostgreSQL/Simple/Types.hs+42
@@ -27,6 +27,7 @@ module Database.PostgreSQL.Simple.Types
27 , (:.)(..)
27 , (:.)(..)
28 , Savepoint(..)
28 , Savepoint(..)
29 , PGArray(..)
29 , PGArray(..)
30+ , Values(..)
30 ) where
31 ) where
31
32
32import Blaze.ByteString.Builder (toByteString)
33import Blaze.ByteString.Builder (toByteString)
@@ -173,3 +174,44 @@ infixr 3 :.
173
174
174newtype Savepoint = Savepoint Query
175newtype Savepoint = Savepoint Query
175 deriving (Eq, Ord, Show, Read, Typeable)
176 deriving (Eq, Ord, Show, Read, Typeable)
177+
178+-- | Represents a @VALUES@ table literal, usable as an alternative
179+-- to @executeMany@ and @returning@. For example:
180+--
181+-- > execute c "INSERT INTO table (key,val) ?"
182+-- > (Only (Values ["int4","text"]
183+-- > [(1,"hello"),(2,"world")]))
184+--
185+-- Issues the following query:
186+--
187+-- > INSERT INTO table (key,val) (VALUES (1::"int4",'hello'::"text"),(2,'world'))
188+--
189+-- When the list of values is empty, the following query will be issued:
190+--
191+-- > INSERT INTO table (key,val) (VALUES (null::"int4",null::"text") LIMIT 0)
192+--
193+-- By contrast, @executeMany@ and @returning@ don't issue the query
194+-- in the empty case, and simply return @0@ and @[]@ respectively.
195+--
196+-- The advantage over @executeMany@ is in cases when you want to
197+-- parameterize table literals in addition to other parameters, as can
198+-- occur with writable common table expressions, for example.
199+--
200+-- The first argument is a list of postgresql type names. Because this
201+-- is turned into a properly quoted identifier, the type name is case
202+-- sensitive and must be as it appears in the @pg_type@ table. Thus,
203+-- you must write @timestamptz@ instead of @timestamp with time zone@,
204+-- @int4@ instead of @integer@, @_int8@ instead of @bigint[]@, etcetera.
205+--
206+-- You may omit the type names, however, if you do so the list
207+-- of values must be non-empty, and postgresql must be able to infer
208+-- the types of the columns from the surrounding context. If these
209+-- conditions are not met, postgresql-simple will throw an exception
210+-- without issuing the query in the former case, and in the latter
211+-- the postgres server will return an error which will be turned into
212+-- a @SqlError@ exception.
213+--
214+-- See <http://www.postgresql.org/docs/9.3/static/sql-values.html> for
215+-- more information.
216+data Values a = Values [QualifiedIdentifier] [a]
217+ deriving (Eq, Ord, Show, Read, Typeable)