mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2025-04-09 19:15:43 +02:00
vfs: Introduce PinState db storage #6815
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:
parent
dfedb09fd8
commit
68126ac208
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user