query.exec();
}
-Optional<PinState> SyncJournalDb::rawPinStateForPath(const QByteArray &path)
+Optional<PinState> SyncJournalDb::PinStateInterface::rawForPath(const QByteArray &path)
{
- QMutexLocker lock(&_mutex);
- if (!checkConnect())
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
return {};
- auto &query = _getRawPinStateQuery;
+ auto &query = _db->_getRawPinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
"SELECT pinState FROM flags WHERE path == ?1;"),
- _db));
+ _db->_db));
query.bindValue(1, path);
query.exec();
return static_cast<PinState>(query.intValue(0));
}
-Optional<PinState> SyncJournalDb::effectivePinStateForPath(const QByteArray &path)
+Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPath(const QByteArray &path)
{
- QMutexLocker lock(&_mutex);
- if (!checkConnect())
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
return {};
- auto &query = _getEffectivePinStateQuery;
+ auto &query = _db->_getEffectivePinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
"SELECT pinState FROM flags WHERE"
// explicitly allow "" to represent the root path
" (" IS_PREFIX_PATH_OR_EQUAL("path", "?1") " OR path == '')"
" AND pinState is not null AND pinState != 0"
" ORDER BY length(path) DESC;"),
- _db));
+ _db->_db));
query.bindValue(1, path);
query.exec();
return static_cast<PinState>(query.intValue(0));
}
-void SyncJournalDb::setPinStateForPath(const QByteArray &path, PinState state)
+void SyncJournalDb::PinStateInterface::setForPath(const QByteArray &path, PinState state)
{
- QMutexLocker lock(&_mutex);
- if (!checkConnect())
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
return;
- auto &query = _setPinStateQuery;
+ auto &query = _db->_setPinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
// If we had sqlite >=3.24.0 everywhere this could be an upsert,
// making further flags columns easy
//" 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));
+ _db->_db));
query.bindValue(1, path);
query.bindValue(2, static_cast<int>(state));
query.exec();
}
-void SyncJournalDb::wipePinStateForPathAndBelow(const QByteArray &path)
+void SyncJournalDb::PinStateInterface::wipeForPathAndBelow(const QByteArray &path)
{
- QMutexLocker lock(&_mutex);
- if (!checkConnect())
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
return;
- auto &query = _wipePinStateQuery;
+ auto &query = _db->_wipePinStateQuery;
ASSERT(query.initOrReset(QByteArrayLiteral(
"DELETE FROM flags WHERE "
// Allow "" to delete everything
" (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"),
- _db));
+ _db->_db));
query.bindValue(1, path);
query.exec();
}
-Optional<QVector<QPair<QByteArray, PinState>>> SyncJournalDb::rawPinStates()
+Optional<QVector<QPair<QByteArray, PinState>>>
+SyncJournalDb::PinStateInterface::rawList()
{
- QMutexLocker lock(&_mutex);
- if (!checkConnect())
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
return {};
- SqlQuery query("SELECT path, pinState FROM flags;", _db);
+ SqlQuery query("SELECT path, pinState FROM flags;", _db->_db);
query.exec();
QVector<QPair<QByteArray, PinState>> result;
return result;
}
+SyncJournalDb::PinStateInterface SyncJournalDb::internalPinStates()
+{
+ return {this};
+}
+
void SyncJournalDb::commit(const QString &context, bool startTrans)
{
QMutexLocker lock(&_mutex);
*/
void markVirtualFileForDownloadRecursively(const QByteArray &path);
- /**
- * Gets the PinState for the path without considering parents.
- *
- * If a path has no explicit PinState "Inherited" is returned.
+ /** Grouping for all functions relating to pin states,
*
- * The path should not have a trailing slash.
- * It's valid to use the root path "".
- *
- * Returns none on db error.
+ * Use internalPinStates() to get at them.
*/
- Optional<PinState> rawPinStateForPath(const QByteArray &path);
+ struct OCSYNC_EXPORT PinStateInterface
+ {
+ PinStateInterface(const PinStateInterface &) = delete;
+ PinStateInterface(PinStateInterface &&) = delete;
- /**
- * Gets the PinState for the path after inheriting from parents.
- *
- * If the exact path has no entry or has an Inherited state,
- * the state of the closest parent path is returned.
- *
- * The path should not have a trailing slash.
- * It's valid to use the root path "".
- *
- * Never returns PinState::Inherited. If the root is "Inherited"
- * or there's an error, "AlwaysLocal" is returned.
- *
- * Returns none on db error.
- */
- Optional<PinState> effectivePinStateForPath(const QByteArray &path);
+ /**
+ * Gets the PinState for the path without considering parents.
+ *
+ * If a path has no explicit PinState "Inherited" is returned.
+ *
+ * The path should not have a trailing slash.
+ * It's valid to use the root path "".
+ *
+ * Returns none on db error.
+ */
+ Optional<PinState> rawForPath(const QByteArray &path);
- /**
- * Sets a path's pin state.
- *
- * The path should not have a trailing slash.
- * It's valid to use the root path "".
- */
- void setPinStateForPath(const QByteArray &path, PinState state);
+ /**
+ * Gets the PinState for the path after inheriting from parents.
+ *
+ * If the exact path has no entry or has an Inherited state,
+ * the state of the closest parent path is returned.
+ *
+ * The path should not have a trailing slash.
+ * It's valid to use the root path "".
+ *
+ * Never returns PinState::Inherited. If the root is "Inherited"
+ * or there's an error, "AlwaysLocal" is returned.
+ *
+ * Returns none on db error.
+ */
+ Optional<PinState> effectiveForPath(const QByteArray &path);
- /**
- * Wipes pin states for a path and below.
- *
- * Used when the user asks a subtree to have a particular pin state.
- * The path should not have a trailing slash.
- * The path "" wipes every entry.
- */
- void wipePinStateForPathAndBelow(const QByteArray &path);
+ /**
+ * Sets a path's pin state.
+ *
+ * The path should not have a trailing slash.
+ * It's valid to use the root path "".
+ */
+ void setForPath(const QByteArray &path, PinState state);
- /**
- * Returns list of all paths with their pin state as in the db.
+ /**
+ * Wipes pin states for a path and below.
+ *
+ * Used when the user asks a subtree to have a particular pin state.
+ * The path should not have a trailing slash.
+ * The path "" wipes every entry.
+ */
+ void wipeForPathAndBelow(const QByteArray &path);
+
+ /**
+ * Returns list of all paths with their pin state as in the db.
+ *
+ * Returns nothing on db error.
+ * Note that this will have an entry for "".
+ */
+ Optional<QVector<QPair<QByteArray, PinState>>> rawList();
+
+ SyncJournalDb *_db;
+ };
+ friend struct PinStateInterface;
+
+ /** Access to PinStates stored in the database.
*
- * Returns nothing on db error.
- * Note that this will have an entry for "".
+ * Important: Not all vfs plugins store the pin states in the database,
+ * prefer to use Vfs::pinState() etc.
*/
- Optional<QVector<QPair<QByteArray, PinState>>> rawPinStates();
+ PinStateInterface internalPinStates();
/**
* Only used for auto-test:
bool VfsDefaults::setPinState(const QString &folderPath, PinState state)
{
auto path = folderPath.toUtf8();
- _setupParams.journal->wipePinStateForPathAndBelow(path);
- _setupParams.journal->setPinStateForPath(path, state);
+ _setupParams.journal->internalPinStates().wipeForPathAndBelow(path);
+ _setupParams.journal->internalPinStates().setForPath(path, state);
return true;
}
-Optional<PinState> VfsDefaults::getPinState(const QString &folderPath)
+Optional<PinState> VfsDefaults::pinState(const QString &folderPath)
{
- return _setupParams.journal->effectivePinStateForPath(folderPath.toUtf8());
+ return _setupParams.journal->internalPinStates().effectiveForPath(folderPath.toUtf8());
}
VfsOff::VfsOff(QObject *parent)
*
* folderPath is relative to the sync folder.
*/
- virtual Optional<PinState> getPinState(const QString &folderPath) = 0;
+ virtual Optional<PinState> pinState(const QString &folderPath) = 0;
public slots:
/** Update in-sync state based on SyncFileStatusTracker signal.
// use the journal to back the pinstates
bool setPinState(const QString &folderPath, PinState state) override;
- Optional<PinState> getPinState(const QString &folderPath) override;
+ Optional<PinState> pinState(const QString &folderPath) override;
// access initial setup data
const VfsSetupParams ¶ms() const { return _setupParams; }
folder->setSupportsVirtualFiles(true);
folder->setVfsOnOffSwitchPending(false);
- // Wipe pin states to be sure
- folder->journalDb()->wipePinStateForPathAndBelow("");
- folder->journalDb()->setPinStateForPath("", PinState::OnlineOnly);
+ // Sets pin states to OnlineOnly everywhere
+ folder->setNewFilesAreVirtual(true);
FolderMan::instance()->scheduleFolder(folder);
folder->setVfsOnOffSwitchPending(false);
// Wipe pin states and selective sync db
- folder->journalDb()->wipePinStateForPathAndBelow("");
- folder->journalDb()->setPinStateForPath("", PinState::AlwaysLocal);
+ folder->setNewFilesAreVirtual(false);
folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, {});
FolderMan::instance()->scheduleFolder(folder);
void AccountSettings::slotSetCurrentFolderAvailability(PinState state)
{
+ ASSERT(state == PinState::OnlineOnly || state == PinState::AlwaysLocal);
+
FolderMan *folderMan = FolderMan::instance();
QPointer<Folder> folder = folderMan->folder(selectedFolderAlias());
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
return;
// similar to socket api: set pin state, wipe sub pin-states and sync
- folder->journalDb()->wipePinStateForPathAndBelow("");
- folder->journalDb()->setPinStateForPath("", state);
+ folder->setNewFilesAreVirtual(state == PinState::OnlineOnly);
if (state == PinState::AlwaysLocal) {
folder->downloadVirtualFile("");
bool Folder::newFilesAreVirtual() const
{
- auto pinState = _journal.rawPinStateForPath("");
+ if (!supportsVirtualFiles())
+ return false;
+ auto pinState = _vfs->pinState(QString());
return pinState && *pinState == PinState::OnlineOnly;
}
void Folder::setNewFilesAreVirtual(bool enabled)
{
- _journal.setPinStateForPath("", enabled ? PinState::OnlineOnly : PinState::AlwaysLocal);
+ _vfs->setPinState(QString(), enabled ? PinState::OnlineOnly : PinState::AlwaysLocal);
}
bool Folder::supportsSelectiveSync() const
for (const auto &file : files) {
auto fileData = FileData::get(file);
auto path = fileData.folderRelativePathNoVfsSuffix();
- auto pinState = syncFolder->vfs().getPinState(path);
+ auto pinState = syncFolder->vfs().pinState(path);
if (!pinState) {
// db error
hasAlwaysLocal = true;
{
_pinState = parentState;
if (_queryLocal != ParentDontExist) {
- if (auto state = _discoveryData->_syncOptions._vfs->getPinState(_currentFolder._local)) // ouch! pin local or original?
+ if (auto state = _discoveryData->_syncOptions._vfs->pinState(_currentFolder._local)) // ouch! pin local or original?
_pinState = *state;
}
}
void testPinState()
{
auto make = [&](const QByteArray &path, PinState state) {
- _db.setPinStateForPath(path, state);
- auto pinState = _db.rawPinStateForPath(path);
+ _db.internalPinStates().setForPath(path, state);
+ auto pinState = _db.internalPinStates().rawForPath(path);
QVERIFY(pinState);
QCOMPARE(*pinState, state);
};
auto get = [&](const QByteArray &path) -> PinState {
- auto state = _db.effectivePinStateForPath(path);
+ auto state = _db.internalPinStates().effectiveForPath(path);
if (!state) {
QTest::qFail("couldn't read pin state", __FILE__, __LINE__);
return PinState::Inherited;
return *state;
};
auto getRaw = [&](const QByteArray &path) -> PinState {
- auto state = _db.rawPinStateForPath(path);
+ auto state = _db.internalPinStates().rawForPath(path);
if (!state) {
QTest::qFail("couldn't read pin state", __FILE__, __LINE__);
return PinState::Inherited;
return *state;
};
- _db.wipePinStateForPathAndBelow("");
- auto list = _db.rawPinStates();
+ _db.internalPinStates().wipeForPathAndBelow("");
+ auto list = _db.internalPinStates().rawList();
QCOMPARE(list->size(), 0);
// Make a thrice-nested setup
}
}
- list = _db.rawPinStates();
+ list = _db.internalPinStates().rawList();
QCOMPARE(list->size(), 4 + 9 + 27);
// Baseline direct checks (the fallback for unset root pinstate is AlwaysLocal)
// Wiping
QCOMPARE(getRaw("local/local"), PinState::AlwaysLocal);
- _db.wipePinStateForPathAndBelow("local/local");
+ _db.internalPinStates().wipeForPathAndBelow("local/local");
QCOMPARE(getRaw("local"), PinState::AlwaysLocal);
QCOMPARE(getRaw("local/local"), PinState::Inherited);
QCOMPARE(getRaw("local/local/local"), PinState::Inherited);
QCOMPARE(getRaw("local/local/online"), PinState::Inherited);
- list = _db.rawPinStates();
+ list = _db.internalPinStates().rawList();
QCOMPARE(list->size(), 4 + 9 + 27 - 4);
// Wiping everything
- _db.wipePinStateForPathAndBelow("");
+ _db.internalPinStates().wipeForPathAndBelow("");
QCOMPARE(getRaw(""), PinState::Inherited);
QCOMPARE(getRaw("local"), PinState::Inherited);
QCOMPARE(getRaw("online"), PinState::Inherited);
- list = _db.rawPinStates();
+ list = _db.internalPinStates().rawList();
QCOMPARE(list->size(), 0);
}
folder.switchToVfs(suffixVfs);
// Using this directly doesn't recursively unpin everything
- folder.syncJournal().setPinStateForPath("", PinState::OnlineOnly);
+ folder.syncJournal().internalPinStates().setForPath("", PinState::OnlineOnly);
return suffixVfs;
}
QVERIFY(fakeFolder.syncOnce());
QVERIFY(fakeFolder.currentLocalState().find("A/a1.nextcloud"));
- fakeFolder.syncJournal().setPinStateForPath("", PinState::AlwaysLocal);
+ fakeFolder.syncJournal().internalPinStates().setForPath("", PinState::AlwaysLocal);
// Create a new remote file, it'll not be virtual
fakeFolder.remoteModifier().insert("A/a2");
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
auto setPin = [&] (const QByteArray &path, PinState state) {
- fakeFolder.syncJournal().setPinStateForPath(path, state);
+ fakeFolder.syncJournal().internalPinStates().setForPath(path, state);
};
fakeFolder.remoteModifier().mkdir("local");
QVERIFY(fakeFolder.currentLocalState().find("unspec/file1.nextcloud"));
// Test 2: root is AlwaysLocal
- fakeFolder.syncJournal().setPinStateForPath("", PinState::AlwaysLocal);
+ setPin("", PinState::AlwaysLocal);
fakeFolder.remoteModifier().insert("file2");
fakeFolder.remoteModifier().insert("online/file2");