Database: Add an index on the parent path

So we can quickly query the items in a parent directory

This uses a custom slite3 function, which means that when downgrading the client,
or using another tool to add entries in the database, any insertion in the metadata
table will produce an error: "unknown function: parent_hash()"
(This will crash the client 2.5)
This commit is contained in:
Olivier Goffart 2018-10-20 13:24:31 +02:00 committed by Kevin Ottens
parent e45e57982d
commit 58eaf9940a
No known key found for this signature in database
GPG key ID: 074BBBCB8DECC9E2
3 changed files with 56 additions and 9 deletions

View file

@ -24,6 +24,7 @@
#include <QUrl>
#include <QDir>
#include <sqlite3.h>
#include <cstring>
#include "common/syncjournaldb.h"
#include "version.h"
@ -342,6 +343,15 @@ bool SyncJournalDb::checkConnect()
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();
@ -682,6 +692,16 @@ bool SyncJournalDb::updateMetadataTableStructure()
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;");
@ -847,11 +867,6 @@ QVector<QByteArray> SyncJournalDb::tableColumns(const QByteArray &table)
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);
@ -1161,6 +1176,39 @@ bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::functio
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);

View file

@ -61,6 +61,7 @@ public:
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
@ -276,6 +277,7 @@ private:
SqlQuery _getFileRecordQueryByFileId;
SqlQuery _getFilesBelowPathQuery;
SqlQuery _getAllFilesQuery;
SqlQuery _listFilesInPathQuery;
SqlQuery _setFileRecordQuery;
SqlQuery _setFileRecordChecksumQuery;
SqlQuery _setFileRecordLocalMetadataQuery;

View file

@ -98,10 +98,7 @@ void ProcessDirectoryJob::process()
// 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());