packages feed

postgresql-simple 0.5.1.3 → 0.5.2.0

raw patch · 18 files changed

+206/−100 lines, 18 filesdep +filepathdep +tastydep +tasty-golden

Dependencies added: filepath, tasty, tasty-golden, tasty-hunit

Files

+ CHANGELOG.md view
@@ -0,0 +1,18 @@+For the full changelog, see+<https://github.com/lpsmith/postgresql-simple/blob/master/CHANGES.md>++### Version 0.5.2.0 (2016-05-25)+  * Significantly improved the error reporting from+    `Copy.putCopyData`, thanks to Ben Gamari.++  * Moved the test suite to use `tasty`,  with a big thanks+    to Ben Gamari.++  * Added `FromField.optionalField`,  and updated the documentation+    of `FromField.fromJSONField`, as inspired by an email conversation+    with Ian Wagner.++  * Updated all links in the haddocks to use https,  and added a link+    to the documentation of `connectPostgreSQL`.++  * Added a truncated changelog to the source distribution.
CONTRIBUTORS view
@@ -30,3 +30,4 @@ Alexey Khudyakov <alexey.skladnoy@gmail.com> Timo von Holtz <tvh@anchor.com.au> Amit Levy <amit@amitlevy.com>+Ben Gamari <ben@smart-cactus.org>
postgresql-simple.cabal view
@@ -1,5 +1,5 @@ Name:                postgresql-simple-Version:             0.5.1.3+Version:             0.5.2.0 Synopsis:            Mid-Level PostgreSQL client library Description:     Mid-Level PostgreSQL client library, forked from mysql-simple.@@ -16,6 +16,7 @@  extra-source-files:      CONTRIBUTORS+     CHANGELOG.md  Library   hs-source-dirs: src@@ -87,7 +88,7 @@ source-repository this   type:     git   location: http://github.com/lpsmith/postgresql-simple-  tag:      v0.5.1.3+  tag:      v0.5.2.0  test-suite test   type:           exitcode-stdio-1.0@@ -115,6 +116,10 @@                , bytestring                , containers                , cryptohash+               , filepath+               , tasty+               , tasty-hunit+               , tasty-golden                , HUnit                , postgresql-simple                , text
src/Database/PostgreSQL/Simple/Copy.hs view
@@ -9,7 +9,7 @@ -- Stability:   experimental -- -- mid-level support for COPY IN and COPY OUT.   See--- <http://www.postgresql.org/docs/9.2/static/sql-copy.html> for+-- <https://www.postgresql.org/docs/9.5/static/sql-copy.html> for -- more information. -- -- To use this binding,  first call 'copy' with an appropriate@@ -224,7 +224,9 @@     consumeResults pqconn     let rowCount =   P.string "COPY " *> (P.decimal <* P.endOfInput)     case P.parseOnly rowCount cmdStat of-      Left  _ -> fail errCmdStatusFmt+      Left  _ -> do mmsg <- PQ.errorMessage pqconn+                    fail $ errCmdStatusFmt+                        ++ maybe "" (\msg -> "\nConnection error: "++B.unpack msg) mmsg       Right n -> return $! n   where     errCmdStatus    = B.unpack funcName ++ ": failed to fetch command status"
src/Database/PostgreSQL/Simple/Errors.hs view
@@ -124,7 +124,7 @@ ------------------------------------------------------------------------ -- Error predicates ----- http://www.postgresql.org/docs/current/static/errcodes-appendix.html+-- https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html  isSerializationError :: SqlError -> Bool isSerializationError = isSqlState "40001"
src/Database/PostgreSQL/Simple/FromField.hs view
@@ -107,6 +107,7 @@     , PQ.Format(..)     , pgArrayFieldParser +    , optionalField     , fromJSONField     ) where @@ -268,10 +269,21 @@ --   compatible with type @a@.  Note that the type is not checked if --   the value is null, although it is inadvisable to rely on this --   behavior.-instance (FromField a) => FromField (Maybe a) where-    fromField _ Nothing = pure Nothing-    fromField f bs      = Just <$> fromField f bs+instance FromField a => FromField (Maybe a) where+    fromField = optionalField fromField +-- | For dealing with SQL @null@ values outside of the 'FromField' class.+--   Alternatively, one could use 'Control.Applicative.optional',  but that+--   also turns type and conversion errors into 'Nothing',  whereas this is+--   more specific and turns only @null@ values into 'Nothing'.++optionalField :: FieldParser a -> FieldParser (Maybe a)+optionalField p f mv =+    case mv of+      Nothing -> pure Nothing+      Just _  -> Just <$> p f mv+{-# INLINE optionalField #-}+ -- | compatible with any data type,  but the value must be null instance FromField Null where     fromField _ Nothing  = pure Null@@ -559,6 +571,16 @@ -- -- The 'Typeable' constraint is required to show more informative -- error messages when parsing fails.+--+-- Note that @fromJSONField :: FieldParser ('Maybe' Foo)@ will return+-- @'Nothing'@ on the json @null@ value, and return an exception on SQL @null@+-- value.  Alternatively,  one could write @'optionalField' fromJSONField@+-- that will return @Nothing@ on SQL @null@,  and otherwise will call+-- @fromJSONField :: FieldParser Foo@ and then return @'Just'@ the+-- result value,  or return its exception.  If one would+-- like to return @Nothing@ on both the SQL @null@ and json @null@ values,+-- one way to do it would be to write+-- @\\f mv -> 'Control.Monad.join' '<$>' optionalField fromJSONField f mv@ fromJSONField :: (JSON.FromJSON a, Typeable a) => FieldParser a fromJSONField f mbBs = do     value <- fromField f mbBs@@ -566,7 +588,6 @@         JSON.Error err -> returnError ConversionFailed f $                             "JSON decoding error: " ++ err         JSON.Success x -> pure x-  -- | Compatible with the same set of types as @a@.  Note that --   modifying the 'IORef' does not have any effects outside
src/Database/PostgreSQL/Simple/HStore.hs view
@@ -8,14 +8,14 @@ -- -- Parsers and printers for hstore,  a extended type bundled with -- PostgreSQL providing finite maps from text strings to text strings.--- See <http://www.postgresql.org/docs/9.2/static/hstore.html> for more+-- See <https://www.postgresql.org/docs/9.5/static/hstore.html> for more -- information. -- -- Note that in order to use this type,  a database superuser must -- install it by running a sql script in the share directory.  This -- can be done on PostgreSQL 9.1 and later with the command -- @CREATE EXTENSION hstore@.  See--- <http://www.postgresql.org/docs/9.2/static/contrib.html> for more+-- <https://www.postgresql.org/docs/9.5/static/contrib.html> for more -- information. -- ------------------------------------------------------------------------------
src/Database/PostgreSQL/Simple/Internal.hs view
@@ -154,10 +154,11 @@ connect = connectPostgreSQL . postgreSQLConnectionString  -- | Attempt to make a connection based on a libpq connection string.---   See <http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING>+--   See <https://www.postgresql.org/docs/9.5/static/libpq-connect.html#LIBPQ-CONNSTRING> --   for more information.  Also note that environment variables also affect --   parameters not provided, parameters provided as the empty string, and a---   few other things; see <http://www.postgresql.org/docs/9.3/static/libpq-envars.html>+--   few other things; see+--   <https://www.postgresql.org/docs/9.5/static/libpq-envars.html> --   for details.  Here is an example with some of the most commonly used --   parameters: --@@ -188,7 +189,7 @@ --   Omitting @password@ will default to an appropriate password found --   in the @pgpass@ file,  or no password at all if a matching line is --   not found.   See---   <http://www.postgresql.org/docs/9.3/static/libpq-pgpass.html> for+--   <https://www.postgresql.org/docs/9.5/static/libpq-pgpass.html> for --   more information regarding this file. -- --   As all parameters are optional and the defaults are sensible,  the@@ -206,18 +207,20 @@ --   or production machines, or you will need to use a @pgpass@ file --   with the @password@ or @md5@ authentication methods. -----   See <http://www.postgresql.org/docs/9.3/static/client-authentication.html>+--   See <https://www.postgresql.org/docs/9.5/static/client-authentication.html> --   for more information regarding the authentication process. -- --   SSL/TLS will typically "just work" if your postgresql server supports or --   requires it.  However,  note that libpq is trivially vulnerable to a MITM---   attack without setting additional SSL parameters in the connection string.---   In particular,  @sslmode@ needs to be set to @require@, @verify-ca@, or+--   attack without setting additional SSL connection parameters.  In+--   particular,  @sslmode@ needs to be set to @require@, @verify-ca@, or --   @verify-full@ in order to perform certificate validation.  When @sslmode@ --   is @require@,  then you will also need to specify a @sslrootcert@ file, --   otherwise no validation of the server's identity will be performed. --   Client authentication via certificates is also possible via the---   @sslcert@ and @sslkey@ parameters.+--   @sslcert@ and @sslkey@ parameters.   See+--   <https://www.postgresql.org/docs/9.5/static/libpq-ssl.html>+--   for detailed information regarding libpq and SSL.  connectPostgreSQL :: ByteString -> IO Connection connectPostgreSQL connstr = do
src/Database/PostgreSQL/Simple/LargeObjects.hs view
@@ -7,7 +7,7 @@ -- Maintainer  :  leon@melding-monads.com -- -- Support for PostgreSQL's Large Objects;  see--- <http://www.postgresql.org/docs/9.1/static/largeobjects.html> for more+-- <https://www.postgresql.org/docs/9.5/static/largeobjects.html> for more -- information. -- -- Note that Large Object File Descriptors are only valid within a single
src/Database/PostgreSQL/Simple/Notification.hs view
@@ -14,7 +14,7 @@ -- -- Support for receiving asynchronous notifications via PostgreSQL's -- Listen/Notify mechanism.  See--- <http://www.postgresql.org/docs/9.1/static/sql-notify.html> for more+-- <https://www.postgresql.org/docs/9.5/static/sql-notify.html> for more -- information. -- -- Note that on Windows,  @getNotification@ currently uses a polling loop@@ -25,7 +25,7 @@ -- Notifications do not create any extra work for the backend,  and are -- likely cheaper on the client side as well. ----- <http://hackage.haskell.org/trac/ghc/ticket/7353>+-- <https://hackage.haskell.org/trac/ghc/ticket/7353> -- ----------------------------------------------------------------------------- 
src/Database/PostgreSQL/Simple/Time.hs view
@@ -11,7 +11,7 @@ Also, this module also contains commentary regarding postgresql's timestamp types,  civil timekeeping in general,  and how it relates to postgresql-simple. You can read more about PostgreSQL's date and time types-at <http://www.postgresql.org/docs/9.1/static/datatype-datetime.html>,+at <https://www.postgresql.org/docs/9.5/static/datatype-datetime.html>, and the IANA time zone database at <https://en.wikipedia.org/wiki/Tz_database>.  Stack Overflow also has some excellent commentary on time,  if it is a
src/Database/PostgreSQL/Simple/Transaction.hs view
@@ -45,7 +45,7 @@  -- | Of the four isolation levels defined by the SQL standard, -- these are the three levels distinguished by PostgreSQL as of version 9.0.--- See <http://www.postgresql.org/docs/9.1/static/transaction-iso.html>+-- See <https://www.postgresql.org/docs/9.5/static/transaction-iso.html> -- for more information.   Note that prior to PostgreSQL 9.0, 'RepeatableRead' -- was equivalent to 'Serializable'. @@ -203,7 +203,7 @@ -- be used inside of a transaction, and provides a sort of -- \"nested transaction\". ----- See <http://www.postgresql.org/docs/current/static/sql-savepoint.html>+-- See <https://www.postgresql.org/docs/9.5/static/sql-savepoint.html> withSavepoint :: Connection -> IO a -> IO a withSavepoint conn body =   mask $ \restore -> do
src/Database/PostgreSQL/Simple/Types.hs view
@@ -131,11 +131,11 @@ -- 1.  Use postgresql-simple's 'Values' type instead,  which can handle the --     empty case correctly.  Note however that while specifying the --     postgresql type @"int4"@ is mandatory in the empty case,  specifying---     the haskell type @[Only Int]@ would not normally be needed in+--     the haskell type @Values (Only Int)@ would not normally be needed in --     realistic use cases. -- --     > query c "select * from whatever where id not in ?"---     >         (Only (Values "int4") ([] :: [Only Int]))+--     >         (Only (Values ["int4"] [] :: Values (Only Int))) -- -- -- 2.  Use sql's @COALESCE@ operator to turn a logical @null@ into the correct@@ -143,10 +143,18 @@ --     case: -- --     > query c "select * from whatever where coalesce(id NOT in ?, TRUE)"---     >         (Only (In ([] :: [Int])))+--     >         (Only (In [] :: In [Int])) -- --     > query c "select * from whatever where coalesce(id IN ?, FALSE)"---     >         (Only (In ([] :: [Int])))+--     >         (Only (In [] :: In [Int]))+--+--     Note that at as of PostgreSQL 9.4,  the query planner cannot see inside+--     the @COALESCE@ operator,  so if you have an index on @id@ then you+--     probably don't want to write the last example with @COALESCE@,  which+--     would result in a table scan.   There are further caveats if @id@ can+--     be null or you want null treated sensibly as a component of @IN@ or+--     @NOT IN@.+ newtype In a = In a     deriving (Eq, Ord, Read, Show, Typeable, Functor) @@ -209,8 +217,9 @@ newtype Savepoint = Savepoint Query     deriving (Eq, Ord, Show, Read, Typeable) --- | Represents a @VALUES@ table literal,  usable as an alternative---   to @executeMany@ and @returning@.  The main advantage is that+-- | Represents a @VALUES@ table literal,  usable as an alternative to+--   'Database.PostgreSQL.Simple.executeMany' and+--   'Database.PostgreSQL.Simple.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@@ -223,7 +232,7 @@ -- >     ), new_attributes AS ( -- >       INSERT INTO thing_attributes -- >          SELECT new_thing.id, attrs.*--- >            FROM new_thing JOIN ? attrs+-- >            FROM new_thing JOIN ? attrs ON TRUE -- >     ) SELECT * FROM new_thing -- >  |] ("foo", Values [  "int4", "text"    ] -- >                    [ ( 1    , "hello" )@@ -249,7 +258,8 @@ --   is turned into a properly quoted identifier,  the type name is case --   sensitive and must be as it appears in the @pg_type@ table.   Thus, --   you must write @timestamptz@ instead of @timestamp with time zone@,---   @int4@ instead of @integer@, @_int8@ instead of @bigint[]@, etcetera.+--   @int4@ instead of @integer@ or @serial@, @_int8@ instead of @bigint[]@,+--   etcetera. -- --   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@@ -258,7 +268,7 @@ --   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+--   See <https://www.postgresql.org/docs/9.5/static/sql-values.html> for --   more information. data Values a = Values [QualifiedIdentifier] [a]     deriving (Eq, Ord, Show, Read, Typeable)
test/Common.hs view
@@ -1,6 +1,6 @@ module Common (     module Database.PostgreSQL.Simple,-    module Test.HUnit,+    module Test.Tasty.HUnit,     TestEnv(..),     md5, ) where@@ -8,7 +8,7 @@ import Data.ByteString              (ByteString) import Data.Text                    (Text) import Database.PostgreSQL.Simple-import Test.HUnit+import Test.Tasty.HUnit  import qualified Crypto.Hash.MD5        as MD5 import qualified Data.ByteString.Base16 as Base16
test/Main.hs view
@@ -8,51 +8,58 @@ import Database.PostgreSQL.Simple.HStore import Database.PostgreSQL.Simple.Copy import qualified Database.PostgreSQL.Simple.Transaction as ST+ import Control.Applicative import Control.Exception as E import Control.Monad-import Data.ByteString (ByteString)+import Data.Char+import Data.List (sort) import Data.IORef import Data.Typeable+import GHC.Generics (Generic)++import Data.Aeson+import Data.ByteString (ByteString) import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy.Char8 as BL import Data.Map (Map)-import Data.List (sort) import qualified Data.Map as Map import Data.Text(Text) import qualified Data.Text.Encoding as T-import System.Exit (exitFailure)-import System.IO import qualified Data.Vector as V-import Data.Aeson-import GHC.Generics (Generic)+import System.FilePath +import Test.Tasty+import Test.Tasty.Golden import Notify import Serializable import Time -tests :: [TestEnv -> Test]-tests =-    [ TestLabel "Bytea"         . testBytea-    , TestLabel "ExecuteMany"   . testExecuteMany-    , TestLabel "Fold"          . testFold-    , TestLabel "Notify"        . testNotify-    , TestLabel "Serializable"  . testSerializable-    , TestLabel "Time"          . testTime-    , TestLabel "Array"         . testArray-    , TestLabel "HStore"        . testHStore-    , TestLabel "JSON"          . testJSON-    , TestLabel "Savepoint"     . testSavepoint-    , TestLabel "Unicode"       . testUnicode-    , TestLabel "Values"        . testValues-    , TestLabel "Copy"          . testCopy-    , TestLabel "Double"        . testDouble-    , TestLabel "1-ary generic" . testGeneric1-    , TestLabel "2-ary generic" . testGeneric2-    , TestLabel "3-ary generic" . testGeneric3+tests :: TestEnv -> TestTree+tests env = testGroup "tests"+    $ map ($ env)+    [ testBytea+    , testCase "ExecuteMany"   . testExecuteMany+    , testCase "Fold"          . testFold+    , testCase "Notify"        . testNotify+    , testCase "Serializable"  . testSerializable+    , testCase "Time"          . testTime+    , testCase "Array"         . testArray+    , testCase "HStore"        . testHStore+    , testCase "JSON"          . testJSON+    , testCase "Savepoint"     . testSavepoint+    , testCase "Unicode"       . testUnicode+    , testCase "Values"        . testValues+    , testCase "Copy"          . testCopy+    , testCopyFailures+    , testCase "Double"        . testDouble+    , testCase "1-ary generic" . testGeneric1+    , testCase "2-ary generic" . testGeneric2+    , testCase "3-ary generic" . testGeneric3     ] -testBytea :: TestEnv -> Test-testBytea TestEnv{..} = TestList+testBytea :: TestEnv -> TestTree+testBytea TestEnv{..} = testGroup "Bytea"     [ testStr "empty"                  []     , testStr "\"hello\""              $ map (fromIntegral . fromEnum) ("hello" :: String)     , testStr "ascending"              [0..255]@@ -61,7 +68,7 @@     , testStr "descending, doubled up" $ doubleUp [255,254..0]     ]   where-    testStr label bytes = TestLabel label $ TestCase $ do+    testStr label bytes = testCase label $ do         let bs = B.pack bytes          [Only h] <- query conn "SELECT md5(?::bytea)" [Binary bs]@@ -72,8 +79,8 @@      doubleUp = concatMap (\x -> [x, x]) -testExecuteMany :: TestEnv -> Test-testExecuteMany TestEnv{..} = TestCase $ do+testExecuteMany :: TestEnv -> Assertion+testExecuteMany TestEnv{..} = do     execute_ conn "CREATE TEMPORARY TABLE tmp_executeMany (i INT, t TEXT, b BYTEA)"      let rows :: [(Int, String, Binary ByteString)]@@ -90,8 +97,8 @@      return () -testFold :: TestEnv -> Test-testFold TestEnv{..} = TestCase $ do+testFold :: TestEnv -> Assertion+testFold TestEnv{..} = do     xs <- fold_ conn "SELECT generate_series(1,10000)"             [] $ \xs (Only x) -> return (x:xs)     reverse xs @?= ([1..10000] :: [Int])@@ -160,8 +167,8 @@                               ++ show (typeOf resultType)                               ++ " -> " ++ show val) -testArray :: TestEnv -> Test-testArray TestEnv{..} = TestCase $ do+testArray :: TestEnv -> Assertion+testArray TestEnv{..} = do     xs <- query_ conn "SELECT '{1,2,3,4}'::_int4"     xs @?= [Only (V.fromList [1,2,3,4 :: Int])]     xs <- query_ conn "SELECT '{{1,2},{3,4}}'::_int4"@@ -170,8 +177,8 @@     queryFailure conn "SELECT '{1,2,3,4}'::_int4" (undefined :: V.Vector Bool)     queryFailure conn "SELECT '{{1,2},{3,4}}'::_int4" (undefined :: V.Vector Int) -testHStore :: TestEnv -> Test-testHStore TestEnv{..} = TestCase $ do+testHStore :: TestEnv -> Assertion+testHStore TestEnv{..} = do     execute_ conn "CREATE EXTENSION IF NOT EXISTS hstore"     roundTrip []     roundTrip [("foo","bar"),("bar","baz"),("baz","hello")]@@ -183,8 +190,8 @@       m' <- query conn "SELECT ?::hstore" m       [m] @?= m' -testJSON :: TestEnv -> Test-testJSON TestEnv{..} = TestCase $ do+testJSON :: TestEnv -> Assertion+testJSON TestEnv{..} = do     roundTrip (Map.fromList [] :: Map Text Text)     roundTrip (Map.fromList [("foo","bar"),("bar","baz"),("baz","hello")] :: Map Text Text)     roundTrip (Map.fromList [("fo\"o","bar"),("b\\ar","baz"),("baz","\"value\\with\"escapes")] :: Map Text Text)@@ -198,8 +205,8 @@       js' <- query conn "SELECT ?::json" js       [js] @?= js' -testSavepoint :: TestEnv -> Test-testSavepoint TestEnv{..} = TestCase $ do+testSavepoint :: TestEnv -> Assertion+testSavepoint TestEnv{..} = do     True <- expectError ST.isNoActiveTransactionError $             withSavepoint conn $ return () @@ -259,8 +266,8 @@      return () -testUnicode :: TestEnv -> Test-testUnicode TestEnv{..} = TestCase $ do+testUnicode :: TestEnv -> Assertion+testUnicode TestEnv{..} = do     let q = Query . T.encodeUtf8  -- Handle encoding ourselves to ensure                                   -- the table gets created correctly.     let messages = map Only ["привет","мир"] :: [Only Text]@@ -269,8 +276,8 @@     messages' <- query_ conn "SELECT сообщение FROM ру́сский"     sort messages @?= sort messages' -testValues :: TestEnv -> Test-testValues TestEnv{..} = TestCase $ do+testValues :: TestEnv -> Assertion+testValues TestEnv{..} = do     execute_ conn "CREATE TEMPORARY TABLE values_test (x int, y text)"     test (Values ["int4","text"] [])     test (Values ["int4","text"] [(1,"hello")])@@ -287,8 +294,8 @@       sort vals @?= sort vals'  -testCopy :: TestEnv -> Test-testCopy TestEnv{..} = TestCase $ do+testCopy :: TestEnv -> Assertion+testCopy TestEnv{..} = do     execute_ conn "CREATE TEMPORARY TABLE copy_test (x int, y text)"     copy_ conn "COPY copy_test FROM STDIN (FORMAT CSV)"     mapM_ (putCopyData conn) copyRows@@ -315,8 +322,53 @@         CopyOutDone _   -> return rows         CopyOutRow  row -> loop (row:rows) -testDouble :: TestEnv -> Test-testDouble TestEnv{..} = TestCase $ do+testCopyFailures :: TestEnv -> TestTree+testCopyFailures env = testGroup "Copy failures"+    $ map ($ env)+    [ testCopyUniqueConstraintError+    , testCopyMalformedError+    ]++goldenTest :: TestName -> IO BL.ByteString -> TestTree+goldenTest testName =+    goldenVsString testName (resultsDir </> fileName<.>"expected")+  where+    resultsDir = "test" </> "results"+    fileName = map normalize testName+    normalize c | not (isAlpha c) = '-'+                | otherwise       = c++-- | Test that we provide a sensible error message on failure+testCopyUniqueConstraintError :: TestEnv -> TestTree+testCopyUniqueConstraintError TestEnv{..} =+    goldenTest "unique constraint violation"+    $ handle (\(SomeException exc) -> return $ BL.pack $ show exc) $ do+        execute_ conn "CREATE TEMPORARY TABLE copy_unique_constraint_error_test (x int PRIMARY KEY, y text)"+        copy_ conn "COPY copy_unique_constraint_error_test FROM STDIN (FORMAT CSV)"+        mapM_ (putCopyData conn) copyRows+        _n <- putCopyEnd conn+        return BL.empty+  where+    copyRows  = ["1,foo\n"+                ,"2,bar\n"+                ,"1,baz\n"]++testCopyMalformedError :: TestEnv -> TestTree+testCopyMalformedError TestEnv{..} =+    goldenTest "malformed input"+    $ handle (\(SomeException exc) -> return $ BL.pack $ show exc) $ do+        execute_ conn "CREATE TEMPORARY TABLE copy_malformed_input_error_test (x int PRIMARY KEY, y text)"+        copy_ conn "COPY copy_unique_constraint_error_test FROM STDIN (FORMAT CSV)"+        mapM_ (putCopyData conn) copyRows+        _n <- putCopyEnd conn+        return BL.empty+  where+    copyRows  = ["1,foo\n"+                ,"2,bar\n"+                ,"z,baz\n"]++testDouble :: TestEnv -> Assertion+testDouble TestEnv{..} = do     [Only (x :: Double)] <- query_ conn "SELECT 'NaN'::float8"     assertBool "expected NaN" (isNaN x)     [Only (x :: Double)] <- query_ conn "SELECT 'Infinity'::float8"@@ -325,24 +377,24 @@     x @?= (-1 / 0)  -testGeneric1 :: TestEnv -> Test-testGeneric1 TestEnv{..} = TestCase $ do+testGeneric1 :: TestEnv -> Assertion+testGeneric1 TestEnv{..} = do     roundTrip conn (Gen1 123)   where     roundTrip conn x0 = do         r <- query conn "SELECT ?::int" (x0 :: Gen1)         r @?= [x0] -testGeneric2 :: TestEnv -> Test-testGeneric2 TestEnv{..} = TestCase $ do+testGeneric2 :: TestEnv -> Assertion+testGeneric2 TestEnv{..} = do     roundTrip conn (Gen2 123 "asdf")   where     roundTrip conn x0 = do         r <- query conn "SELECT ?::int, ?::text" x0         r @?= [x0] -testGeneric3 :: TestEnv -> Test-testGeneric3 TestEnv{..} = TestCase $ do+testGeneric3 :: TestEnv -> Assertion+testGeneric3 TestEnv{..} = do     roundTrip conn (Gen3 123 "asdf" True)   where     roundTrip conn x0 = do@@ -401,8 +453,4 @@     withConn = bracket testConnect close  main :: IO ()-main = do-    mapM_ (`hSetBuffering` LineBuffering) [stdout, stderr]-    Counts{cases, tried, errors, failures} <--        withTestEnv $ \env -> runTestTT $ TestList $ map ($ env) tests-    when (cases /= tried || errors /= 0 || failures /= 0) $ exitFailure+main = withTestEnv $ defaultMain . tests
test/Notify.hs view
@@ -14,9 +14,8 @@ -- TODO: Test with payload, but only for PostgreSQL >= 9.0 -- (when that feature was introduced). -testNotify :: TestEnv -> Test+testNotify :: TestEnv -> Assertion testNotify TestEnv{..} =-    TestCase $     withConn $ \conn2 -> do         execute_ conn "LISTEN foo"         execute_ conn "LISTEN bar"
test/Serializable.hs view
@@ -23,9 +23,8 @@     1 <- execute conn "UPDATE testSerializableCounter SET n=?" (Only n)     return () -testSerializable :: TestEnv -> Test+testSerializable :: TestEnv -> Assertion testSerializable TestEnv{..} =-    TestCase $     withConn $ \conn2 -> do         initCounter conn 
test/Time.hs view
@@ -43,8 +43,8 @@ numTests :: Int numTests = 200 -testTime :: TestEnv -> Test-testTime env@TestEnv{..} = TestCase $ do+testTime :: TestEnv -> Assertion+testTime env@TestEnv{..} = do   initializeTable env   execute_ conn "SET timezone TO 'UTC'"   checkRoundTrips env "1860-01-01 00:00:00+00"