postgresql-simple 0.4.2.0 → 0.4.2.1
raw patch · 6 files changed
+103/−31 lines, 6 files
Files
- postgresql-simple.cabal +2/−2
- src/Database/PostgreSQL/Simple.hs +21/−0
- src/Database/PostgreSQL/Simple/FromField.hs +12/−2
- src/Database/PostgreSQL/Simple/ToField.hs +10/−8
- src/Database/PostgreSQL/Simple/Types.hs +37/−17
- test/Main.hs +21/−2
postgresql-simple.cabal view
@@ -1,5 +1,5 @@ Name: postgresql-simple-Version: 0.4.2.0+Version: 0.4.2.1 Synopsis: Mid-Level PostgreSQL client library Description: Mid-Level PostgreSQL client library, forked from mysql-simple.@@ -81,7 +81,7 @@ source-repository this type: git location: http://github.com/lpsmith/postgresql-simple- tag: v0.4.1.0+ tag: v0.4.2.1 test-suite test type: exitcode-stdio-1.0
src/Database/PostgreSQL/Simple.hs view
@@ -343,6 +343,27 @@ -- Returns the number of rows affected. -- -- Throws 'FormatError' if the query could not be formatted correctly.+--+-- For example, here's a command that inserts two rows into a table+-- with two columns:+--+-- @+-- executeMany c [sql|+-- INSERT INTO sometable VALUES (?,?)+-- |] [(1, \"hello\"),(2, \"world\")]+-- @+--+-- Here's an canonical example of a multi-row update command:+--+-- @+-- executeMany c [sql|+-- UPDATE sometable+-- SET sometable.y = upd.y+-- FROM (VALUES (?,?)) as upd(x,y)+-- WHERE sometable.x = upd.x+-- |] [(1, \"hello\"),(2, \"world\")+-- @+ executeMany :: (ToRow q) => Connection -> Query -> [q] -> IO Int64 executeMany _ _ [] = return 0 executeMany conn q qs = do
src/Database/PostgreSQL/Simple/FromField.hs view
@@ -23,6 +23,14 @@ since a 'Double' might lose precision if representing PostgreSQL's 64-bit @bigint@, the two are /not/ considered compatible. +Note that the 'Float' and 'Double' instances use attoparsec's 'double'+conversion routine, which sacrifices some accuracy for speed. If you+need accuracy, consider first converting data to a 'Scientific' or 'Rational'+type, and then converting to a floating-point type. If you are defining+your own 'ToRow' instances, this can be acheived simply by+@'fromRational' '<$>' 'field'@, although this idiom additionally compatible+with PostgreSQL's @numeric@ type.+ Because 'FromField' is a typeclass, one may provide conversions to additional Haskell types without modifying postgresql-simple. This is particularly useful for supporting PostgreSQL types that postgresql-simple@@ -301,12 +309,14 @@ instance FromField Integer where fromField = atto ok64 $ signed decimal --- | int2, float4+-- | int2, float4 (Uses attoparsec's 'double' routine, for+-- better accuracy convert to 'Scientific' or 'Rational' first) instance FromField Float where fromField = atto ok (realToFrac <$> double) where ok = $(mkCompats [TI.float4,TI.int2]) --- | int2, int4, float4, float8+-- | int2, int4, float4, float8 (Uses attoparsec's 'double' routine, for+-- better accuracy convert to 'Scientific' or 'Rational' first) instance FromField Double where fromField = atto ok double where ok = $(mkCompats [TI.float4,TI.float8,TI.int2,TI.int4])
src/Database/PostgreSQL/Simple/ToField.hs view
@@ -338,14 +338,16 @@ typedRows :: ToRow a => [a] -> [QualifiedIdentifier] -> [Action] -> [Action] typedRows [] _ _ = error funcname typedRows (val:vals) types rest =- typedRow (toRow val) types (litC ',' : untypedRows vals rest)+ typedRow (toRow val) types (multiRows vals rest) untypedRows :: ToRow a => [a] -> [Action] -> [Action]- untypedRows [] rest = rest+ untypedRows [] _ = error funcname untypedRows (val:vals) rest =- untypedRow (toRow val) $- interleaveFoldr- (untypedRow . toRow)- (litC ',')- rest- vals+ untypedRow (toRow val) (multiRows vals rest)++ multiRows :: ToRow a => [a] -> [Action] -> [Action]+ multiRows vals rest = interleaveFoldr+ (untypedRow . toRow)+ (litC ',')+ rest+ vals
src/Database/PostgreSQL/Simple/Types.hs view
@@ -139,6 +139,13 @@ instance Hashable QualifiedIdentifier where hashWithSalt i (QualifiedIdentifier q t) = hashWithSalt i (q, t) +-- | @\"foo.bar\"@ will get turned into+-- @QualifiedIdentifier (Just \"foo\") \"bar\"@, while @\"foo\"@ will get+-- turned into @QualifiedIdentifier Nothing \"foo\"@. Note this instance+-- is for convenience, and does not match postgres syntax. It+-- only examines the first period character, and thus cannot be used if the+-- qualifying identifier contains a period for example.+ instance IsString QualifiedIdentifier where fromString str = let (x,y) = T.break (== '.') (fromString str) in if T.null y@@ -173,26 +180,40 @@ deriving (Eq, Ord, Show, Read, Typeable) -- | Represents a @VALUES@ table literal, usable as an alternative--- to @executeMany@ and @returning@. For example:+-- to @executeMany@ and @returning@. The main advantage is that+-- you can parametrize more than just a single @VALUES@ expression.+-- For example, here's a query to insert a thing into one table+-- and some attributes of that thing into another, returning the+-- new id generated by the database: ----- > execute c "INSERT INTO table (key,val) ?"--- > (Only (Values ["int4","text"]--- > [(1,"hello"),(2,"world")])) ----- Issues the following query:+-- > query c [sql|+-- > WITH new_thing AS (+-- > INSERT INTO thing (name) VALUES (?) RETURNING id+-- > ), new_attributes AS (+-- > INSERT INTO thing_attributes+-- > SELECT new_thing.id, attrs.*+-- > FROM new_thing JOIN ? attrs+-- > ) SELECT * FROM new_thing+-- > |] ("foo", Values [ "int4", "text" ]+-- > [ ( 1 , "hello" )+-- > , ( 2 , "world" ) ]) ----- > INSERT INTO table (key,val) (VALUES (1::"int4",'hello'::"text"),(2,'world'))+-- (Note this example uses writable common table expressions,+-- which were added in PostgreSQL 9.1) ----- When the list of values is empty, the following query will be issued:+-- The second parameter gets expanded into the following SQL syntax: ----- > INSERT INTO table (key,val) (VALUES (null::"int4",null::"text") LIMIT 0)+-- > (VALUES (1::"int4",'hello'::"text"),(2,'world')) --+-- When the list of attributes is empty, the second parameter expands to:+--+-- > (VALUES (null::"int4",null::"text") LIMIT 0)+-- -- By contrast, @executeMany@ and @returning@ don't issue the query -- in the empty case, and simply return @0@ and @[]@ respectively.------ The advantage over @executeMany@ is in cases when you want to--- parameterize table literals in addition to other parameters, as can--- occur with writable common table expressions, for example.+-- This behavior is usually correct given their intended use cases,+-- but would certainly be wrong in the example above. -- -- The first argument is a list of postgresql type names. Because this -- is turned into a properly quoted identifier, the type name is case@@ -202,11 +223,10 @@ -- -- You may omit the type names, however, if you do so the list -- of values must be non-empty, and postgresql must be able to infer--- the types of the columns from the surrounding context. If these--- conditions are not met, postgresql-simple will throw an exception--- without issuing the query in the former case, and in the latter--- the postgres server will return an error which will be turned into--- a @SqlError@ exception.+-- the types of the columns from the surrounding context. If the first+-- condition is not met, postgresql-simple will throw an exception+-- without issuing the query. In the second case, the postgres server+-- will return an error which will be turned into a @SqlError@ exception. -- -- See <http://www.postgresql.org/docs/9.3/static/sql-values.html> for -- more information.
test/Main.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} import Common import Database.PostgreSQL.Simple.FromField (FromField)-import Database.PostgreSQL.Simple.Types(Query(..))+import Database.PostgreSQL.Simple.Types(Query(..),Values(..)) import Database.PostgreSQL.Simple.HStore import qualified Database.PostgreSQL.Simple.Transaction as ST import Control.Applicative@@ -40,6 +40,7 @@ , TestLabel "JSON" . testJSON , TestLabel "Savepoint" . testSavepoint , TestLabel "Unicode" . testUnicode+ , TestLabel "Values" . testValues ] testBytea :: TestEnv -> Test@@ -249,12 +250,30 @@ testUnicode :: TestEnv -> Test testUnicode TestEnv{..} = TestCase $ do- let q = Query . T.encodeUtf8+ let q = Query . T.encodeUtf8 -- Handle encoding ourselves to ensure+ -- the table gets created correctly. let messages = map Only ["привет","мир"] :: [Only Text] execute_ conn (q "CREATE TEMPORARY TABLE ру́сский (сообщение TEXT)") executeMany conn "INSERT INTO ру́сский (сообщение) VALUES (?)" messages messages' <- query_ conn "SELECT сообщение FROM ру́сский" sort messages @?= sort messages'++testValues :: TestEnv -> Test+testValues TestEnv{..} = TestCase $ do+ execute_ conn "CREATE TEMPORARY TABLE values_test (x int, y text)"+ test (Values ["int4","text"] [])+ test (Values ["int4","text"] [(1,"hello")])+ test (Values ["int4","text"] [(1,"hello"),(2,"world")])+ test (Values ["int4","text"] [(1,"hello"),(2,"world"),(3,"goodbye")])+ test (Values [] [(1,"hello")])+ test (Values [] [(1,"hello"),(2,"world")])+ test (Values [] [(1,"hello"),(2,"world"),(3,"goodbye")])+ where+ test :: Values (Int, Text) -> Assertion+ test table@(Values _ vals) = do+ execute conn "INSERT INTO values_test ?" (Only table)+ vals' <- query_ conn "DELETE FROM values_test RETURNING *"+ sort vals @?= sort vals' data TestException = TestException