2015-12-09 21:00:37 +00:00
|
|
|
|
{- Sqlite database of information about Keys
|
2015-12-07 17:42:03 +00:00
|
|
|
|
-
|
|
|
|
|
- Copyright 2015 Joey Hess <id@joeyh.name>
|
|
|
|
|
-:
|
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell #-}
|
|
|
|
|
{-# LANGUAGE OverloadedStrings, GADTs, FlexibleContexts #-}
|
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
|
|
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
module Database.Keys (
|
2015-12-07 17:42:03 +00:00
|
|
|
|
DbHandle,
|
|
|
|
|
openDb,
|
|
|
|
|
closeDb,
|
2015-12-09 21:47:05 +00:00
|
|
|
|
shutdown,
|
2015-12-09 21:00:37 +00:00
|
|
|
|
addAssociatedFile,
|
|
|
|
|
getAssociatedFiles,
|
2015-12-15 17:05:23 +00:00
|
|
|
|
getAssociatedKey,
|
2015-12-09 21:00:37 +00:00
|
|
|
|
removeAssociatedFile,
|
2015-12-09 21:47:05 +00:00
|
|
|
|
storeInodeCaches,
|
|
|
|
|
addInodeCaches,
|
|
|
|
|
getInodeCaches,
|
|
|
|
|
removeInodeCaches,
|
2015-12-07 17:42:03 +00:00
|
|
|
|
AssociatedId,
|
2015-12-09 21:47:05 +00:00
|
|
|
|
ContentId,
|
2015-12-07 17:42:03 +00:00
|
|
|
|
) where
|
|
|
|
|
|
|
|
|
|
import Database.Types
|
2015-12-09 21:00:37 +00:00
|
|
|
|
import Database.Keys.Types
|
2015-12-07 17:42:03 +00:00
|
|
|
|
import qualified Database.Handle as H
|
|
|
|
|
import Locations
|
|
|
|
|
import Common hiding (delete)
|
|
|
|
|
import Annex
|
|
|
|
|
import Types.Key
|
|
|
|
|
import Annex.Perms
|
|
|
|
|
import Annex.LockFile
|
|
|
|
|
import Messages
|
2015-12-09 21:00:37 +00:00
|
|
|
|
import Utility.InodeCache
|
2015-12-09 21:47:05 +00:00
|
|
|
|
import Annex.InodeSentinal
|
2015-12-07 17:42:03 +00:00
|
|
|
|
|
|
|
|
|
import Database.Persist.TH
|
|
|
|
|
import Database.Esqueleto hiding (Key)
|
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
share [mkPersist sqlSettings, mkMigrate "migrateKeysDb"] [persistLowerCase|
|
2015-12-07 17:42:03 +00:00
|
|
|
|
Associated
|
|
|
|
|
key SKey
|
|
|
|
|
file FilePath
|
|
|
|
|
KeyFileIndex key file
|
2015-12-09 21:47:05 +00:00
|
|
|
|
Content
|
2015-12-09 21:00:37 +00:00
|
|
|
|
key SKey
|
2015-12-09 21:47:05 +00:00
|
|
|
|
cache SInodeCache
|
|
|
|
|
KeyCacheIndex key cache
|
2015-12-07 17:42:03 +00:00
|
|
|
|
|]
|
|
|
|
|
|
|
|
|
|
{- Opens the database, creating it if it doesn't exist yet. -}
|
|
|
|
|
openDb :: Annex DbHandle
|
2015-12-09 21:00:37 +00:00
|
|
|
|
openDb = withExclusiveLock gitAnnexKeysDbLock $ do
|
|
|
|
|
dbdir <- fromRepo gitAnnexKeysDb
|
2015-12-07 17:42:03 +00:00
|
|
|
|
let db = dbdir </> "db"
|
|
|
|
|
unlessM (liftIO $ doesFileExist db) $ do
|
|
|
|
|
liftIO $ do
|
|
|
|
|
createDirectoryIfMissing True dbdir
|
|
|
|
|
H.initDb db $ void $
|
2015-12-09 21:00:37 +00:00
|
|
|
|
runMigrationSilent migrateKeysDb
|
2015-12-07 17:42:03 +00:00
|
|
|
|
setAnnexDirPerm dbdir
|
|
|
|
|
setAnnexFilePerm db
|
2015-12-09 21:47:05 +00:00
|
|
|
|
h <- liftIO $ H.openDb db "content"
|
2015-12-07 17:42:03 +00:00
|
|
|
|
|
|
|
|
|
-- work around https://github.com/yesodweb/persistent/issues/474
|
|
|
|
|
liftIO setConsoleEncoding
|
|
|
|
|
|
|
|
|
|
return $ DbHandle h
|
|
|
|
|
|
|
|
|
|
closeDb :: DbHandle -> IO ()
|
|
|
|
|
closeDb (DbHandle h) = H.closeDb h
|
|
|
|
|
|
2015-12-09 18:55:47 +00:00
|
|
|
|
withDbHandle :: (H.DbHandle -> IO a) -> Annex a
|
|
|
|
|
withDbHandle a = do
|
|
|
|
|
(DbHandle h) <- dbHandle
|
|
|
|
|
liftIO $ a h
|
|
|
|
|
|
|
|
|
|
dbHandle :: Annex DbHandle
|
2015-12-09 21:00:37 +00:00
|
|
|
|
dbHandle = maybe startup return =<< Annex.getState Annex.keysdbhandle
|
2015-12-09 18:55:47 +00:00
|
|
|
|
where
|
|
|
|
|
startup = do
|
|
|
|
|
h <- openDb
|
2015-12-09 21:00:37 +00:00
|
|
|
|
Annex.changeState $ \s -> s { Annex.keysdbhandle = Just h }
|
2015-12-09 18:55:47 +00:00
|
|
|
|
return h
|
|
|
|
|
|
2015-12-09 21:47:05 +00:00
|
|
|
|
shutdown :: Annex ()
|
|
|
|
|
shutdown = maybe noop go =<< Annex.getState Annex.keysdbhandle
|
|
|
|
|
where
|
|
|
|
|
go h = do
|
|
|
|
|
Annex.changeState $ \s -> s { Annex.keysdbhandle = Nothing }
|
|
|
|
|
liftIO $ closeDb h
|
2015-12-09 18:55:47 +00:00
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
addAssociatedFile :: Key -> FilePath -> Annex ()
|
|
|
|
|
addAssociatedFile k f = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $ do
|
2015-12-07 17:42:03 +00:00
|
|
|
|
-- If the same file was associated with a different key before,
|
|
|
|
|
-- remove that.
|
|
|
|
|
delete $ from $ \r -> do
|
|
|
|
|
where_ (r ^. AssociatedFile ==. val f &&. r ^. AssociatedKey ==. val sk)
|
|
|
|
|
void $ insertUnique $ Associated sk f
|
|
|
|
|
where
|
|
|
|
|
sk = toSKey k
|
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
{- Note that the files returned were once associated with the key, but
|
2015-12-07 17:42:03 +00:00
|
|
|
|
- some of them may not be any longer. -}
|
2015-12-09 21:00:37 +00:00
|
|
|
|
getAssociatedFiles :: Key -> Annex [FilePath]
|
|
|
|
|
getAssociatedFiles k = withDbHandle $ \h -> H.queryDb h $
|
|
|
|
|
getAssociatedFiles' $ toSKey k
|
2015-12-07 17:42:03 +00:00
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
getAssociatedFiles' :: SKey -> SqlPersistM [FilePath]
|
|
|
|
|
getAssociatedFiles' sk = do
|
2015-12-07 17:42:03 +00:00
|
|
|
|
l <- select $ from $ \r -> do
|
|
|
|
|
where_ (r ^. AssociatedKey ==. val sk)
|
|
|
|
|
return (r ^. AssociatedFile)
|
|
|
|
|
return $ map unValue l
|
|
|
|
|
|
2015-12-15 17:05:23 +00:00
|
|
|
|
{- Gets any keys that are on record as having a particular associated file.
|
|
|
|
|
- (Should be one or none but the database doesn't enforce that.) -}
|
|
|
|
|
getAssociatedKey :: FilePath -> Annex [Key]
|
|
|
|
|
getAssociatedKey f = withDbHandle $ \h -> H.queryDb h $
|
|
|
|
|
getAssociatedKey' f
|
|
|
|
|
|
|
|
|
|
getAssociatedKey' :: FilePath -> SqlPersistM [Key]
|
|
|
|
|
getAssociatedKey' f = do
|
|
|
|
|
l <- select $ from $ \r -> do
|
|
|
|
|
where_ (r ^. AssociatedFile ==. val f)
|
|
|
|
|
return (r ^. AssociatedKey)
|
|
|
|
|
return $ map (fromSKey . unValue) l
|
|
|
|
|
|
2015-12-09 21:00:37 +00:00
|
|
|
|
removeAssociatedFile :: Key -> FilePath -> Annex ()
|
2015-12-09 21:47:05 +00:00
|
|
|
|
removeAssociatedFile k f = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
|
2015-12-07 17:42:03 +00:00
|
|
|
|
delete $ from $ \r -> do
|
|
|
|
|
where_ (r ^. AssociatedKey ==. val sk &&. r ^. AssociatedFile ==. val f)
|
|
|
|
|
where
|
|
|
|
|
sk = toSKey k
|
2015-12-09 21:00:37 +00:00
|
|
|
|
|
2015-12-09 21:47:05 +00:00
|
|
|
|
{- Stats the files, and stores their InodeCaches. -}
|
|
|
|
|
storeInodeCaches :: Key -> [FilePath] -> Annex ()
|
|
|
|
|
storeInodeCaches k fs = withTSDelta $ \d ->
|
|
|
|
|
addInodeCaches k . catMaybes =<< liftIO (mapM (`genInodeCache` d) fs)
|
|
|
|
|
|
|
|
|
|
addInodeCaches :: Key -> [InodeCache] -> Annex ()
|
|
|
|
|
addInodeCaches k is = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
|
|
|
|
|
forM_ is $ \i -> insertUnique $ Content (toSKey k) (toSInodeCache i)
|
2015-12-09 21:00:37 +00:00
|
|
|
|
|
2015-12-09 21:47:05 +00:00
|
|
|
|
{- A key may have multiple InodeCaches; one for the annex object, and one
|
|
|
|
|
- for each pointer file that is a copy of it. -}
|
|
|
|
|
getInodeCaches :: Key -> Annex [InodeCache]
|
|
|
|
|
getInodeCaches k = withDbHandle $ \h -> H.queryDb h $ do
|
2015-12-09 21:00:37 +00:00
|
|
|
|
l <- select $ from $ \r -> do
|
2015-12-09 21:47:05 +00:00
|
|
|
|
where_ (r ^. ContentKey ==. val sk)
|
|
|
|
|
return (r ^. ContentCache)
|
|
|
|
|
return $ map (fromSInodeCache . unValue) l
|
|
|
|
|
where
|
|
|
|
|
sk = toSKey k
|
|
|
|
|
|
|
|
|
|
removeInodeCaches :: Key -> Annex ()
|
|
|
|
|
removeInodeCaches k = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
|
|
|
|
|
delete $ from $ \r -> do
|
|
|
|
|
where_ (r ^. ContentKey ==. val sk)
|
2015-12-09 21:00:37 +00:00
|
|
|
|
where
|
|
|
|
|
sk = toSKey k
|