packages feed

experimenter-0.1.0.0: src/Experimenter/DB.hs

{-# LANGUAGE OverloadedStrings #-}
module Experimenter.DB where


import           Conduit                      as C
import           Control.Monad.Logger
import           Control.Monad.Trans.Reader   (ReaderT)
import           Data.Char
import           Data.List                    (foldl')
import qualified Data.Text                    as T
import qualified Database.Esqueleto           as E
import           Database.Persist.Postgresql  (SqlBackend, withPostgresqlPool)

import           Experimenter.DatabaseSetting


type DB m = ReaderT SqlBackend (LoggingT (ResourceT m))
type SimpleDB = DB IO

logFun :: (MonadIO m) => LoggingT m a -> m a
logFun = runStdoutLoggingT . filterLogger (\s _ -> s /= "SQL")

-- logFun :: NoLoggingT m a -> m a
-- logFun = runNoLoggingT


runDB :: (MonadUnliftIO m) => DatabaseSetting -> DB m a -> m a
runDB = runDBWithM runResourceT

runDBSimple :: DatabaseSetting -> SimpleDB a -> IO a
runDBSimple = runDBWithM runResourceT


runDBWithM :: (MonadUnliftIO m1) => (m1 a -> m a) -> DatabaseSetting -> ReaderT SqlBackend (LoggingT m1) a -> m a
runDBWithM runM dbSetting action = runM $ logFun $ withPostgresqlPool (connectionString dbSetting) (parallelConnections dbSetting) $ \pool -> E.runSqlPool action pool


-- withPostgresqlPool :: (MonadLogger m, MonadUnliftIO m) => ConnectionString -> Int -> (Pool SqlBackend -> m a) -> m a
-- runSqlPool :: (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> m a

-- | Create indices for fast lookups in the DB.
indexCreation :: (MonadIO m) => ReaderT SqlBackend (NoLoggingT (ResourceT m)) ()
indexCreation = mapM_ ((\x -> E.rawExecute ("CREATE INDEX IF NOT EXISTS " <> mkName x <> " ON " <> x) []) . mkLowerCase) indices
  where
    mkName txt = foldl' (\acc (from, to) -> T.replace from to acc) (txt <> "index") replacements
    replacements = [("(", "_"), (")", "_"), (",", "_"), ("\"", "")]
    mkLowerCase x = toLowerCase ( T.takeWhile (/= '(') x) <> T.singleton '(' <> toLowerCase (T.tail $ T.dropWhile (/= '(') x)
    toLowerCase =
        let go c
                | isUpper c = T.pack ['_', toLower c]
                | otherwise = T.singleton c
        in T.dropWhile (== '_') . T.concatMap go
    indices =
      [ -- "ExpsInfoParam(exps)"
      -- , "ExpsMaster(exps)"
      -- , "ExpsSetup(exps)"
      -- , "Param(exps)"
      -- , "Exp(exps)"
      -- , "ExpExecutionLock(exp)"
      -- , "ExpProgress(exp)"
      -- , "ParamSetting(exp)"
      -- , "ExpResult(exp)"
      -- , "PrepStartStatePart(resultData)"
      -- , "PrepEndStatePart(resultData)"
      -- , "PrepInput(prepResultData)"
      -- , "PrepInputValue(prepInput)"
      -- , "PrepMeasure(prepResultData)"
      -- , "PrepResultStep(measure)"
      -- , "RepResult(expResult)"

        "RepResultStep(measure)"
      , "WarmUpResultStep(measure)"
      , "PrepResultStep(measure)"

      , "RepMeasure(repResult)"
      , "WarmUpMeasure(repResult)"
      , "PrepMeasure(prepResultData)"

      , "RepInputValue(repInput)"
      , "WarmUpInputValue(warmUpInput)"
      , "PrepInputValue(prepInput)"

      , "RepStartStatePart(resultData)"
      , "RepEndStatePart(resultData)"


      -- , "\"user\"(ident)" -- user is a postgres keyword!
      ]