1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2025-04-09 19:15:43 +02:00

vfs: Introduce PinState db storage

The idea is to allow folders (and later maybe files?) to be
- pinned to be available locally
- pinned to be online only
- inherit their pin from the parent

Where this pinning only controls the default for new files.
Subfolders may have a different pin state, and contained files
may be hydrated or dehydrated based on user actions.

This value is stored in a new 'flags' table. The idea is to store
data there that doesn't necessarily exist for each metadata entry.
The selective sync state could be migrated to this table.
This commit is contained in:
Christian Kamm 2018-11-26 13:40:51 +01:00 committed by Kevin Ottens
parent dfedb09fd8
commit 68126ac208
No known key found for this signature in database
GPG Key ID: 074BBBCB8DECC9E2
3 changed files with 140 additions and 0 deletions

View File

@ -465,6 +465,15 @@ bool SyncJournalDb::checkConnect()
return sqlFail("Create table datafingerprint", createQuery);
}
// create the flags table.
createQuery.prepare("CREATE TABLE IF NOT EXISTS flags ("
"path TEXT PRIMARY KEY,"
"pinState INTEGER"
");");
if (!createQuery.exec()) {
return sqlFail("Create table flags", createQuery);
}
// create the conflicts table.
createQuery.prepare("CREATE TABLE IF NOT EXISTS conflicts("
"path TEXT PRIMARY KEY,"
@ -2062,6 +2071,48 @@ void SyncJournalDb::markVirtualFileForDownloadRecursively(const QByteArray &path
query.exec();
}
PinState SyncJournalDb::pinStateForPath(const QByteArray &path)
{
QMutexLocker lock(&_mutex);
if (!checkConnect())
return PinState::Unspecified;
auto &query = _getPinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
"SELECT pinState FROM flags WHERE"
" " IS_PREFIX_PATH_OR_EQUAL("path", "?1")
" AND pinState is not null AND pinState != 0"
" ORDER BY length(path) DESC;"),
_db));
query.bindValue(1, path);
query.exec();
if (!query.next())
return PinState::Unspecified;
return static_cast<PinState>(query.intValue(0));
}
void SyncJournalDb::setPinStateForPath(const QByteArray &path, PinState state)
{
QMutexLocker lock(&_mutex);
if (!checkConnect())
return;
auto &query = _setPinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
// If we had sqlite >=3.24.0 everywhere this could be an upsert,
// making further flags columns easy
//"INSERT INTO flags(path, pinState) VALUES(?1, ?2)"
//" ON CONFLICT(path) DO UPDATE SET pinState=?2;"),
// Simple version that doesn't work nicely with multiple columns:
"INSERT OR REPLACE INTO flags(path, pinState) VALUES(?1, ?2);"),
_db));
query.bindValue(1, path);
query.bindValue(2, static_cast<int>(state));
query.exec();
}
void SyncJournalDb::commit(const QString &context, bool startTrans)
{
QMutexLocker lock(&_mutex);

View File

@ -32,6 +32,24 @@
namespace OCC {
class SyncJournalFileRecord;
/** Determines whether files should be available locally or not
*
* For new remote files the file's PinState is calculated by looking for
* the closest parent folder that isn't Unspecified.
*
* TODO: It seems to make sense to also store per-file PinStates.
* Maybe these could communicate intent, similar to ItemTypeVirtualFileDownload
* and ...FileDehydrate?
*/
enum class PinState {
/// Inherit the PinState of the parent directory (default)
Unspecified = 0,
/// Download file and keep it updated.
AlwaysLocal = 1,
/// File shall be virtual locally.
OnlineOnly = 2,
};
/**
* @brief Class that handles the sync database
*
@ -242,6 +260,19 @@ public:
*/
void markVirtualFileForDownloadRecursively(const QByteArray &path);
/**
* Gets the PinState for the path.
*
* If the exact path has no entry or has an unspecified state,
* the state is inherited through the parent.
*/
PinState pinStateForPath(const QByteArray &path);
/**
* Sets a path's pin state.
*/
void setPinStateForPath(const QByteArray &path, PinState state);
/**
* Only used for auto-test:
* when positive, will decrease the counter for every database operation.
@ -306,6 +337,8 @@ private:
SqlQuery _getConflictRecordQuery;
SqlQuery _setConflictRecordQuery;
SqlQuery _deleteConflictRecordQuery;
SqlQuery _getPinStateQuery;
SqlQuery _setPinStateQuery;
/* Storing etags to these folders, or their parent folders, is filtered out.
*

View File

@ -320,6 +320,62 @@ private slots:
QVERIFY(checkElements());
}
void testPinState()
{
auto make = [&](const QByteArray &path, PinState state) {
_db.setPinStateForPath(path, state);
};
auto get = [&](const QByteArray &path) {
return _db.pinStateForPath(path);
};
// Make a thrice-nested setup
make("local", PinState::AlwaysLocal);
make("online", PinState::OnlineOnly);
make("unspec", PinState::Unspecified);
for (auto base : {"local/", "online/", "unspec/"}) {
make(QByteArray(base) + "unspec", PinState::Unspecified);
make(QByteArray(base) + "local", PinState::AlwaysLocal);
make(QByteArray(base) + "online", PinState::OnlineOnly);
for (auto base2 : {"local/", "online/", "unspec/"}) {
make(QByteArray(base) + base2 + "/unspec", PinState::Unspecified);
make(QByteArray(base) + base2 + "/local", PinState::AlwaysLocal);
make(QByteArray(base) + base2 + "/online", PinState::OnlineOnly);
}
}
// Baseline direct checks
QCOMPARE(get("local"), PinState::AlwaysLocal);
QCOMPARE(get("online"), PinState::OnlineOnly);
QCOMPARE(get("unspec"), PinState::Unspecified);
QCOMPARE(get("nonexistant"), PinState::Unspecified);
QCOMPARE(get("online/local"), PinState::AlwaysLocal);
QCOMPARE(get("local/online"), PinState::OnlineOnly);
QCOMPARE(get("unspec/local"), PinState::AlwaysLocal);
QCOMPARE(get("unspec/online"), PinState::OnlineOnly);
QCOMPARE(get("unspec/unspec"), PinState::Unspecified);
QCOMPARE(get("unspec/nonexistant"), PinState::Unspecified);
// Inheriting checks, level 1
QCOMPARE(get("local/unspec"), PinState::AlwaysLocal);
QCOMPARE(get("local/nonexistant"), PinState::AlwaysLocal);
QCOMPARE(get("online/unspec"), PinState::OnlineOnly);
QCOMPARE(get("online/nonexistant"), PinState::OnlineOnly);
// Inheriting checks, level 2
QCOMPARE(get("local/unspec/unspec"), PinState::AlwaysLocal);
QCOMPARE(get("local/local/unspec"), PinState::AlwaysLocal);
QCOMPARE(get("local/local/nonexistant"), PinState::AlwaysLocal);
QCOMPARE(get("local/online/unspec"), PinState::OnlineOnly);
QCOMPARE(get("local/online/nonexistant"), PinState::OnlineOnly);
QCOMPARE(get("online/unspec/unspec"), PinState::OnlineOnly);
QCOMPARE(get("online/local/unspec"), PinState::AlwaysLocal);
QCOMPARE(get("online/local/nonexistant"), PinState::AlwaysLocal);
QCOMPARE(get("online/online/unspec"), PinState::OnlineOnly);
QCOMPARE(get("online/online/nonexistant"), PinState::OnlineOnly);
}
private:
SyncJournalDb _db;
};