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 +4/−0
- benchmark/Main.hs +32/−7
- src/Database/Postgres/Temp.hs +4/−0
- src/Database/Postgres/Temp/Internal.hs +116/−0
- src/Database/Postgres/Temp/Internal/Config.hs +22/−11
- src/Database/Postgres/Temp/Internal/Core.hs +119/−6
- test/Main.hs +64/−6
- tmp-postgres.cabal +10/−2
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