#include <QUrl>
#include <QDir>
#include <sqlite3.h>
+#include <cstring>
#include "common/syncjournaldb.h"
#include "version.h"
return sqlFail("Set PRAGMA case_sensitivity", pragma1);
}
+ sqlite3_create_function(_db.sqliteDb(), "parent_hash", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
+ [] (sqlite3_context *ctx,int, sqlite3_value **argv) {
+ auto text = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+ const char *end = std::strrchr(text, '/');
+ if (!end) end = text;
+ sqlite3_result_int64(ctx, c_jhash64(reinterpret_cast<const uint8_t*>(text),
+ end - text, 0));
+ }, nullptr, nullptr);
+
/* Because insert is so slow, we do everything in a transaction, and only need one call to commit */
startTransaction();
commitInternal("update database structure: add path index");
}
+ if (1) {
+ SqlQuery query(_db);
+ query.prepare("CREATE INDEX IF NOT EXISTS metadata_parent ON metadata(parent_hash(path));");
+ if (!query.exec()) {
+ sqlFail("updateMetadataTableStructure: create index parent", query);
+ re = false;
+ }
+ commitInternal("update database structure: add parent index");
+ }
+
if (columns.indexOf("ignoredChildrenRemote") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN ignoredChildrenRemote INT;");
qint64 SyncJournalDb::getPHash(const QByteArray &file)
{
int64_t h = 0;
-
- if (file.isEmpty()) {
- return -1;
- }
-
int len = file.length();
h = c_jhash64((uint8_t *)file.data(), len, 0);
return true;
}
+bool SyncJournalDb::listFilesInPath(const QByteArray& path,
+ const std::function<void (const SyncJournalFileRecord &)>& rowCallback)
+{
+ QMutexLocker locker(&_mutex);
+
+ if (_metadataTableIsEmpty)
+ return true;
+
+ if (!checkConnect())
+ return false;
+
+ if (!_listFilesInPathQuery.initOrReset(QByteArrayLiteral(
+ GET_FILE_RECORD_QUERY " WHERE parent_hash(path) = ?1 ORDER BY path||'/' ASC"), _db))
+ return false;
+
+ _listFilesInPathQuery.bindValue(1, getPHash(path));
+
+ if (!_listFilesInPathQuery.exec())
+ return false;
+
+ while (_listFilesInPathQuery.next()) {
+ SyncJournalFileRecord rec;
+ fillFileRecordFromGetQuery(rec, _listFilesInPathQuery);
+ if (!rec._path.startsWith(path) || rec._path.indexOf("/", path.size() + 1) > 0) {
+ qWarning(lcDb) << "hash collision " << path << rec._path;
+ continue;
+ }
+ rowCallback(rec);
+ }
+
+ return true;
+}
+
int SyncJournalDb::getFileRecordCount()
{
QMutexLocker locker(&_mutex);
bool getFileRecordByInode(quint64 inode, SyncJournalFileRecord *rec);
bool getFileRecordsByFileId(const QByteArray &fileId, const std::function<void(const SyncJournalFileRecord &)> &rowCallback);
bool getFilesBelowPath(const QByteArray &path, const std::function<void(const SyncJournalFileRecord&)> &rowCallback);
+ bool listFilesInPath(const QByteArray &path, const std::function<void(const SyncJournalFileRecord&)> &rowCallback);
bool setFileRecord(const SyncJournalFileRecord &record);
/// Like setFileRecord, but preserves checksums
SqlQuery _getFileRecordQueryByFileId;
SqlQuery _getFilesBelowPathQuery;
SqlQuery _getAllFilesQuery;
+ SqlQuery _listFilesInPathQuery;
SqlQuery _setFileRecordQuery;
SqlQuery _setFileRecordChecksumQuery;
SqlQuery _setFileRecordLocalMetadataQuery;
// fetch all the name from the DB
auto pathU8 = _currentFolder._original.toUtf8();
- // FIXME do that better (a query that do not get stuff recursively ?)
- if (!_discoveryData->_statedb->getFilesBelowPath(pathU8, [&](const SyncJournalFileRecord &rec) {
- if (rec._path.indexOf("/", pathU8.size() + 1) > 0)
- return;
+ if (!_discoveryData->_statedb->listFilesInPath(pathU8, [&](const SyncJournalFileRecord &rec) {
auto name = pathU8.isEmpty() ? rec._path : QString::fromUtf8(rec._path.mid(pathU8.size() + 1));
if (rec._type == ItemTypeVirtualFile || rec._type == ItemTypeVirtualFileDownload) {
name.chop(_discoveryData->_syncOptions._virtualFileSuffix.size());