module Database.CQL.IO.PrepQuery
( PrepQuery
, prepared
, queryString
, PreparedQueries
, new
, lookupQueryId
, lookupQueryString
, insert
, delete
, queryStrings
) where
import Control.Applicative
import Control.Concurrent.STM
import Control.Monad
import Crypto.Hash
import Crypto.Hash.Algorithms (SHA1)
import Data.ByteString (ByteString)
import Data.Text.Lazy (Text)
import Data.Text.Lazy.Encoding (encodeUtf8)
import Data.Foldable (for_)
import Data.Map.Strict (Map)
import Data.String
import Database.CQL.Protocol hiding (Map)
import Database.CQL.IO.Exception (HashCollision (..))
import Prelude
import qualified Data.Map.Strict as M
data PrepQuery k a b = PrepQuery
{ pqStr :: !(QueryString k a b)
, pqId :: !PrepQueryId
}
instance IsString (PrepQuery k a b) where
fromString = prepared . fromString
newtype PrepQueryId = PrepQueryId (Digest SHA1) deriving (Eq, Ord)
prepared :: QueryString k a b -> PrepQuery k a b
prepared q = PrepQuery q $ PrepQueryId (hashlazy . encodeUtf8 . unQueryString $ q)
queryString :: PrepQuery k a b -> QueryString k a b
queryString = pqStr
newtype QST = QST { unQST :: Text }
newtype QID = QID { unQID :: ByteString } deriving (Eq, Ord)
data PreparedQueries = PreparedQueries
{ queryMap :: !(TVar (Map PrepQueryId (QID, QST)))
, qid2Str :: !(TVar (Map QID QST))
}
new :: IO PreparedQueries
new = PreparedQueries <$> newTVarIO M.empty <*> newTVarIO M.empty
lookupQueryId :: PrepQuery k a b -> PreparedQueries -> STM (Maybe (QueryId k a b))
lookupQueryId q m = do
qm <- readTVar (queryMap m)
return $ QueryId . unQID . fst <$> M.lookup (pqId q) qm
lookupQueryString :: QueryId k a b -> PreparedQueries -> STM (Maybe (QueryString k a b))
lookupQueryString q m = do
qm <- readTVar (qid2Str m)
return $ QueryString . unQST <$> M.lookup (QID $ unQueryId q) qm
insert :: PrepQuery k a b -> QueryId k a b -> PreparedQueries -> STM ()
insert q i m = do
qq <- M.lookup (pqId q) <$> readTVar (queryMap m)
for_ qq (verify . snd)
modifyTVar' (queryMap m) $
M.insert (pqId q) (QID $ unQueryId i, QST $ unQueryString (pqStr q))
modifyTVar' (qid2Str m) $
M.insert (QID $ unQueryId i) (QST $ unQueryString (pqStr q))
where
verify qs =
unless (unQST qs == unQueryString (pqStr q)) $ do
let a = unQST qs
let b = unQueryString (pqStr q)
throwSTM (HashCollision a b)
delete :: PrepQuery k a b -> PreparedQueries -> STM ()
delete q m = do
qid <- M.lookup (pqId q) <$> readTVar (queryMap m)
modifyTVar' (queryMap m) $ M.delete (pqId q)
case qid of
Nothing -> return ()
Just i -> modifyTVar' (qid2Str m) $ M.delete (fst i)
queryStrings :: PreparedQueries -> STM [Text]
queryStrings m = map (unQST . snd) . M.elems <$> readTVar (queryMap m)