packages feed

hs-hath-1.1.1: src/Storage/Database.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeOperators #-}

module Storage.Database ( FileRecord(..), initializeDB, runCache, runCachePure ) where

import           Data.String.Interpolate ( i )

import           Database.SQLite.Simple

import           Polysemy
import           Polysemy.KVStore        ( KVStore(..), runKVStorePurely )
import           Polysemy.Operators

import           Relude                  hiding ( Reader, State, ask, evalState, get, modify, put )

import           Types                   ( FileRecord(..), FileURI, StorageResult(..) )

{-# INLINE initializeDB #-}
-- | Initialize database with required schema
initializeDB :: Connection -> IO ()
initializeDB conn = do
  execute_
    conn
    [i|CREATE TABLE IF NOT EXISTS files (
        lru_counter INTEGER NOT NULL DEFAULT 0,
        s4 TEXT NOT NULL,
        file_id TEXT PRIMARY KEY,
        file_name TEXT,
        bytes BLOB NOT NULL
    ) strict|]
  execute_ conn "pragma journal_mode=delete"
  execute_ conn "pragma synchronous=normal"
  execute_ conn "pragma temp_store=memory"

    -- execute_ conn "pragma cache_size=100000"
    -- execute_ conn "pragma mmap_size=65536"
-- | Run the cache with SQLite
runCache
  :: Members '[ Embed IO ] r => Connection -> KVStore FileURI StorageResult : r @> a -> r @> a
runCache conn = interpret $ \case
  LookupKV uri -> do
    let fid = show @Text uri
    embed
      $ execute conn "UPDATE files SET lru_counter = lru_counter + 1 WHERE file_id = ?" (Only fid)
    results <- embed
      $ query
        conn
        "SELECT lru_counter, s4, file_id, file_name, bytes FROM files WHERE file_id = ?"
        (Only fid)
    case listToMaybe results of
      Just record -> return $ Just (Record record)
      Nothing     -> return Nothing

  UpdateKV _uri (Just (Redirect _url))
    -> error "impossible: cannot store Redirect in Database backend"

  UpdateKV _ (Just (Record record)) -> do
    embed
      $ execute
        conn
        [i|INSERT OR REPLACE INTO files
                    (lru_counter, s4, file_id, file_name, bytes)
                    VALUES (?, ?, ?, ?, ?)|]
        record

  UpdateKV uri Nothing -> let
      fid = show @ByteString uri
    in 
      embed $ execute conn "DELETE FROM files WHERE file_id = ?" (Only fid)

{-# INLINE runCachePure #-}
runCachePure :: Map FileURI FileRecord -> KVStore FileURI FileRecord : r @> a -> r @> a
runCachePure initial = fmap snd . runKVStorePurely initial