packages feed

tmp-postgres 1.15.0.0 → 1.15.1.0

raw patch · 8 files changed

+371/−32 lines, 8 filesdep +base64-bytestringdep +cryptohash-sha1dep +deepseq

Dependencies added: base64-bytestring, cryptohash-sha1, deepseq, stm

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ Changelog for tmp-postgres +1.15.1.0+  #119 Add `initdb` cache+  #134 Expand tilde in Permanent DirectoryType setup.+ 1.15.0.0   #137 Remove SocketClass and listen unconditionally on 127.0.0.1, ::1 and a UNIX socket.   #138 Fix bug where optionsToDefaultConfig would make createdb plan even if one is not needed.
benchmark/Main.hs view
@@ -4,7 +4,7 @@ import Criterion.Main import Data.String import Database.Postgres.Temp.Internal-import Database.Postgres.Temp.Config+import Database.Postgres.Temp.Internal.Config import qualified Database.PostgreSQL.Simple as PG  defaultConfigDefaultInitDb :: Config@@ -38,16 +38,41 @@   bracket (PG.connectPostgreSQL theConnectionString) PG.close $     \conn -> void $ PG.execute_ conn "INSERT INTO foo1 (id) VALUES (1)" +setupCache :: IO (Bool, CompleteDirectoryType)+setupCache = do+  defCacheConfig <- createDefaultCacheConfig+  cacheInfo <- setupInitDbCache defCacheConfig+  void (withConfig (silentConfig <> toCacheConfig cacheInfo) (const $ pure ()))+  pure cacheInfo++setupWithCache :: (Config -> Benchmark) -> Benchmark+setupWithCache f = envWithCleanup setupCache cleanupInitDbCache $ f . (silentConfig <>) . toCacheConfig+ main :: IO () main = defaultMain   [ --bench "with" $ whnfIO $ with $ const $ pure ()   --, bench "withConfig no --no-sync" $ whnfIO $   --    withConfig defaultConfigDefaultInitDb $ const $ pure ()   bench "withConfig silent" $ whnfIO $-      withConfig silentConfig $ const $ pure ()-  --  bench "with migrate 10x" $ whnfIO $ replicateM 10 $ withConfig silent $ \db ->-  --    migrateDb db >> testQuery db-  --, bench "withNewDb migrate 10x" $ whnfIO $ withConfig silent $ \db -> do-  --    migrateDb db-  --    replicateM 10 $ withNewDb db testQuery+    withConfig silentConfig $ const $ pure ()++  , setupWithCache $ \cacheConfig -> bench "withConfig silent cache" $ whnfIO $+      withConfig cacheConfig $ const $ pure ()++  , bench "with migrate 10x" $ whnfIO $ replicateM 10 $ withConfig silentConfig $ \db ->+      migrateDb db >> testQuery db++  , bench "withNewDb migrate 10x" $ whnfIO $ withConfig silentConfig $ \db -> do+      migrateDb db+      replicateM 10 $ withNewDb db testQuery++  , setupWithCache $ \cacheConfig -> do+      bench "with migrate 10x and cache" $ whnfIO $ withConfig cacheConfig $ \_ -> do+        replicateM 10 $ withConfig cacheConfig $ \db ->+          migrateDb db >> testQuery db++  , setupWithCache $ \cacheConfig -> bench "withNewDbConfig migrate 10x and cache" $ whnfIO $ withConfig cacheConfig+    $ \db -> do+      migrateDb db+      replicateM 10 $ withNewDb db testQuery   ]
src/Database/Postgres/Temp.hs view
@@ -52,6 +52,8 @@   , withRestart   , withNewDb   , withNewDbConfig+  , withDbCacheConfig+  , withDbCache   -- * Separate start and stop interface.   , start   , startConfig@@ -61,6 +63,8 @@   , startNewDb   , startNewDbConfig   , stopNewDb+  , setupInitDbCache+  , cleanupInitDbCache   -- * Main resource handle   , DB   -- ** 'DB' accessors
src/Database/Postgres/Temp/Internal.hs view
@@ -4,8 +4,10 @@ identifiers that are used for testing but are not exported. -} module Database.Postgres.Temp.Internal where+ import Database.Postgres.Temp.Internal.Core import Database.Postgres.Temp.Internal.Config+ import           Control.Exception import           Control.Monad (void) import           Control.Monad.Trans.Cont@@ -18,6 +20,7 @@ import qualified Database.PostgreSQL.Simple.Options as Client import           System.Environment import           System.Exit (ExitCode(..))+import           System.Process import           System.Random import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>)) @@ -613,3 +616,116 @@         { Client.dbname = pure "template1"         }   dropDbIfExists template1Options newDbName++-------------------------------------------------------------------------------+-- initdb cache+-------------------------------------------------------------------------------+-- | Configuration for the @initdb@ data directory cache.+data CacheConfig = CacheConfig+  { cacheTemporaryDirectory :: FilePath+  -- ^ Root temporary directory used if 'cacheDirectoryType' is set to+  -- 'Temporary'. @\/tmp@ is a good default.+  , cacheDirectoryType      :: DirectoryType+  -- ^ Used to specify is a 'Permanent' or 'Temporary' directory should be+  --   used. 'createDefaultCacheConfig' uses 'Permanent' @~\/.tmp-postgres@+  --   by default.+  , cacheUseCopyOnWrite     :: Bool+  -- ^ Some operatoring system versions support flags for @cp@ that allow+  --   \"copy on write\" which is about 2x faster. 'createDefaultCacheConfig'+  --   attempts to determine if the @cp@ on the path supports copy on write+  --   and sets this to 'True' if it does.+  }++{-|+'createDefaultCacheConfig' attempts to determine if the @cp@ on the path+supports \"copy on write\" flags and if it does, sets 'cacheUseCopyOnWrite'+to 'True'.++It sets 'cacheDirectoryType' to 'Permanent' @~\/.tmp-postgres@ and+'cacheTemporaryDirectory' to @\/tmp@ (but this is not used when+'Permanent' is set).+-}+createDefaultCacheConfig :: IO CacheConfig+createDefaultCacheConfig = do+  let+#ifdef darwin_HOST_OS+    cpFlag = "-c"+#else+    cpFlag = "--reflink=auto"+#endif+  (_, _, errorOutput)<- readProcessWithExitCode "cp" [cpFlag] ""+  -- if the flags do not exist we get a message like "cp: illegal option"+  let usage = "usage:" -- macos+      missingFile = "cp: missing file operand" -- linux+      cacheUseCopyOnWrite = usage ==  take (length usage) errorOutput+        || missingFile ==  take (length missingFile) errorOutput+      cacheDirectoryType = Permanent "~/.tmp-postgres"+      cacheTemporaryDirectory = "/tmp"+  pure CacheConfig {..}++-- | Setup the @initdb@ cache folder.+setupInitDbCache+  :: CacheConfig+  -> IO (Bool, CompleteDirectoryType)+setupInitDbCache CacheConfig {..} =+  bracketOnError+    (setupDirectoryType+      cacheTemporaryDirectory+      "tmp-postgres-cache"+      cacheDirectoryType+    )+    cleanupDirectoryType $ pure . (cacheUseCopyOnWrite,)++{-|+Cleanup the cache directory if it was 'Temporary'.+-}+cleanupInitDbCache :: (Bool, CompleteDirectoryType) -> IO ()+cleanupInitDbCache = cleanupDirectoryType . snd++{-|+Enable @initdb@ data directory caching. This can lead to a 4x speedup.++Exception safe version of 'setupInitDbCache'. Equivalent to++@+   'withDbCacheConfig' = bracket_ ('setupInitDbCache' config) 'cleanupInitDbCache'+@++-}+withDbCacheConfig+  :: CacheConfig+  -- ^ Configuration+  -> ((Bool, CompleteDirectoryType) -> IO a)+  -- ^ action for which caching is enabled+  -> IO a+withDbCacheConfig config =+  bracket (setupInitDbCache config) cleanupInitDbCache++{-|+Equivalent to 'withDbCacheConfig' with the 'CacheConfig'+'createDefaultCacheConfig' makes.+-}+withDbCache :: ((Bool, CompleteDirectoryType) -> IO a) -> IO a+withDbCache action =+  flip withDbCacheConfig action =<< createDefaultCacheConfig++{-|+Helper to make a 'Config' out of caching info.++Equivalent to++@+  toCacheConfig cacheInfo = mempty+    { plan = mempty+        { initDbCache = pure $ Just $ fmap toFilePath cacheInfo+        }+    }+@++-}+toCacheConfig :: (Bool, CompleteDirectoryType) -> Config+toCacheConfig cacheInfo = mempty+  { plan = mempty+      { initDbCache = pure $ pure $ fmap toFilePath cacheInfo+      }+  }
src/Database/Postgres/Temp/Internal/Config.hs view
@@ -17,6 +17,7 @@ import Database.Postgres.Temp.Internal.Core  import           Control.Applicative.Lift+import           Control.DeepSeq import           Control.Exception import           Control.Monad (join) import           Control.Monad.Trans.Class@@ -256,7 +257,8 @@ -- --   @since 1.12.0.0 data CompleteDirectoryType = CPermanent FilePath | CTemporary FilePath-  deriving(Show, Eq, Ord)+  deriving stock (Show, Eq, Ord, Generic)+  deriving anyclass (NFData)  -- | Get the file path of a 'CompleteDirectoryType', regardless if it is a -- 'CPermanent' or 'CTemporary' type.@@ -291,8 +293,8 @@  instance Pretty DirectoryType where   pretty = \case-    Permanent x -> text "CPermanent" <+> pretty x-    Temporary   -> text "CTemporary"+    Permanent x -> text "Permanent" <+> pretty x+    Temporary   -> text "Temporary"  -- | Takes the last 'Permanent' value. instance Semigroup DirectoryType where@@ -317,7 +319,11 @@   -> IO CompleteDirectoryType setupDirectoryType tempDir pat = \case   Temporary -> CTemporary <$> createTempDirectory tempDir pat-  Permanent x  -> pure $ CPermanent x+  Permanent x  -> CPermanent <$> case x of+    '~':rest -> do+      homeDir <- getHomeDirectory+      pure $ homeDir <> "/" <> rest+    xs -> pure xs  -- Remove a temporary directory and ignore errors -- about it not being there.@@ -390,6 +396,7 @@   , connectionTimeout :: Last Int   -- ^ Max time to spend attempting to connection to @postgres@.   --   Time is in microseconds.+  , initDbCache :: Last (Maybe (Bool, FilePath))   }   deriving stock (Generic)   deriving Semigroup via GenericSemigroup Plan@@ -416,6 +423,8 @@     <> text "dataDirectoryString:" <+> pretty (getLast dataDirectoryString)     <> hardline     <> text "connectionTimeout:" <+> pretty (getLast connectionTimeout)+    <> hardline+    <> text "initDbCache:" <+> pretty (getLast initDbCache)  -- | Turn a 'Plan' into a 'CompletePlan'. Fails if any values are missing. completePlan :: [(String, String)] -> Plan -> Either [String] CompletePlan@@ -432,6 +441,8 @@     dataDirectoryString   completePlanConnectionTimeout <- getOption "connectionTimeout"     connectionTimeout+  completePlanCacheDirectory <- getOption "initDbCache"+    initDbCache    pure CompletePlan {..} @@ -484,7 +495,7 @@     <> hardline     <> text "port:" <+> pretty (getLast port)     <> hardline-    <> text "dataDirectory:"+    <> text "temporaryDirectory:"     <> softline     <> pretty (getLast temporaryDirectory) @@ -500,21 +511,22 @@ --   'Database.Postgres.Temp.startConfig'. toPlan   :: Bool-  -- ^ Make @initdb@ options+  -- ^ Make @initdb@ options.   -> Bool-  -- ^ Make @createdb@ options+  -- ^ Make @createdb@ options.   -> Int-  -- ^ port+  -- ^ The port.   -> FilePath-  -- ^ Socket directory+  -- ^ Socket directory.   -> FilePath-  -- ^ The @postgres@ data directory+  -- ^ The @postgres@ data directory.   -> Plan toPlan makeInitDb makeCreateDb port socketDirectory dataDirectoryString = mempty   { postgresConfigFile = socketDirectoryToConfig socketDirectory   , dataDirectoryString = pure dataDirectoryString   , connectionTimeout = pure (60 * 1000000) -- 1 minute   , logger = pure print+  , initDbCache = pure Nothing   , postgresPlan = mempty       { postgresConfig = standardProcessConfig           { commandLine = mempty@@ -546,7 +558,6 @@             { keyBased = Map.fromList                 [("--pgdata=", Just dataDirectoryString)]             }-         }       else Nothing   }
src/Database/Postgres/Temp/Internal/Core.hs view
@@ -9,11 +9,16 @@ import           Control.Concurrent (threadDelay) import           Control.Concurrent.Async (race_, withAsync) import           Control.Exception-import           Control.Monad (forever, (>=>), unless)+import           Control.Monad (forever, unless, void)+import           Crypto.Hash.SHA1 (hash) import qualified Data.ByteString.Char8 as BSC+import qualified Data.ByteString.Base64.URL as Base64+import           Data.Char import           Data.Foldable (for_) import           Data.IORef+import           Data.Maybe import           Data.Monoid+import           Data.List import           Data.String import           Data.Typeable import qualified Database.PostgreSQL.Simple as PG@@ -80,7 +85,12 @@   | DeleteDbError PG.SqlError   | EmptyDataDirectory   -- ^ This will happen if a 'Database.Postgres.Temp.Config.Plan' is missing a-  --   'initDbConfig'.+  --   'Database.Postgres.Temp.Config.initDbConfig'.+  | CopyCachedInitDbFailed String ExitCode+  -- ^ This is called if copying a folder cache fails.+  | FailedToFindDataDirectory String+  -- ^ Failed to find a data directory when trying to get+  --   a cached @initdb@ folder.   deriving (Show, Eq, Typeable)  instance Exception StartError@@ -99,7 +109,7 @@   let theConnectionString = Client.toConnectionString options       startAction = PG.connectPostgreSQL theConnectionString   try (bracket startAction PG.close mempty) >>= \case-    Left (_ :: IOError) -> threadDelay 10000 >> waitForDB logger options+    Left (_ :: IOError) -> threadDelay 1000 >> waitForDB logger options     Right () -> return ()  -- Only useful if we believe the output is finite@@ -144,6 +154,12 @@     <> softline     <> text (unwords completeProcessConfigCmdLine) +makeCommandLine :: String -> CompleteProcessConfig -> String+makeCommandLine command CompleteProcessConfig {..} =+  let envs = unwords $ map (\(x, y) -> x <> "=" <> y) completeProcessConfigEnvVars+      args = unwords completeProcessConfigCmdLine+  in envs <> " " <> command <> args+ -- | Start a process interactively and return the 'ProcessHandle' startProcess   :: String@@ -291,7 +307,102 @@        -- Postgres is now ready so return       return result+ -------------------------------------------------------------------------------+-- initdb cache+-------------------------------------------------------------------------------+getInitDbVersion :: IO String+getInitDbVersion = readProcessWithExitCode "initdb" ["--version"] "" >>= \case+  (ExitSuccess, outputString, _) -> do+    let+      theLastPart = last $ words outputString+      versionPart = takeWhile (\x -> isDigit x || x == '.' || x == '-') theLastPart+    pure $ if last versionPart == '.'+             then init versionPart+             else versionPart++  (startErrorExitCode, startErrorStdOut, startErrorStdErr) ->+    throwIO InitDbFailed {..}++-- TODO We need to remove the data directory!+makeInitDbCommandLine :: CompleteProcessConfig -> String+makeInitDbCommandLine = makeCommandLine "initdb"++makeArgumentHash :: String -> String+makeArgumentHash = BSC.unpack . Base64.encode . hash . BSC.pack++splitDataDirectory :: CompleteProcessConfig -> (Maybe String, CompleteProcessConfig)+splitDataDirectory old =+  let isDataDirectoryFlag xs = "-D" `isPrefixOf` xs || "--pgdata=" `isPrefixOf` xs+      (dataDirectoryArgs, otherArgs) =+        partition isDataDirectoryFlag $ completeProcessConfigCmdLine old++      firstDataDirectoryArg = flip fmap (listToMaybe dataDirectoryArgs) $ \case+        '-':'D':' ':theDir -> theDir+        '-':'D':theDir -> theDir+        '-':'-':'p':'g':'d':'a':'t':'a':'=':theDir -> theDir+        _ -> error "splitDataDirectory not possible"++      filteredEnvs = filter (not . ("PGDATA"==) . fst) $+        completeProcessConfigEnvVars old++      clearedConfig = old+        { completeProcessConfigCmdLine = otherArgs+        , completeProcessConfigEnvVars = filteredEnvs+        }++  in (firstDataDirectoryArg, clearedConfig)++makeCachePath :: FilePath -> String -> IO String+makeCachePath cacheFolder cmdLine = do+  version <- getInitDbVersion+  let theHash = makeArgumentHash cmdLine+  pure $ cacheFolder <> "/" <> version <> "/" <> theHash++addDataDirectory :: String -> CompleteProcessConfig -> CompleteProcessConfig+addDataDirectory theDataDirectory x = x+  { completeProcessConfigCmdLine =+      ("--pgdata=" <> theDataDirectory) : completeProcessConfigCmdLine x+  }++executeInitDb :: Maybe (Bool, FilePath) -> CompleteProcessConfig -> IO ()+executeInitDb cache config = do+  -- helper+  let runInitDb theConfig = do+        (res, stdOut, stdErr) <- executeProcessAndTee "initdb" theConfig+        throwIfNotSuccess (InitDbFailed stdOut stdErr) res++  -- Probably want a MVar to lock this whole operation+  void $ case cache of+    Nothing -> runInitDb config+    Just (copyOnWrite, directoryType) -> do+      let (mtheDataDirectory, clearedConfig) = splitDataDirectory config+      theDataDirectory <- maybe+        (throwIO $ FailedToFindDataDirectory (show $ pretty config))+        pure+        mtheDataDirectory++      let theCommandLine = makeInitDbCommandLine clearedConfig++      cachePath <- makeCachePath directoryType theCommandLine+      let newDataDirectory = cachePath <> "/data"+      doesDirectoryExist cachePath >>= \case+        True -> pure ()+        False -> do+          createDirectoryIfMissing True cachePath+          writeFile (cachePath <> "/commandLine.log") theCommandLine+          runInitDb $ addDataDirectory newDataDirectory clearedConfig+      -- TODO do a check for macos or linux+      let+#ifdef darwin_HOST_OS+        cpFlags = if copyOnWrite then "cp -Rc " else "cp -R "+#else+        cpFlags = if copyOnWrite then "cp -R --reflink=auto " else "cp -R "+#endif+        copyCommand = cpFlags <> newDataDirectory <> "/* " <> theDataDirectory+      throwIfNotSuccess (CopyCachedInitDbFailed copyCommand) =<< system copyCommand++------------------------------------------------------------------------------- -- CompletePlan ------------------------------------------------------------------------------- -- | 'CompletePlan' is the low level configuration necessary for creating a database@@ -308,6 +419,7 @@   , completePlanConfig            :: String   , completePlanDataDirectory     :: FilePath   , completePlanConnectionTimeout :: Int+  , completePlanCacheDirectory    :: Maybe (Bool, FilePath)   }  instance Pretty CompletePlan where@@ -330,6 +442,9 @@     <>  hardline     <>  text "completePlanDataDirectory:"     <+> pretty completePlanDataDirectory+    <>  hardline+    <>  text "completePlanCacheDirectory:"+    <+> pretty completePlanCacheDirectory  -- A simple helper to throw 'ExitCode's when they are 'ExitFailure'. throwIfNotSuccess :: Exception e => (ExitCode -> e) -> ExitCode -> IO ()@@ -352,8 +467,7 @@ startPlan :: CompletePlan -> IO PostgresProcess startPlan plan@CompletePlan {..} = do   completePlanLogger $ StartPlan $ show $ pretty plan-  for_ completePlanInitDb $ executeProcessAndTee "initdb" >=>-    \(res, stdOut, stdErr) -> throwIfNotSuccess (InitDbFailed stdOut stdErr) res+  for_ completePlanInitDb $ executeInitDb completePlanCacheDirectory    -- Try to give a better error if @initdb@ was not   -- configured to run.@@ -370,7 +484,6 @@     for_ completePlanCreateDb executeCreateDb      pure result-  -- | Stop the @postgres@ process. See 'stopPostgresProcess' for more details. stopPlan :: PostgresProcess -> IO ExitCode
test/Main.hs view
@@ -6,6 +6,7 @@ import           Data.Maybe import           Data.Monoid import           Data.Monoid.Generic+import           Data.List import qualified Data.Set as Set import           Data.String import qualified Database.PostgreSQL.Simple as PG@@ -56,6 +57,13 @@    pure $ length $ lines xs +assertConnection :: DB -> IO ()+assertConnection db = do+  one <- fmap (PG.fromOnly . head) $+      withConn db $ \conn -> PG.query_ conn "SELECT 1"++  one `shouldBe` (1 :: Int)+ testSuccessfulConfigNoTmp :: ConfigAndAssertion -> IO () testSuccessfulConfigNoTmp ConfigAndAssertion {..} = do   initialPostgresCount <- countPostgresProcesses@@ -64,11 +72,7 @@    withConfig' cConfig $ \db -> do     cAssert db-    -- check for a valid connection-    one <- fmap (PG.fromOnly . head) $-      withConn db $ \conn -> PG.query_ conn "SELECT 1"--    one `shouldBe` (1 :: Int)+    assertConnection db    countPostgresProcesses `shouldReturn` initialPostgresCount   countInitdbProcesses `shouldReturn` initialInitdbCount@@ -86,7 +90,7 @@  testWithTemporaryDirectory :: ConfigAndAssertion -> (ConfigAndAssertion -> IO a) -> IO a testWithTemporaryDirectory x f =-  withTempDirectory "/tmp" "tmp-postgres-spec" $ \directoryPath -> do+  withTempDirectory "/tmp" "tmp-postgres-spec" $ \directoryPath ->     f x       { cConfig = (cConfig x) { temporaryDirectory = pure directoryPath }       , cAssert = cAssert x <> (\db -> toTemporaryDirectory db `shouldBe` directoryPath)@@ -331,6 +335,60 @@        doesDirectoryExist pathToCheck >>= \case          True -> pure ()          False -> fail "temporary file was not made permanent"++  it "withDbCacheConfig actually caches the config and cleans up" $+    withTempDirectory "/tmp" "tmp-postgres-cache-test" $ \dirPath -> do+      let config = silentConfig { temporaryDirectory = pure dirPath }+          cacheConfig = CacheConfig+            { cacheTemporaryDirectory = dirPath+            , cacheDirectoryType      = Temporary+            , cacheUseCopyOnWrite     = True+            }+      withDbCacheConfig cacheConfig $ \cacheInfo -> do+        withConfig' (config <> toCacheConfig cacheInfo) $ const $ pure ()+        -- see if there is a cache+        tmpFiles <- listDirectory dirPath++        cacheDir <- case filter ("tmp-postgres-cache" `isPrefixOf`) tmpFiles of+          [x] -> pure x+          xs -> fail $ "expected single cache dir got: " <> show xs++        listDirectory (dirPath <> "/" <> cacheDir) >>= \case+          [versionDir] -> listDirectory  (dirPath <> "/" <> cacheDir <> "/" <> versionDir) >>= \case+            [initDbCacheDir] ->+              writeFile+                (  dirPath+                <> "/"+                <> cacheDir+                <> "/"+                <> versionDir+                <> "/"+                <> initDbCacheDir+                <> "/data/cacheCheck.txt"+                )+                "test"+            xs -> fail $ "expected a single initdb cache directory but got " <> show xs+          xs -> fail $ "expected a single version directory but got " <> show xs++        -- add a file to look for later+        withConfig' (config <> toCacheConfig cacheInfo) $ \db -> do+          -- see if the file is in the data directory+          let theDataDirectory = toDataDirectory db+          xs <- listDirectory theDataDirectory+          xs `shouldContain` ["cacheCheck.txt"]++          assertConnection db++      -- See if cache has been cleaned up+      tmpFiles <- listDirectory dirPath++      case filter ("tmp-postgres-cache" `isPrefixOf`) tmpFiles of+        [] -> pure ()+        xs -> fail $ "unexpected cache dirs. Should be cleaned up. Got: " <> show xs++  it "withDbCache seems to work" $+    withDbCache $ \cacheInfo ->+      either throwIO pure =<< withConfig (silentConfig <> toCacheConfig cacheInfo) assertConnection  -- -- Error Plans. Can't be combined. Just list them out inline since they can't be combined
tmp-postgres.cabal view
@@ -1,5 +1,5 @@ name:                tmp-postgres-version:             1.15.0.0+version:             1.15.1.0 synopsis: Start and stop a temporary postgres description: Start and stop a temporary postgres. See README.md homepage:            https://github.com/jfischoff/tmp-postgres#readme@@ -23,6 +23,9 @@                  , Database.Postgres.Temp.Internal.Config   default-extensions:       ApplicativeDo+    , BlockArguments+    , CPP+    , DeriveAnyClass     , DeriveFunctor     , DeriveGeneric     , DerivingStrategies@@ -37,10 +40,13 @@     , TupleSections     , ViewPatterns   build-depends: base >= 4.6 && < 5+               , base64-bytestring                , ansi-wl-pprint                , async                , bytestring                , containers+               , cryptohash-sha1+               , deepseq                , directory                , generic-monoid                , port-utils@@ -48,6 +54,7 @@                , postgresql-simple                , process >= 1.2.0.0                , random+               , stm                , temporary                , transformers                , unix@@ -77,7 +84,8 @@   ghc-options:        -Wall -threaded -rtsopts -with-rtsopts=-N   default-language:    Haskell2010   default-extensions:-      DeriveDataTypeable+      BlockArguments+    , DeriveDataTypeable     , DeriveGeneric     , DerivingStrategies     , DerivingVia