11#include <QtCore/qdatastream.h>
12#include <QtCore/qdatetime.h>
13#include <QtCore/qdir.h>
14#include <QtCore/qfileinfo.h>
15#include <QtCore/qset.h>
16#include <QtCore/qtimer.h>
17#include <QtCore/qversionnumber.h>
18#include <QtSql/qsqldriver.h>
19#include <QtSql/qsqlerror.h>
20#include <QtSql/qsqlquery.h>
24using namespace Qt::StringLiterals;
36 m_inTransaction = m_db.transaction();
51 m_inTransaction =
false;
61 , m_collectionFile(collectionFile)
63 const QFileInfo fi(m_collectionFile);
65 m_collectionFile = fi.absoluteFilePath();
78 emit that->error(tr(
"The collection file \"%1\" is not set up yet.").arg(m_collectionFile));
88 QSqlDatabase::removeDatabase(m_connectionName);
89 m_connectionName.clear();
97 m_connectionName = QHelpGlobal::uniquifyConnectionName(
"QHelpCollectionHandler"_L1,
this);
99 QSqlDatabase db = QSqlDatabase::addDatabase(
"QSQLITE"_L1, m_connectionName);
101 && db.driver()->lastError().type() == QSqlError::ConnectionError) {
102 emit error(tr(
"Cannot load sqlite database driver."));
106 db.setDatabaseName(collectionFile());
108 m_query.reset(
new QSqlQuery(db));
111 QSqlDatabase::removeDatabase(m_connectionName);
112 emit error(tr(
"Cannot open collection file: %1").arg(collectionFile()));
120 m_query->exec(
"PRAGMA synchronous=OFF"_L1);
121 m_query->exec(
"PRAGMA cache_size=3000"_L1);
123 m_query->exec(
"SELECT COUNT(*) FROM sqlite_master WHERE TYPE=\'table\' "
124 "AND Name=\'NamespaceTable\'"_L1);
127 const bool tablesExist = m_query->value(0).toInt() > 0;
129 if (!createTables(m_query.get())) {
131 emit error(tr(
"Cannot create tables in file %1.").arg(collectionFile()));
136 bool indexAndNamespaceFilterTablesMissing =
false;
138 const QStringList newTables = {
142 "FileFilterTable"_L1,
143 "IndexFilterTable"_L1,
144 "ContentsFilterTable"_L1,
145 "FileAttributeSetTable"_L1,
146 "OptimizedFilterTable"_L1,
151 "ComponentMapping"_L1,
152 "ComponentFilter"_L1,
156 QString queryString =
"SELECT COUNT(*) FROM sqlite_master WHERE TYPE=\'table\'"_L1;
157 queryString.append(
" AND (Name=\'"_L1);
158 queryString.append(newTables.join(
"\' OR Name=\'"_L1));
159 queryString.append(
"\')"_L1);
161 m_query->exec(queryString);
163 if (m_query->value(0).toInt() != newTables.size()) {
164 if (!recreateIndexAndNamespaceFilterTables(m_query.get())) {
165 emit error(tr(
"Cannot create index tables in file %1.").arg(collectionFile()));
170 indexAndNamespaceFilterTablesMissing = tablesExist;
174 if (indexAndNamespaceFilterTablesMissing) {
175 for (
const QHelpCollectionHandler::FileInfo &info : docList) {
176 if (!registerIndexAndNamespaceFilterTables(info.namespaceName,
true)) {
177 emit error(tr(
"Cannot register index tables in file %1.").arg(collectionFile()));
184 QList<TimeStamp> timeStamps;
185 m_query->exec(
"SELECT NamespaceId, FolderId, FilePath, Size, TimeStamp FROM TimeStampTable"_L1);
186 while (m_query->next()) {
188 timeStamp.namespaceId = m_query->value(0).toInt();
189 timeStamp.folderId = m_query->value(1).toInt();
190 timeStamp.fileName = m_query->value(2).toString();
191 timeStamp.size = m_query->value(3).toInt();
192 timeStamp.timeStamp = m_query->value(4).toDateTime();
193 timeStamps.append(timeStamp);
196 QList<TimeStamp> toRemove;
197 for (
const TimeStamp &timeStamp : timeStamps) {
198 if (!isTimeStampCorrect(timeStamp))
199 toRemove.append(timeStamp);
205 for (
const TimeStamp &timeStamp : toRemove) {
206 if (!unregisterIndexTable(timeStamp.namespaceId, timeStamp.folderId)) {
207 emit error(tr(
"Cannot unregister index tables in file %1.").arg(collectionFile()));
213 for (
const QHelpCollectionHandler::FileInfo &info : docList) {
214 if (!hasTimeStampInfo(info.namespaceName)
215 && !registerIndexAndNamespaceFilterTables(info.namespaceName)) {
218 unregisterDocumentation(info.namespaceName);
226 const QFileInfo fi(collectionFile());
227 return QDir::isAbsolutePath(fileName)
229 : QFileInfo(fi.absolutePath() + u'/' + fileName).absoluteFilePath();
234 const QFileInfo fi(absoluteDocPath(timeStamp.fileName));
239 if (fi.size() != timeStamp
.size)
242 if (fi.lastModified(QTimeZone::UTC) != timeStamp.timeStamp)
245 m_query->prepare(
"SELECT FilePath FROM NamespaceTable WHERE Id = ?"_L1);
246 m_query->bindValue(0, timeStamp.namespaceId);
247 if (!m_query->exec() || !m_query->next())
250 const QString oldFileName = m_query->value(0).toString();
252 if (oldFileName != timeStamp.fileName)
262 "TimeStampTable.NamespaceId "
266 "WHERE NamespaceTable.Id = TimeStampTable.NamespaceId "
267 "AND NamespaceTable.Name = ? LIMIT 1"_L1);
268 m_query->bindValue(0, nameSpace);
269 if (!m_query->exec())
272 if (!m_query->next())
281 if (m_vacuumScheduled)
284 m_vacuumScheduled =
true;
285 QTimer::singleShot(0,
this, &QHelpCollectionHandler::execVacuum);
293 m_query->exec(
"VACUUM"_L1);
294 m_vacuumScheduled =
false;
302 const QFileInfo fi(fileName);
304 emit error(tr(
"The collection file \"%1\" already exists.").arg(fileName));
308 if (!fi.absoluteDir().exists() && !QDir().mkpath(fi.absolutePath())) {
309 emit error(tr(
"Cannot create directory: %1").arg(fi.absolutePath()));
313 const QString &colFile = fi.absoluteFilePath();
314 const QString &connectionName =
315 QHelpGlobal::uniquifyConnectionName(
"QHelpCollectionHandlerCopy"_L1,
this);
316 QSqlDatabase db = QSqlDatabase::addDatabase(
"QSQLITE"_L1, connectionName);
317 db.setDatabaseName(colFile);
319 emit error(tr(
"Cannot open collection file: %1").arg(colFile));
323 QSqlQuery copyQuery(db);
324 copyQuery.exec(
"PRAGMA synchronous=OFF"_L1);
325 copyQuery.exec(
"PRAGMA cache_size=3000"_L1);
327 if (!createTables(©Query) || !recreateIndexAndNamespaceFilterTables(©Query)) {
328 emit error(tr(
"Cannot copy collection file: %1").arg(colFile));
332 const QString &oldBaseDir = QFileInfo(collectionFile()).absolutePath();
333 const QFileInfo newColFi(colFile);
334 m_query->exec(
"SELECT Name, FilePath FROM NamespaceTable"_L1);
335 while (m_query->next()) {
336 copyQuery.prepare(
"INSERT INTO NamespaceTable VALUES(NULL, ?, ?)"_L1);
337 copyQuery.bindValue(0, m_query->value(0).toString());
338 QString oldFilePath = m_query->value(1).toString();
339 if (!QDir::isAbsolutePath(oldFilePath))
340 oldFilePath = oldBaseDir + u'/' + oldFilePath;
341 copyQuery.bindValue(1, newColFi.absoluteDir().relativeFilePath(oldFilePath));
345 m_query->exec(
"SELECT NamespaceId, Name FROM FolderTable"_L1);
346 while (m_query->next()) {
347 copyQuery.prepare(
"INSERT INTO FolderTable VALUES(NULL, ?, ?)"_L1);
348 copyQuery.bindValue(0, m_query->value(0).toString());
349 copyQuery.bindValue(1, m_query->value(1).toString());
353 m_query->exec(
"SELECT Name FROM FilterAttributeTable"_L1);
354 while (m_query->next()) {
355 copyQuery.prepare(
"INSERT INTO FilterAttributeTable VALUES(NULL, ?)"_L1);
356 copyQuery.bindValue(0, m_query->value(0).toString());
360 m_query->exec(
"SELECT Name FROM FilterNameTable"_L1);
361 while (m_query->next()) {
362 copyQuery.prepare(
"INSERT INTO FilterNameTable VALUES(NULL, ?)"_L1);
363 copyQuery.bindValue(0, m_query->value(0).toString());
367 m_query->exec(
"SELECT NameId, FilterAttributeId FROM FilterTable"_L1);
368 while (m_query->next()) {
369 copyQuery.prepare(
"INSERT INTO FilterTable VALUES(?, ?)"_L1);
370 copyQuery.bindValue(0, m_query->value(0).toInt());
371 copyQuery.bindValue(1, m_query->value(1).toInt());
375 m_query->exec(
"SELECT Key, Value FROM SettingsTable"_L1);
376 while (m_query->next()) {
377 if (m_query->value(0).toString() ==
"FTS5IndexedNamespaces"_L1)
379 copyQuery.prepare(
"INSERT INTO SettingsTable VALUES(?, ?)"_L1);
380 copyQuery.bindValue(0, m_query->value(0).toString());
381 copyQuery.bindValue(1, m_query->value(1));
386 QSqlDatabase::removeDatabase(connectionName);
392 const QStringList tables = {
393 "CREATE TABLE NamespaceTable ("
394 "Id INTEGER PRIMARY KEY, "
396 "FilePath TEXT )"_L1,
397 "CREATE TABLE FolderTable ("
398 "Id INTEGER PRIMARY KEY, "
399 "NamespaceId INTEGER, "
401 "CREATE TABLE FilterAttributeTable ("
402 "Id INTEGER PRIMARY KEY, "
404 "CREATE TABLE FilterNameTable ("
405 "Id INTEGER PRIMARY KEY, "
407 "CREATE TABLE FilterTable ("
409 "FilterAttributeId INTEGER )"_L1,
410 "CREATE TABLE SettingsTable ("
411 "Key TEXT PRIMARY KEY, "
415 for (
const QString &q : tables) {
424 const QStringList tables = {
425 "DROP TABLE IF EXISTS FileNameTable"_L1,
426 "DROP TABLE IF EXISTS IndexTable"_L1,
427 "DROP TABLE IF EXISTS ContentsTable"_L1,
428 "DROP TABLE IF EXISTS FileFilterTable"_L1,
429 "DROP TABLE IF EXISTS IndexFilterTable"_L1,
430 "DROP TABLE IF EXISTS ContentsFilterTable"_L1,
431 "DROP TABLE IF EXISTS FileAttributeSetTable"_L1,
432 "DROP TABLE IF EXISTS OptimizedFilterTable"_L1,
433 "DROP TABLE IF EXISTS TimeStampTable"_L1,
434 "DROP TABLE IF EXISTS VersionTable"_L1,
435 "DROP TABLE IF EXISTS Filter"_L1,
436 "DROP TABLE IF EXISTS ComponentTable"_L1,
437 "DROP TABLE IF EXISTS ComponentMapping"_L1,
438 "DROP TABLE IF EXISTS ComponentFilter"_L1,
439 "DROP TABLE IF EXISTS VersionFilter"_L1,
440 "CREATE TABLE FileNameTable ("
443 "FileId INTEGER PRIMARY KEY, "
445 "CREATE TABLE IndexTable ("
446 "Id INTEGER PRIMARY KEY, "
449 "NamespaceId INTEGER, "
452 "CREATE TABLE ContentsTable ("
453 "Id INTEGER PRIMARY KEY, "
454 "NamespaceId INTEGER, "
456 "CREATE TABLE FileFilterTable ("
457 "FilterAttributeId INTEGER, "
458 "FileId INTEGER)"_L1,
459 "CREATE TABLE IndexFilterTable ("
460 "FilterAttributeId INTEGER, "
461 "IndexId INTEGER)"_L1,
462 "CREATE TABLE ContentsFilterTable ("
463 "FilterAttributeId INTEGER, "
464 "ContentsId INTEGER )"_L1,
465 "CREATE TABLE FileAttributeSetTable ("
466 "NamespaceId INTEGER, "
467 "FilterAttributeSetId INTEGER, "
468 "FilterAttributeId INTEGER)"_L1,
469 "CREATE TABLE OptimizedFilterTable ("
470 "NamespaceId INTEGER, "
471 "FilterAttributeId INTEGER)"_L1,
472 "CREATE TABLE TimeStampTable ("
473 "NamespaceId INTEGER, "
477 "TimeStamp TEXT)"_L1,
478 "CREATE TABLE VersionTable ("
479 "NamespaceId INTEGER, "
481 "CREATE TABLE Filter ("
482 "FilterId INTEGER PRIMARY KEY, "
484 "CREATE TABLE ComponentTable ("
485 "ComponentId INTEGER PRIMARY KEY, "
487 "CREATE TABLE ComponentMapping ("
488 "ComponentId INTEGER, "
489 "NamespaceId INTEGER)"_L1,
490 "CREATE TABLE ComponentFilter ("
491 "ComponentName TEXT, "
492 "FilterId INTEGER)"_L1,
493 "CREATE TABLE VersionFilter ("
495 "FilterId INTEGER)"_L1
498 for (
const QString &q : tables) {
509 m_query->exec(
"SELECT Name FROM FilterNameTable"_L1);
510 while (m_query->next())
511 list.append(m_query->value(0).toString());
520 m_query->exec(
"SELECT Name FROM Filter ORDER BY Name"_L1);
521 while (m_query->next())
522 list.append(m_query->value(0).toString());
531 m_query->exec(
"SELECT DISTINCT Name FROM ComponentTable ORDER BY Name"_L1);
532 while (m_query->next())
533 list.append(m_query->value(0).toString());
540 QList<QVersionNumber> list;
542 m_query->exec(
"SELECT DISTINCT Version FROM VersionTable ORDER BY Version"_L1);
543 while (m_query->next())
544 list.append(QVersionNumber::fromString(m_query->value(0).toString()));
551 QMap<QString, QString> result;
555 "NamespaceTable.Name, "
556 "ComponentTable.Name "
557 "FROM NamespaceTable, "
560 "WHERE NamespaceTable.Id = ComponentMapping.NamespaceId "
561 "AND ComponentMapping.ComponentId = ComponentTable.ComponentId"_L1);
562 while (m_query->next())
563 result.insert(m_query->value(0).toString(), m_query->value(1).toString());
570 QMap<QString, QVersionNumber> result;
574 "NamespaceTable.Name, "
575 "VersionTable.Version "
576 "FROM NamespaceTable, "
578 "WHERE NamespaceTable.Id = VersionTable.NamespaceId"_L1);
579 while (m_query->next()) {
580 result.insert(m_query->value(0).toString(),
581 QVersionNumber::fromString(m_query->value(1).toString()));
589 QStringList components;
590 QList<QVersionNumber> versions;
593 "SELECT ComponentFilter.ComponentName "
594 "FROM ComponentFilter, Filter "
595 "WHERE ComponentFilter.FilterId = Filter.FilterId "
596 "AND Filter.Name = ? "
597 "ORDER BY ComponentFilter.ComponentName"_L1);
598 m_query->bindValue(0, filterName);
600 while (m_query->next())
601 components.append(m_query->value(0).toString());
604 "SELECT VersionFilter.Version "
605 "FROM VersionFilter, Filter "
606 "WHERE VersionFilter.FilterId = Filter.FilterId "
607 "AND Filter.Name = ? "
608 "ORDER BY VersionFilter.Version"_L1);
609 m_query->bindValue(0, filterName);
611 while (m_query->next())
612 versions.append(QVersionNumber::fromString(m_query->value(0).toString()));
615 QHelpFilterData data;
616 data.setComponents(components);
617 data.setVersions(versions);
622 const QHelpFilterData &filterData)
624 if (!removeFilter(filterName))
627 m_query->prepare(
"INSERT INTO Filter VALUES (NULL, ?)"_L1);
628 m_query->bindValue(0, filterName);
629 if (!m_query->exec())
632 const int filterId = m_query->lastInsertId().toInt();
634 QVariantList componentList;
635 QVariantList versionList;
636 QVariantList filterIdList;
638 const auto &components = filterData.components();
639 for (
const QString &component : components) {
640 componentList.append(component);
641 filterIdList.append(filterId);
644 m_query->prepare(
"INSERT INTO ComponentFilter VALUES (?, ?)"_L1);
645 m_query->addBindValue(componentList);
646 m_query->addBindValue(filterIdList);
647 if (!m_query->execBatch())
650 filterIdList.clear();
651 const auto &versions = filterData.versions();
652 for (
const QVersionNumber &version : versions) {
653 versionList.append(version.isNull() ? QString() : version.toString());
654 filterIdList.append(filterId);
657 m_query->prepare(
"INSERT INTO VersionFilter VALUES (?, ?)"_L1);
658 m_query->addBindValue(versionList);
659 m_query->addBindValue(filterIdList);
660 if (!m_query->execBatch())
668 m_query->prepare(
"SELECT FilterId FROM Filter WHERE Name = ?"_L1);
669 m_query->bindValue(0, filterName);
670 if (!m_query->exec())
673 if (!m_query->next())
676 const int filterId = m_query->value(0).toInt();
678 m_query->prepare(
"DELETE FROM Filter WHERE Filter.Name = ?"_L1);
679 m_query->bindValue(0, filterName);
680 if (!m_query->exec())
683 m_query->prepare(
"DELETE FROM ComponentFilter WHERE ComponentFilter.FilterId = ?"_L1);
684 m_query->bindValue(0, filterId);
685 if (!m_query->exec())
688 m_query->prepare(
"DELETE FROM VersionFilter WHERE VersionFilter.FilterId = ?"_L1);
689 m_query->bindValue(0, filterId);
690 if (!m_query->exec())
698 if (!isDBOpened() || filterName.isEmpty())
701 int filterNameId = -1;
702 m_query->prepare(
"SELECT Id FROM FilterNameTable WHERE Name=?"_L1);
703 m_query->bindValue(0, filterName);
706 filterNameId = m_query->value(0).toInt();
708 if (filterNameId < 0) {
709 emit error(tr(
"Unknown filter \"%1\".").arg(filterName));
713 m_query->prepare(
"DELETE FROM FilterTable WHERE NameId=?"_L1);
714 m_query->bindValue(0, filterNameId);
717 m_query->prepare(
"DELETE FROM FilterNameTable WHERE Id=?"_L1);
718 m_query->bindValue(0, filterNameId);
725 const QStringList &attributes)
727 if (!isDBOpened() || filterName.isEmpty())
731 m_query->prepare(
"SELECT Id FROM FilterNameTable WHERE Name=?"_L1);
732 m_query->bindValue(0, filterName);
735 nameId = m_query->value(0).toInt();
737 m_query->exec(
"SELECT Id, Name FROM FilterAttributeTable"_L1);
738 QStringList idsToInsert = attributes;
739 QMap<QString,
int> attributeMap;
740 while (m_query->next()) {
742 const QString attributeName = m_query->value(1).toString();
743 attributeMap.insert(attributeName, m_query->value(0).toInt());
744 idsToInsert.removeAll(attributeName);
747 for (
const QString &id : std::as_const(idsToInsert)) {
748 m_query->prepare(
"INSERT INTO FilterAttributeTable VALUES(NULL, ?)"_L1);
749 m_query->bindValue(0, id);
751 attributeMap.insert(id, m_query->lastInsertId().toInt());
755 m_query->prepare(
"INSERT INTO FilterNameTable VALUES(NULL, ?)"_L1);
756 m_query->bindValue(0, filterName);
758 nameId = m_query->lastInsertId().toInt();
762 emit error(tr(
"Cannot register filter %1.").arg(filterName));
766 m_query->prepare(
"DELETE FROM FilterTable WHERE NameId=?"_L1);
767 m_query->bindValue(0, nameId);
770 for (
const QString &att : attributes) {
771 m_query->prepare(
"INSERT INTO FilterTable VALUES(?, ?)"_L1);
772 m_query->bindValue(0, nameId);
773 m_query->bindValue(1, attributeMap[att]);
774 if (!m_query->exec())
781 const QString &namespaceName)
const
790 "NamespaceTable.Name, "
791 "NamespaceTable.FilePath, "
796 "WHERE NamespaceTable.Id = FolderTable.NamespaceId "
797 "AND NamespaceTable.Name = ? LIMIT 1"_L1);
798 m_query->bindValue(0, namespaceName);
799 if (!m_query->exec() || !m_query->next())
802 fileInfo.namespaceName = m_query->value(0).toString();
803 fileInfo.fileName = m_query->value(1).toString();
804 fileInfo.folderName = m_query->value(2).toString();
819 "NamespaceTable.Name, "
820 "NamespaceTable.FilePath, "
825 "WHERE NamespaceTable.Id = FolderTable.NamespaceId"_L1);
827 while (m_query->next()) {
829 fileInfo.namespaceName = m_query->value(0).toString();
830 fileInfo.fileName = m_query->value(1).toString();
831 fileInfo.folderName = m_query->value(2).toString();
832 list.append(fileInfo);
843 QHelpDBReader reader(fileName, QHelpGlobal::uniquifyConnectionName(
844 "QHelpCollectionHandler"_L1,
this),
nullptr);
846 emit error(tr(
"Cannot open documentation file %1.").arg(fileName));
850 const QString &ns = reader.namespaceName();
852 emit error(tr(
"Invalid documentation file \"%1\".").arg(fileName));
856 const int nsId = registerNamespace(ns, fileName);
860 const int vfId = registerVirtualFolder(reader.virtualFolder(), nsId);
864 registerVersion(reader.version(), nsId);
865 registerFilterAttributes(reader.filterAttributeSets(), nsId);
866 const auto &customFilters = reader.customFilters();
867 for (
const QString &filterName : customFilters)
868 addCustomFilter(filterName, reader.filterAttributes(filterName));
870 if (!registerIndexTable(reader.indexTable(), nsId, vfId, registeredDocumentation(ns).fileName))
881 m_query->prepare(
"SELECT Id FROM NamespaceTable WHERE Name = ?"_L1);
882 m_query->bindValue(0, namespaceName);
885 if (!m_query->next()) {
886 emit error(tr(
"The namespace %1 was not registered.").arg(namespaceName));
890 const int nsId = m_query->value(0).toInt();
892 m_query->prepare(
"DELETE FROM NamespaceTable WHERE Id = ?"_L1);
893 m_query->bindValue(0, nsId);
894 if (!m_query->exec())
897 m_query->prepare(
"SELECT Id FROM FolderTable WHERE NamespaceId = ?"_L1);
898 m_query->bindValue(0, nsId);
901 if (!m_query->next()) {
902 emit error(tr(
"The namespace %1 was not registered.").arg(namespaceName));
906 const int vfId = m_query->value(0).toInt();
908 m_query->prepare(
"DELETE FROM NamespaceTable WHERE Id = ?"_L1);
909 m_query->bindValue(0, nsId);
910 if (!m_query->exec())
913 m_query->prepare(
"DELETE FROM FolderTable WHERE NamespaceId = ?"_L1);
914 m_query->bindValue(0, nsId);
915 if (!m_query->exec())
918 if (!unregisterIndexTable(nsId, vfId))
928 QHelpCollectionHandler::FileInfo fileInfo;
930 if (!url.isValid() || url.toString().count(u'/') < 4
931 || url.scheme() !=
"qthelp"_L1) {
935 fileInfo.namespaceName = url.authority();
936 fileInfo.fileName = url.path();
937 if (fileInfo.fileName.startsWith(u'/'))
938 fileInfo.fileName = fileInfo.fileName.mid(1);
939 fileInfo.folderName = fileInfo.fileName.mid(0, fileInfo.fileName.indexOf(u'/', 1));
940 fileInfo.fileName.remove(0, fileInfo.folderName.size() + 1);
950 const FileInfo fileInfo = extractFileInfo(url);
951 if (fileInfo.namespaceName.isEmpty())
955 "SELECT COUNT (DISTINCT NamespaceTable.Id) "
960 "WHERE FolderTable.Name = ? "
961 "AND FileNameTable.Name = ? "
962 "AND FileNameTable.FolderId = FolderTable.Id "
963 "AND FolderTable.NamespaceId = NamespaceTable.Id"_L1);
964 m_query->bindValue(0, fileInfo.folderName);
965 m_query->bindValue(1, fileInfo.fileName);
966 if (!m_query->exec() || !m_query->next())
969 const int count = m_query->value(0).toInt();
977 if (filterName.isEmpty())
980 return " AND EXISTS(SELECT * FROM Filter WHERE Filter.Name = ?) "
986 "WHERE ComponentFilter.FilterId = Filter.FilterId "
987 "AND Filter.Name = ?) "
988 "OR NamespaceTable.Id IN ("
997 "WHERE ComponentMapping.NamespaceId = NamespaceTable.Id "
998 "AND ComponentTable.ComponentId = ComponentMapping.ComponentId "
999 "AND ((ComponentTable.Name = ComponentFilter.ComponentName) "
1000 "OR (ComponentTable.Name IS NULL AND ComponentFilter.ComponentName IS NULL)) "
1001 "AND ComponentFilter.FilterId = Filter.FilterId "
1002 "AND Filter.Name = ?))"
1008 "WHERE VersionFilter.FilterId = Filter.FilterId "
1009 "AND Filter.Name = ?) "
1010 "OR NamespaceTable.Id IN ("
1012 "NamespaceTable.Id "
1018 "WHERE VersionFilter.FilterId = Filter.FilterId "
1019 "AND ((VersionFilter.Version = VersionTable.Version) "
1020 "OR (VersionFilter.Version IS NULL AND VersionTable.Version IS NULL)) "
1021 "AND VersionTable.NamespaceId = NamespaceTable.Id "
1022 "AND Filter.Name = ?))"
1028 if (filterName.isEmpty())
1031 query->bindValue(bindStart, filterName);
1032 query->bindValue(bindStart + 1, filterName);
1033 query->bindValue(bindStart + 2, filterName);
1034 query->bindValue(bindStart + 3, filterName);
1035 query->bindValue(bindStart + 4, filterName);
1039 const QString &idTableName,
1040 const QString &idColumnName,
1041 const QString &filterTableName,
1042 const QString &filterColumnName)
1044 if (!attributesCount)
1047 QString filterQuery =
" AND (%1.%2 IN ("_L1.arg(idTableName, idColumnName);
1049 const QString filterQueryTemplate =
1051 "FROM %1, FilterAttributeTable "
1052 "WHERE %1.FilterAttributeId = FilterAttributeTable.Id "
1053 "AND FilterAttributeTable.Name = ?"_L1.arg(filterTableName, filterColumnName);
1055 for (
int i = 0; i < attributesCount; ++i) {
1057 filterQuery.append(
" INTERSECT "_L1);
1058 filterQuery.append(filterQueryTemplate);
1061 filterQuery.append(
") OR NamespaceTable.Id IN ("_L1);
1063 const QString optimizedFilterQueryTemplate =
1064 "SELECT OptimizedFilterTable.NamespaceId "
1065 "FROM OptimizedFilterTable, FilterAttributeTable "
1066 "WHERE OptimizedFilterTable.FilterAttributeId = FilterAttributeTable.Id "
1067 "AND FilterAttributeTable.Name = ?"_L1;
1069 for (
int i = 0; i < attributesCount; ++i) {
1071 filterQuery.append(
" INTERSECT "_L1);
1072 filterQuery.append(optimizedFilterQueryTemplate);
1075 filterQuery.append(
"))"_L1);
1080static void bindFilterQuery(QSqlQuery *query,
int startingBindPos,
const QStringList &filterAttributes)
1082 for (
int i = 0; i < 2; ++i) {
1083 for (
int j = 0; j < filterAttributes.size(); j++) {
1084 query->bindValue(i * filterAttributes.size() + j + startingBindPos,
1085 filterAttributes.at(j));
1091 const QStringList &filterAttributes)
const
1096 const FileInfo fileInfo = extractFileInfo(url);
1097 if (fileInfo.namespaceName.isEmpty())
1100 const QString filterlessQuery =
1102 "NamespaceTable.Name "
1107 "WHERE FolderTable.Name = ? "
1108 "AND FileNameTable.Name = ? "
1109 "AND FileNameTable.FolderId = FolderTable.Id "
1110 "AND FolderTable.NamespaceId = NamespaceTable.Id"_L1;
1112 const QString filterQuery = filterlessQuery
1113 + prepareFilterQuery(filterAttributes.size(),
"FileNameTable"_L1,
"FileId"_L1,
1114 "FileFilterTable"_L1,
"FileId"_L1);
1116 m_query->prepare(filterQuery);
1117 m_query->bindValue(0, fileInfo.folderName);
1118 m_query->bindValue(1, fileInfo.fileName);
1119 bindFilterQuery(m_query.get(), 2, filterAttributes);
1121 if (!m_query->exec())
1124 QStringList namespaceList;
1125 while (m_query->next())
1126 namespaceList.append(m_query->value(0).toString());
1128 if (namespaceList.isEmpty())
1131 if (namespaceList.contains(fileInfo.namespaceName))
1132 return fileInfo.namespaceName;
1134 const QString originalVersion = namespaceVersion(fileInfo.namespaceName);
1136 for (
const QString &ns : std::as_const(namespaceList)) {
1137 const QString nsVersion = namespaceVersion(ns);
1138 if (originalVersion == nsVersion)
1143 return namespaceList.first();
1147 const QString &filterName)
const
1152 const FileInfo fileInfo = extractFileInfo(url);
1153 if (fileInfo.namespaceName.isEmpty())
1156 const QString filterlessQuery =
1158 "NamespaceTable.Name "
1163 "WHERE FolderTable.Name = ? "
1164 "AND FileNameTable.Name = ? "
1165 "AND FileNameTable.FolderId = FolderTable.Id "
1166 "AND FolderTable.NamespaceId = NamespaceTable.Id"_L1;
1168 const QString filterQuery = filterlessQuery
1169 + prepareFilterQuery(filterName);
1171 m_query->prepare(filterQuery);
1172 m_query->bindValue(0, fileInfo.folderName);
1173 m_query->bindValue(1, fileInfo.fileName);
1174 bindFilterQuery(m_query.get(), 2, filterName);
1176 if (!m_query->exec())
1179 QStringList namespaceList;
1180 while (m_query->next())
1181 namespaceList.append(m_query->value(0).toString());
1183 if (namespaceList.isEmpty())
1186 if (namespaceList.contains(fileInfo.namespaceName))
1187 return fileInfo.namespaceName;
1189 const QString originalVersion = namespaceVersion(fileInfo.namespaceName);
1191 for (
const QString &ns : std::as_const(namespaceList)) {
1192 const QString nsVersion = namespaceVersion(ns);
1193 if (originalVersion == nsVersion)
1198 return namespaceList.first();
1202 const QStringList &filterAttributes,
1203 const QString &extensionFilter)
const
1208 const QString extensionQuery = extensionFilter.isEmpty()
1209 ? QString() :
" AND FileNameTable.Name LIKE ?"_L1;
1210 const QString filterlessQuery =
1212 "FolderTable.Name, "
1213 "FileNameTable.Name "
1218 "WHERE FileNameTable.FolderId = FolderTable.Id "
1219 "AND FolderTable.NamespaceId = NamespaceTable.Id "
1220 "AND NamespaceTable.Name = ?"_L1 + extensionQuery;
1222 const QString filterQuery = filterlessQuery
1223 + prepareFilterQuery(filterAttributes.size(),
"FileNameTable"_L1,
"FileId"_L1,
1224 "FileFilterTable"_L1,
"FileId"_L1);
1226 m_query->prepare(filterQuery);
1227 m_query->bindValue(0, namespaceName);
1229 if (!extensionFilter.isEmpty()) {
1230 m_query->bindValue(bindCount,
"%.%1"_L1.arg(extensionFilter));
1233 bindFilterQuery(m_query.get(), bindCount, filterAttributes);
1235 if (!m_query->exec())
1238 QStringList fileNames;
1239 while (m_query->next())
1240 fileNames.append(m_query->value(0).toString() + u'/' + m_query->value(1).toString());
1245 const QString &filterName,
1246 const QString &extensionFilter)
const
1251 const QString extensionQuery = extensionFilter.isEmpty()
1252 ? QString() :
" AND FileNameTable.Name LIKE ?"_L1;
1253 const QString filterlessQuery =
1255 "FolderTable.Name, "
1256 "FileNameTable.Name "
1261 "WHERE FileNameTable.FolderId = FolderTable.Id "
1262 "AND FolderTable.NamespaceId = NamespaceTable.Id "
1263 "AND NamespaceTable.Name = ?"_L1 + extensionQuery;
1265 const QString filterQuery = filterlessQuery
1266 + prepareFilterQuery(filterName);
1268 m_query->prepare(filterQuery);
1269 m_query->bindValue(0, namespaceName);
1271 if (!extensionFilter.isEmpty()) {
1272 m_query->bindValue(bindCount,
"%.%1"_L1.arg(extensionFilter));
1276 bindFilterQuery(m_query.get(), bindCount, filterName);
1278 if (!m_query->exec())
1281 QStringList fileNames;
1282 while (m_query->next())
1283 fileNames.append(m_query->value(0).toString() + u'/' + m_query->value(1).toString());
1292 const QString namespaceName = namespaceForFile(url, filterAttributes);
1293 if (namespaceName.isEmpty())
1297 result.setAuthority(namespaceName);
1306 const QString namespaceName = namespaceForFile(url, filterName);
1307 if (namespaceName.isEmpty())
1311 result.setAuthority(namespaceName);
1320 const QString namespaceName = namespaceForFile(url, QString());
1321 if (namespaceName.isEmpty())
1324 const FileInfo fileInfo = extractFileInfo(url);
1326 const FileInfo docInfo = registeredDocumentation(namespaceName);
1327 const QString absFileName = absoluteDocPath(docInfo.fileName);
1329 QHelpDBReader reader(absFileName, QHelpGlobal::uniquifyConnectionName(
1330 docInfo.fileName,
const_cast<QHelpCollectionHandler *>(
this)),
nullptr);
1334 return reader.fileData(fileInfo.folderName, fileInfo.fileName);
1339 QStringList indices;
1344 const QString filterlessQuery =
1352 "WHERE IndexTable.FileId = FileNameTable.FileId "
1353 "AND FileNameTable.FolderId = FolderTable.Id "
1354 "AND IndexTable.NamespaceId = NamespaceTable.Id"_L1;
1356 const QString filterQuery = filterlessQuery
1357 + prepareFilterQuery(filterAttributes.size(),
"IndexTable"_L1,
"Id"_L1,
1358 "IndexFilterTable"_L1,
"IndexId"_L1)
1359 +
" ORDER BY LOWER(IndexTable.Name), IndexTable.Name"_L1;
1362 m_query->prepare(filterQuery);
1363 bindFilterQuery(m_query.get(), 0, filterAttributes);
1367 while (m_query->next())
1368 indices.append(m_query->value(0).toString());
1375 QStringList indices;
1380 const QString filterlessQuery =
1388 "WHERE IndexTable.FileId = FileNameTable.FileId "
1389 "AND FileNameTable.FolderId = FolderTable.Id "
1390 "AND IndexTable.NamespaceId = NamespaceTable.Id"_L1;
1392 const QString filterQuery = filterlessQuery
1393 + prepareFilterQuery(filterName)
1394 +
" ORDER BY LOWER(IndexTable.Name), IndexTable.Name"_L1;
1396 m_query->prepare(filterQuery);
1397 bindFilterQuery(m_query.get(), 0, filterName);
1401 while (m_query->next())
1402 indices.append(m_query->value(0).toString());
1409 if (!contents.size())
1416 QDataStream s(contents);
1425 const QStringList &filterAttributes)
const
1430 const QString filterlessQuery =
1432 "NamespaceTable.Name, "
1433 "FolderTable.Name, "
1434 "ContentsTable.Data, "
1435 "VersionTable.Version "
1441 "WHERE ContentsTable.NamespaceId = NamespaceTable.Id "
1442 "AND NamespaceTable.Id = FolderTable.NamespaceId "
1443 "AND ContentsTable.NamespaceId = NamespaceTable.Id "
1444 "AND VersionTable.NamespaceId = NamespaceTable.Id"_L1;
1446 const QString filterQuery = filterlessQuery
1447 + prepareFilterQuery(filterAttributes.size(),
"ContentsTable"_L1,
"Id"_L1,
1448 "ContentsFilterTable"_L1,
"ContentsId"_L1);
1450 m_query->prepare(filterQuery);
1451 bindFilterQuery(m_query.get(), 0, filterAttributes);
1455 QMap<QString, QMap<QVersionNumber, ContentsData>> contentsMap;
1457 while (m_query->next()) {
1458 const QString namespaceName = m_query->value(0).toString();
1459 const QByteArray contents = m_query->value(2).toByteArray();
1460 const QString versionString = m_query->value(3).toString();
1462 const QString title = getTitle(contents);
1463 const QVersionNumber version = QVersionNumber::fromString(versionString);
1465 ContentsData &contentsData = contentsMap[title][version];
1466 contentsData.namespaceName = namespaceName;
1467 contentsData.folderName = m_query->value(1).toString();
1468 contentsData.contentsList.append(contents);
1471 QList<QHelpCollectionHandler::ContentsData> result;
1472 for (
const auto &versionContents : std::as_const(contentsMap)) {
1474 const auto itBegin = versionContents.constBegin();
1475 auto it = versionContents.constEnd();
1476 while (it != itBegin) {
1478 result.append(it.value());
1489 const QString filterlessQuery =
1491 "NamespaceTable.Name, "
1492 "FolderTable.Name, "
1493 "ContentsTable.Data, "
1494 "VersionTable.Version "
1500 "WHERE ContentsTable.NamespaceId = NamespaceTable.Id "
1501 "AND NamespaceTable.Id = FolderTable.NamespaceId "
1502 "AND ContentsTable.NamespaceId = NamespaceTable.Id "
1503 "AND VersionTable.NamespaceId = NamespaceTable.Id"_L1;
1505 const QString filterQuery = filterlessQuery + prepareFilterQuery(filterName);
1507 m_query->prepare(filterQuery);
1508 bindFilterQuery(m_query.get(), 0, filterName);
1512 QMap<QString, QMap<QVersionNumber, ContentsData>> contentsMap;
1514 while (m_query->next()) {
1515 const QString namespaceName = m_query->value(0).toString();
1516 const QByteArray contents = m_query->value(2).toByteArray();
1517 const QString versionString = m_query->value(3).toString();
1519 const QString title = getTitle(contents);
1520 const QVersionNumber version = QVersionNumber::fromString(versionString);
1522 ContentsData &contentsData = contentsMap[title][version];
1523 contentsData.namespaceName = namespaceName;
1524 contentsData.folderName = m_query->value(1).toString();
1525 contentsData.contentsList.append(contents);
1528 QList<QHelpCollectionHandler::ContentsData> result;
1529 for (
const auto &versionContents : std::as_const(contentsMap)) {
1531 const auto itBegin = versionContents.constBegin();
1532 auto it = versionContents.constEnd();
1533 while (it != itBegin) {
1535 result.append(it.value());
1546 m_query->prepare(
"DELETE FROM SettingsTable WHERE Key=?"_L1);
1547 m_query->bindValue(0, key);
1548 return m_query->exec();
1552 const QVariant &defaultValue)
const
1555 return defaultValue;
1557 m_query->prepare(
"SELECT COUNT(Key) FROM SettingsTable WHERE Key=?"_L1);
1558 m_query->bindValue(0, key);
1559 if (!m_query->exec() || !m_query->next() || !m_query->value(0).toInt()) {
1561 return defaultValue;
1565 m_query->prepare(
"SELECT Value FROM SettingsTable WHERE Key=?"_L1);
1566 m_query->bindValue(0, key);
1567 if (m_query->exec() && m_query->next()) {
1568 const QVariant &value = m_query->value(0);
1572 return defaultValue;
1576 const QVariant &value)
1581 m_query->prepare(
"SELECT Value FROM SettingsTable WHERE Key=?"_L1);
1582 m_query->bindValue(0, key);
1584 if (m_query->next()) {
1585 m_query->prepare(
"UPDATE SettingsTable SET Value=? where Key=?"_L1);
1586 m_query->bindValue(0, value);
1587 m_query->bindValue(1, key);
1589 m_query->prepare(
"INSERT INTO SettingsTable VALUES(?, ?)"_L1);
1590 m_query->bindValue(0, key);
1591 m_query->bindValue(1, value);
1593 return m_query->exec();
1602 m_query->exec(
"SELECT Name FROM FilterAttributeTable"_L1);
1604 while (m_query->next())
1605 atts.insert(m_query->value(0).toString());
1607 for (
const QStringList &attributeSet : attributeSets) {
1608 for (
const QString &attribute : attributeSet) {
1609 if (!atts.contains(attribute)) {
1610 m_query->prepare(
"INSERT INTO FilterAttributeTable VALUES(NULL, ?)"_L1);
1611 m_query->bindValue(0, attribute);
1616 return registerFileAttributeSets(attributeSets, nsId);
1625 if (attributeSets.isEmpty())
1629 QVariantList attributeSetIds;
1630 QVariantList filterAttributeIds;
1632 if (!m_query->exec(
"SELECT MAX(FilterAttributeSetId) FROM FileAttributeSetTable"_L1)
1633 || !m_query->next()) {
1637 int attributeSetId = m_query->value(0).toInt();
1639 for (
const QStringList &attributeSet : attributeSets) {
1642 for (
const QString &attribute : attributeSet) {
1643 m_query->prepare(
"SELECT Id FROM FilterAttributeTable WHERE Name=?"_L1);
1644 m_query->bindValue(0, attribute);
1646 if (!m_query->exec() || !m_query->next())
1650 attributeSetIds.append(attributeSetId);
1651 filterAttributeIds.append(m_query->value(0).toInt());
1655 m_query->prepare(
"INSERT INTO FileAttributeSetTable "
1656 "(NamespaceId, FilterAttributeSetId, FilterAttributeId) "
1657 "VALUES(?, ?, ?)"_L1);
1658 m_query->addBindValue(nsIds);
1659 m_query->addBindValue(attributeSetIds);
1660 m_query->addBindValue(filterAttributeIds);
1661 return m_query->execBatch();
1668 m_query->exec(
"SELECT Name FROM FilterAttributeTable"_L1);
1669 while (m_query->next())
1670 list.append(m_query->value(0).toString());
1681 "FilterAttributeTable.Name "
1683 "FilterAttributeTable, "
1686 "WHERE FilterAttributeTable.Id = FilterTable.FilterAttributeId "
1687 "AND FilterTable.NameId = FilterNameTable.Id "
1688 "AND FilterNameTable.Name=?"_L1);
1689 m_query->bindValue(0, filterName);
1691 while (m_query->next())
1692 list.append(m_query->value(0).toString());
1704 "FileAttributeSetTable.FilterAttributeSetId, "
1705 "FilterAttributeTable.Name "
1707 "FileAttributeSetTable, "
1708 "FilterAttributeTable, "
1710 "WHERE FileAttributeSetTable.FilterAttributeId = FilterAttributeTable.Id "
1711 "AND FileAttributeSetTable.NamespaceId = NamespaceTable.Id "
1712 "AND NamespaceTable.Name = ? "
1713 "ORDER BY FileAttributeSetTable.FilterAttributeSetId"_L1);
1714 m_query->bindValue(0, namespaceName);
1717 QList<QStringList> result;
1718 while (m_query->next()) {
1719 const int id = m_query->value(0).toInt();
1721 result.append(QStringList());
1724 result.last().append(m_query->value(1).toString());
1727 if (result.isEmpty())
1728 result.append(QStringList());
1739 "VersionTable.Version "
1743 "WHERE NamespaceTable.Name = ? "
1744 "AND NamespaceTable.Id = VersionTable.NamespaceId"_L1);
1745 m_query->bindValue(0, namespaceName);
1746 if (!m_query->exec() || !m_query->next())
1749 const QString ret = m_query->value(0).toString();
1756 const int errorValue = -1;
1760 m_query->prepare(
"SELECT COUNT(Id) FROM NamespaceTable WHERE Name=?"_L1);
1761 m_query->bindValue(0, nspace);
1763 while (m_query->next()) {
1764 if (m_query->value(0).toInt() > 0) {
1765 emit error(tr(
"Namespace %1 already exists.").arg(nspace));
1770 QFileInfo fi(m_collectionFile);
1771 m_query->prepare(
"INSERT INTO NamespaceTable VALUES(NULL, ?, ?)"_L1);
1772 m_query->bindValue(0, nspace);
1773 m_query->bindValue(1, fi.absoluteDir().relativeFilePath(fileName));
1774 int namespaceId = errorValue;
1775 if (m_query->exec()) {
1776 namespaceId = m_query->lastInsertId().toInt();
1779 if (namespaceId < 1) {
1780 emit error(tr(
"Cannot register namespace \"%1\".").arg(nspace));
1791 m_query->prepare(
"INSERT INTO FolderTable VALUES(NULL, ?, ?)"_L1);
1792 m_query->bindValue(0, namespaceId);
1793 m_query->bindValue(1, folderName);
1796 if (m_query->exec()) {
1797 virtualId = m_query->lastInsertId().toInt();
1800 if (virtualId < 1) {
1801 emit error(tr(
"Cannot register virtual folder '%1'.").arg(folderName));
1804 if (registerComponent(folderName, namespaceId) < 0)
1811 m_query->prepare(
"SELECT ComponentId FROM ComponentTable WHERE Name = ?"_L1);
1812 m_query->bindValue(0, componentName);
1813 if (!m_query->exec())
1816 if (!m_query->next()) {
1817 m_query->prepare(
"INSERT INTO ComponentTable VALUES(NULL, ?)"_L1);
1818 m_query->bindValue(0, componentName);
1819 if (!m_query->exec())
1822 m_query->prepare(
"SELECT ComponentId FROM ComponentTable WHERE Name = ?"_L1);
1823 m_query->bindValue(0, componentName);
1824 if (!m_query->exec() || !m_query->next())
1828 const int componentId = m_query->value(0).toInt();
1830 m_query->prepare(
"INSERT INTO ComponentMapping VALUES(?, ?)"_L1);
1831 m_query->bindValue(0, componentId);
1832 m_query->bindValue(1, namespaceId);
1833 if (!m_query->exec())
1844 m_query->prepare(
"INSERT INTO VersionTable (NamespaceId, Version) VALUES(?, ?)"_L1);
1845 m_query->addBindValue(namespaceId);
1846 m_query->addBindValue(version);
1847 return m_query->exec();
1851 const QString &nameSpace,
bool createDefaultVersionFilter)
1856 m_query->prepare(
"SELECT Id, FilePath FROM NamespaceTable WHERE Name=?"_L1);
1857 m_query->bindValue(0, nameSpace);
1859 if (!m_query->next())
1862 const int nsId = m_query->value(0).toInt();
1863 const QString fileName = m_query->value(1).toString();
1865 m_query->prepare(
"SELECT Id, Name FROM FolderTable WHERE NamespaceId=?"_L1);
1866 m_query->bindValue(0, nsId);
1868 if (!m_query->next())
1871 const int vfId = m_query->value(0).toInt();
1872 const QString vfName = m_query->value(1).toString();
1874 const QString absFileName = absoluteDocPath(fileName);
1875 QHelpDBReader reader(absFileName, QHelpGlobal::uniquifyConnectionName(
1876 fileName,
this),
this);
1880 registerComponent(vfName, nsId);
1881 registerVersion(reader.version(), nsId);
1882 if (!registerFileAttributeSets(reader.filterAttributeSets(), nsId))
1885 if (!registerIndexTable(reader.indexTable(), nsId, vfId, fileName))
1888 if (createDefaultVersionFilter)
1889 createVersionFilter(reader.version());
1895 if (version.isEmpty())
1898 const QVersionNumber versionNumber = QVersionNumber::fromString(version);
1899 if (versionNumber.isNull())
1902 const QString filterName = tr(
"Version %1").arg(version);
1903 if (filters().contains(filterName))
1906 QHelpFilterData filterData;
1907 filterData.setVersions({versionNumber});
1908 setFilterData(filterName, filterData);
1912 int nsId,
int vfId,
const QString &fileName)
1916 QMap<QString, QVariantList> filterAttributeToNewFileId;
1918 QVariantList fileFolderIds;
1919 QVariantList fileNames;
1920 QVariantList fileTitles;
1921 const int fileSize = indexTable.fileItems.size();
1922 fileFolderIds.reserve(fileSize);
1923 fileNames.reserve(fileSize);
1924 fileTitles.reserve(fileSize);
1926 if (!m_query->exec(
"SELECT MAX(FileId) FROM FileNameTable"_L1) || !m_query->next())
1929 const int maxFileId = m_query->value(0).toInt();
1932 for (
const QHelpDBReader::FileItem &item : indexTable.fileItems) {
1933 fileFolderIds.append(vfId);
1934 fileNames.append(item.name);
1935 fileTitles.append(item.title);
1937 for (
const QString &filterAttribute : item.filterAttributes)
1938 filterAttributeToNewFileId[filterAttribute].append(maxFileId + newFileId + 1);
1942 m_query->prepare(
"INSERT INTO FileNameTable VALUES(?, ?, NULL, ?)"_L1);
1943 m_query->addBindValue(fileFolderIds);
1944 m_query->addBindValue(fileNames);
1945 m_query->addBindValue(fileTitles);
1946 if (!m_query->execBatch())
1949 for (
auto it = filterAttributeToNewFileId.cbegin(),
1950 end = filterAttributeToNewFileId.cend(); it != end; ++it) {
1951 const QString filterAttribute = it.key();
1952 m_query->prepare(
"SELECT Id From FilterAttributeTable WHERE Name = ?"_L1);
1953 m_query->bindValue(0, filterAttribute);
1954 if (!m_query->exec() || !m_query->next())
1957 const int attributeId = m_query->value(0).toInt();
1959 QVariantList attributeIds;
1960 for (
int i = 0; i < it.value().size(); i++)
1961 attributeIds.append(attributeId);
1963 m_query->prepare(
"INSERT INTO FileFilterTable VALUES(?, ?)"_L1);
1964 m_query->addBindValue(attributeIds);
1965 m_query->addBindValue(it.value());
1966 if (!m_query->execBatch())
1970 QMap<QString, QVariantList> filterAttributeToNewIndexId;
1972 if (!m_query->exec(
"SELECT MAX(Id) FROM IndexTable"_L1) || !m_query->next())
1975 const int maxIndexId = m_query->value(0).toInt();
1978 QVariantList indexNames;
1979 QVariantList indexIdentifiers;
1980 QVariantList indexNamespaceIds;
1981 QVariantList indexFileIds;
1982 QVariantList indexAnchors;
1983 const int indexSize = indexTable.indexItems.size();
1984 indexNames.reserve(indexSize);
1985 indexIdentifiers.reserve(indexSize);
1986 indexNamespaceIds.reserve(indexSize);
1987 indexFileIds.reserve(indexSize);
1988 indexAnchors.reserve(indexSize);
1990 for (
const QHelpDBReader::IndexItem &item : indexTable.indexItems) {
1991 indexNames.append(item.name);
1992 indexIdentifiers.append(item.identifier);
1993 indexNamespaceIds.append(nsId);
1994 indexFileIds.append(maxFileId + item.fileId + 1);
1995 indexAnchors.append(item.anchor);
1997 for (
const QString &filterAttribute : item.filterAttributes)
1998 filterAttributeToNewIndexId[filterAttribute].append(maxIndexId + newIndexId + 1);
2002 m_query->prepare(
"INSERT INTO IndexTable VALUES(NULL, ?, ?, ?, ?, ?)"_L1);
2003 m_query->addBindValue(indexNames);
2004 m_query->addBindValue(indexIdentifiers);
2005 m_query->addBindValue(indexNamespaceIds);
2006 m_query->addBindValue(indexFileIds);
2007 m_query->addBindValue(indexAnchors);
2008 if (!m_query->execBatch())
2011 for (
auto it = filterAttributeToNewIndexId.cbegin(),
2012 end = filterAttributeToNewIndexId.cend(); it != end; ++it) {
2013 const QString filterAttribute = it.key();
2014 m_query->prepare(
"SELECT Id From FilterAttributeTable WHERE Name = ?"_L1);
2015 m_query->bindValue(0, filterAttribute);
2016 if (!m_query->exec() || !m_query->next())
2019 const int attributeId = m_query->value(0).toInt();
2021 QVariantList attributeIds;
2022 for (
int i = 0; i < it.value().size(); i++)
2023 attributeIds.append(attributeId);
2025 m_query->prepare(
"INSERT INTO IndexFilterTable VALUES(?, ?)"_L1);
2026 m_query->addBindValue(attributeIds);
2027 m_query->addBindValue(it.value());
2028 if (!m_query->execBatch())
2032 QMap<QString, QVariantList> filterAttributeToNewContentsId;
2034 QVariantList contentsNsIds;
2035 QVariantList contentsData;
2036 const int contentsSize = indexTable.contentsItems.size();
2037 contentsNsIds.reserve(contentsSize);
2038 contentsData.reserve(contentsSize);
2040 if (!m_query->exec(
"SELECT MAX(Id) FROM ContentsTable"_L1) || !m_query->next())
2043 const int maxContentsId = m_query->value(0).toInt();
2045 int newContentsId = 0;
2046 for (
const QHelpDBReader::ContentsItem &item : indexTable.contentsItems) {
2047 contentsNsIds.append(nsId);
2048 contentsData.append(item.data);
2050 for (
const QString &filterAttribute : item.filterAttributes) {
2051 filterAttributeToNewContentsId[filterAttribute]
2052 .append(maxContentsId + newContentsId + 1);
2057 m_query->prepare(
"INSERT INTO ContentsTable VALUES(NULL, ?, ?)"_L1);
2058 m_query->addBindValue(contentsNsIds);
2059 m_query->addBindValue(contentsData);
2060 if (!m_query->execBatch())
2063 for (
auto it = filterAttributeToNewContentsId.cbegin(),
2064 end = filterAttributeToNewContentsId.cend(); it != end; ++it) {
2065 const QString filterAttribute = it.key();
2066 m_query->prepare(
"SELECT Id From FilterAttributeTable WHERE Name = ?"_L1);
2067 m_query->bindValue(0, filterAttribute);
2068 if (!m_query->exec() || !m_query->next())
2071 const int attributeId = m_query->value(0).toInt();
2073 QVariantList attributeIds;
2074 for (
int i = 0; i < it.value().size(); i++)
2075 attributeIds.append(attributeId);
2077 m_query->prepare(
"INSERT INTO ContentsFilterTable VALUES(?, ?)"_L1);
2078 m_query->addBindValue(attributeIds);
2079 m_query->addBindValue(it.value());
2080 if (!m_query->execBatch())
2084 QVariantList filterNsIds;
2085 QVariantList filterAttributeIds;
2086 for (
const QString &filterAttribute : indexTable.usedFilterAttributes) {
2087 filterNsIds.append(nsId);
2089 m_query->prepare(
"SELECT Id From FilterAttributeTable WHERE Name = ?"_L1);
2090 m_query->bindValue(0, filterAttribute);
2091 if (!m_query->exec() || !m_query->next())
2094 filterAttributeIds.append(m_query->value(0).toInt());
2097 m_query->prepare(
"INSERT INTO OptimizedFilterTable "
2098 "(NamespaceId, FilterAttributeId) VALUES(?, ?)"_L1);
2099 m_query->addBindValue(filterNsIds);
2100 m_query->addBindValue(filterAttributeIds);
2101 if (!m_query->execBatch())
2104 m_query->prepare(
"INSERT INTO TimeStampTable "
2105 "(NamespaceId, FolderId, FilePath, Size, TimeStamp) "
2106 "VALUES(?, ?, ?, ?, ?)"_L1);
2107 m_query->addBindValue(nsId);
2108 m_query->addBindValue(vfId);
2109 m_query->addBindValue(fileName);
2110 const QFileInfo fi(absoluteDocPath(fileName));
2111 m_query->addBindValue(fi.size());
2112 QDateTime lastModified = fi.lastModified(QTimeZone::UTC);
2113 if (qEnvironmentVariableIsSet(
"SOURCE_DATE_EPOCH")) {
2114 const QString sourceDateEpochStr = qEnvironmentVariable(
"SOURCE_DATE_EPOCH");
2116 const qlonglong sourceDateEpoch = sourceDateEpochStr.toLongLong(&ok);
2117 if (ok && sourceDateEpoch < lastModified.toSecsSinceEpoch())
2118 lastModified.setSecsSinceEpoch(sourceDateEpoch);
2120 m_query->addBindValue(lastModified);
2121 if (!m_query->exec())
2130 m_query->prepare(
"DELETE FROM IndexFilterTable WHERE IndexId IN "
2131 "(SELECT Id FROM IndexTable WHERE NamespaceId = ?)"_L1);
2132 m_query->bindValue(0, nsId);
2133 if (!m_query->exec())
2136 m_query->prepare(
"DELETE FROM IndexTable WHERE NamespaceId = ?"_L1);
2137 m_query->bindValue(0, nsId);
2138 if (!m_query->exec())
2141 m_query->prepare(
"DELETE FROM FileFilterTable WHERE FileId IN "
2142 "(SELECT FileId FROM FileNameTable WHERE FolderId = ?)"_L1);
2143 m_query->bindValue(0, vfId);
2144 if (!m_query->exec())
2147 m_query->prepare(
"DELETE FROM FileNameTable WHERE FolderId = ?"_L1);
2148 m_query->bindValue(0, vfId);
2149 if (!m_query->exec())
2152 m_query->prepare(
"DELETE FROM ContentsFilterTable WHERE ContentsId IN "
2153 "(SELECT Id FROM ContentsTable WHERE NamespaceId = ?)"_L1);
2154 m_query->bindValue(0, nsId);
2155 if (!m_query->exec())
2158 m_query->prepare(
"DELETE FROM ContentsTable WHERE NamespaceId = ?"_L1);
2159 m_query->bindValue(0, nsId);
2160 if (!m_query->exec())
2163 m_query->prepare(
"DELETE FROM FileAttributeSetTable WHERE NamespaceId = ?"_L1);
2164 m_query->bindValue(0, nsId);
2165 if (!m_query->exec())
2168 m_query->prepare(
"DELETE FROM OptimizedFilterTable WHERE NamespaceId = ?"_L1);
2169 m_query->bindValue(0, nsId);
2170 if (!m_query->exec())
2173 m_query->prepare(
"DELETE FROM TimeStampTable WHERE NamespaceId = ?"_L1);
2174 m_query->bindValue(0, nsId);
2175 if (!m_query->exec())
2178 m_query->prepare(
"DELETE FROM VersionTable WHERE NamespaceId = ?"_L1);
2179 m_query->bindValue(0, nsId);
2180 if (!m_query->exec())
2183 m_query->prepare(
"SELECT ComponentId FROM ComponentMapping WHERE NamespaceId = ?"_L1);
2184 m_query->bindValue(0, nsId);
2185 if (!m_query->exec())
2188 if (!m_query->next())
2191 const int componentId = m_query->value(0).toInt();
2193 m_query->prepare(
"DELETE FROM ComponentMapping WHERE NamespaceId = ?"_L1);
2194 m_query->bindValue(0, nsId);
2195 if (!m_query->exec())
2198 m_query->prepare(
"SELECT ComponentId FROM ComponentMapping WHERE ComponentId = ?"_L1);
2199 m_query->bindValue(0, componentId);
2200 if (!m_query->exec())
2203 if (!m_query->next()) {
2204 m_query->prepare(
"DELETE FROM ComponentTable WHERE ComponentId = ?"_L1);
2205 m_query->bindValue(0, componentId);
2206 if (!m_query->exec())
2214 const QString &relFileName,
const QString &anchor)
2217 url.setScheme(
"qthelp"_L1);
2218 url.setAuthority(ns);
2219 url.setPath(u'/' + folder + u'/' + relFileName);
2220 url.setFragment(anchor);
2225 const QString &id,
const QStringList &filterAttributes)
const
2227 return documentsForField(
"Identifier"_L1, id, filterAttributes);
2231 const QString &keyword,
const QStringList &filterAttributes)
const
2233 return documentsForField(
"Name"_L1, keyword, filterAttributes);
2237 const QString &fieldValue,
const QStringList &filterAttributes)
const
2242 const QString filterlessQuery =
2244 "FileNameTable.Title, "
2245 "NamespaceTable.Name, "
2246 "FolderTable.Name, "
2247 "FileNameTable.Name, "
2248 "IndexTable.Anchor "
2254 "WHERE IndexTable.FileId = FileNameTable.FileId "
2255 "AND FileNameTable.FolderId = FolderTable.Id "
2256 "AND IndexTable.NamespaceId = NamespaceTable.Id "
2257 "AND IndexTable.%1 = ?"_L1.arg(fieldName);
2259 const QString filterQuery = filterlessQuery
2260 + prepareFilterQuery(filterAttributes.size(),
"IndexTable"_L1,
"Id"_L1,
2261 "IndexFilterTable"_L1,
"IndexId"_L1);
2263 m_query->prepare(filterQuery);
2264 m_query->bindValue(0, fieldValue);
2265 bindFilterQuery(m_query.get(), 1, filterAttributes);
2269 QList<QHelpLink> docList;
2270 while (m_query->next()) {
2271 QString title = m_query->value(0).toString();
2272 if (title.isEmpty())
2273 title = fieldValue +
" : "_L1 + m_query->value(3).toString();
2275 const QUrl url = buildQUrl(m_query->value(1).toString(),
2276 m_query->value(2).toString(),
2277 m_query->value(3).toString(),
2278 m_query->value(4).toString());
2279 docList.append(QHelpLink {url, title});
2285 const QString &id,
const QString &filterName)
const
2287 return documentsForField(
"Identifier"_L1, id, filterName);
2291 const QString &keyword,
const QString &filterName)
const
2293 return documentsForField(
"Name"_L1, keyword, filterName);
2297 const QString &fieldValue,
const QString &filterName)
const
2299 QMultiMap<QString, QUrl> linkMap;
2300 const auto documents = documentsForField(fieldName, fieldValue, filterName);
2301 for (
const auto &document : documents)
2302 linkMap.insert(document.title, document.url);
2307 const QString &fieldValue,
const QString &filterName)
const
2312 const QString filterlessQuery =
2314 "FileNameTable.Title, "
2315 "NamespaceTable.Name, "
2316 "FolderTable.Name, "
2317 "FileNameTable.Name, "
2318 "IndexTable.Anchor "
2324 "WHERE IndexTable.FileId = FileNameTable.FileId "
2325 "AND FileNameTable.FolderId = FolderTable.Id "
2326 "AND IndexTable.NamespaceId = NamespaceTable.Id "
2327 "AND IndexTable.%1 = ?"_L1.arg(fieldName);
2329 const QString filterQuery = filterlessQuery
2330 + prepareFilterQuery(filterName)
2331 +
" ORDER BY LOWER(FileNameTable.Title), FileNameTable.Title"_L1;
2333 m_query->prepare(filterQuery);
2334 m_query->bindValue(0, fieldValue);
2335 bindFilterQuery(m_query.get(), 1, filterName);
2339 QList<QHelpLink> docList;
2340 while (m_query->next()) {
2341 QString title = m_query->value(0).toString();
2342 if (title.isEmpty())
2343 title = fieldValue +
" : "_L1 + m_query->value(3).toString();
2345 const QUrl url = buildQUrl(m_query->value(1).toString(),
2346 m_query->value(2).toString(),
2347 m_query->value(3).toString(),
2348 m_query->value(4).toString());
2349 docList.append(QHelpLink {url, title});
2356 QStringList namespaceList;
2359 return namespaceList;
2361 const QString filterlessQuery =
2363 "NamespaceTable.Name "
2368 const QString filterQuery = filterlessQuery
2369 + prepareFilterQuery(filterName);
2371 m_query->prepare(filterQuery);
2372 bindFilterQuery(m_query.get(), 0, filterName);
2376 while (m_query->next())
2377 namespaceList.append(m_query->value(0).toString());
2378 return namespaceList;
QStringList filters() const
QUrl findFile(const QUrl &url, const QStringList &filterAttributes) const
QByteArray fileData(const QUrl &url) const
QStringList customFilters() const
FileInfoList registeredDocumentations() const
QList< ContentsData > contentsForFilter(const QStringList &filterAttributes) const
~QHelpCollectionHandler()
QMap< QString, QVersionNumber > namespaceToVersion() const
QList< FileInfo > FileInfoList
QList< QVersionNumber > availableVersions() const
QString namespaceForFile(const QUrl &url, const QStringList &filterAttributes) const
QStringList filterAttributes() const
QMap< QString, QString > namespaceToComponent() const
QStringList availableComponents() const
QStringList indicesForFilter(const QStringList &filterAttributes) const
bool fileExists(const QUrl &url) const
bool openCollectionFile()
Transaction(const QString &connectionName)
Combined button and popup list for selecting options.
static QString getTitle(const QByteArray &contents)
static QHelpCollectionHandler::FileInfo extractFileInfo(const QUrl &url)
static QString prepareFilterQuery(int attributesCount, const QString &idTableName, const QString &idColumnName, const QString &filterTableName, const QString &filterColumnName)
static void bindFilterQuery(QSqlQuery *query, int bindStart, const QString &filterName)
static void bindFilterQuery(QSqlQuery *query, int startingBindPos, const QStringList &filterAttributes)
static QString prepareFilterQuery(const QString &filterName)