7 #include "collectionfetchhandler.h"
8 #include "akonadiserver_debug.h"
10 #include "connection.h"
11 #include "handlerhelper.h"
12 #include "storage/collectionqueryhelper.h"
13 #include "storage/datastore.h"
14 #include "storage/selectquerybuilder.h"
16 #include <private/scope_p.h>
19 using namespace Akonadi::Server;
23 for (
const T &e2 : l2) {
31 CollectionFetchHandler::CollectionFetchHandler(AkonadiServer &akonadi)
38 if (mAncestorDepth <= 0) {
43 for (
int i = 0; i < mAncestorDepth; ++i) {
44 if (parent.parentId() == 0) {
47 if (mAncestors.contains(parent.parentId())) {
48 parent = mAncestors.value(parent.parentId());
50 parent = mCollections.value(parent.parentId());
52 if (!parent.isValid()) {
53 qCWarning(AKONADISERVER_LOG) <<
"Found an invalid parent in ancestors of Collection" << col.name() <<
"(ID:" << col.id() <<
")";
54 throw HandlerException(
"Found invalid parent in ancestors");
63 CollectionAttribute::List attributes;
64 auto it = mCollectionAttributes.find(col.id());
65 while (it != mCollectionAttributes.end() && it.key() == col.id()) {
66 if (
filter.isEmpty() ||
filter.contains(it.value().type())) {
67 attributes << it.
value();
73 CollectionAttribute attr;
74 attr.setType(AKONADI_PARAM_ENABLED);
75 attr.setValue(col.enabled() ?
"TRUE" :
"FALSE");
82 void CollectionFetchHandler::listCollection(
const Collection &root,
85 const CollectionAttribute::List &attributes)
89 if (!mAncestorAttributes.isEmpty()) {
92 ancestorAttributes.
push(getAttributes(col, mAncestorAttributes));
98 storageBackend()->activeCachePolicy(dummy);
101 HandlerHelper::fetchCollectionsResponse(akonadi(), dummy, attributes, mIncludeStatistics, mAncestorDepth, ancestors, ancestorAttributes, mimeTypes));
107 orCondition.addValueCondition(column, Query::Equals,
static_cast<int>(Collection::True));
109 andCondition.addValueCondition(column, Query::Equals,
static_cast<int>(Collection::Undefined));
110 andCondition.addValueCondition(Collection::enabledFullColumnName(), Query::Equals,
true);
111 orCondition.addCondition(andCondition);
115 bool CollectionFetchHandler::checkFilterCondition(
const Collection &col)
const
118 if (mEnabledCollections && !col.enabled()) {
122 if (mCollectionsToDisplay && (((col.displayPref() == Collection::Undefined) && !col.enabled()) || (col.displayPref() == Collection::False))) {
125 if (mCollectionsToIndex && (((col.indexPref() == Collection::Undefined) && !col.enabled()) || (col.indexPref() == Collection::False))) {
129 if (mCollectionsToSynchronize && (((col.syncPref() == Collection::Undefined) && !col.enabled()) || (col.syncPref() == Collection::False))) {
139 qb.addValueCondition(CollectionAttribute::collectionIdFullColumnName(), Query::In, ids);
141 qb.addColumn(CollectionAttribute::collectionIdFullColumnName());
142 qb.addColumn(CollectionAttribute::typeFullColumnName());
143 qb.addColumn(CollectionAttribute::valueFullColumnName());
145 if (!requestedAttributes.
isEmpty()) {
146 QVariantList attributes;
147 attributes.reserve(requestedAttributes.
size());
148 for (
const QByteArray &type : requestedAttributes) {
151 qb.addValueCondition(CollectionAttribute::typeFullColumnName(), Query::In, attributes);
154 qb.addSortColumn(CollectionAttribute::collectionIdFullColumnName(), Query::Ascending);
157 throw HandlerException(
"Unable to retrieve attributes for listing");
162 void CollectionFetchHandler::retrieveAttributes(
const QVariantList &collectionIds)
166 const int size = 999;
167 while (
start < collectionIds.size()) {
168 const QVariantList ids = collectionIds.mid(
start, size);
169 QSqlQuery attributeQuery = getAttributeQuery(ids, mAncestorAttributes);
170 while (attributeQuery.
next()) {
171 CollectionAttribute attr;
175 mCollectionAttributes.insert(attributeQuery.
value(0).
toLongLong(), attr);
182 static QSqlQuery getMimeTypeQuery(
const QVariantList &ids)
184 QueryBuilder qb(CollectionMimeTypeRelation::tableName());
186 qb.addJoin(QueryBuilder::LeftJoin, MimeType::tableName(), MimeType::idFullColumnName(), CollectionMimeTypeRelation::rightFullColumnName());
187 qb.addValueCondition(CollectionMimeTypeRelation::leftFullColumnName(), Query::In, ids);
189 qb.addColumn(CollectionMimeTypeRelation::leftFullColumnName());
190 qb.addColumn(CollectionMimeTypeRelation::rightFullColumnName());
191 qb.addColumn(MimeType::nameFullColumnName());
192 qb.addSortColumn(CollectionMimeTypeRelation::leftFullColumnName(), Query::Ascending);
195 throw HandlerException(
"Unable to retrieve mimetypes for listing");
200 void CollectionFetchHandler::retrieveCollections(
const Collection &topParent,
int depth)
215 const qint64 parentId = topParent.isValid() ? topParent.id() : 0;
221 }
else if (depth == 1) {
222 if (topParent.isValid()) {
223 qb.
addValueCondition(Collection::parentIdFullColumnName(), Query::Equals, parentId);
228 if (topParent.isValid()) {
229 qb.
addValueCondition(Collection::resourceIdFullColumnName(), Query::Equals, topParent.resourceId());
237 if (mCollectionsToSynchronize) {
238 qb.
addCondition(filterCondition(Collection::syncPrefFullColumnName()));
239 }
else if (mCollectionsToDisplay) {
240 qb.
addCondition(filterCondition(Collection::displayPrefFullColumnName()));
241 }
else if (mCollectionsToIndex) {
242 qb.
addCondition(filterCondition(Collection::indexPrefFullColumnName()));
243 }
else if (mEnabledCollections) {
246 if (mResource.isValid()) {
247 qb.
addValueCondition(Collection::resourceIdFullColumnName(), Query::Equals, mResource.id());
250 if (!mMimeTypes.isEmpty()) {
251 qb.
addJoin(QueryBuilder::LeftJoin,
252 CollectionMimeTypeRelation::tableName(),
253 CollectionMimeTypeRelation::leftColumn(),
254 Collection::idFullColumnName());
255 QVariantList mimeTypeFilter;
256 mimeTypeFilter.reserve(mMimeTypes.size());
257 for (MimeType::Id mtId : std::as_const(mMimeTypes)) {
258 mimeTypeFilter << mtId;
260 qb.
addValueCondition(CollectionMimeTypeRelation::rightColumn(), Query::In, mimeTypeFilter);
266 throw HandlerException(
"Unable to retrieve collection for listing");
268 const auto result{qb.
result()};
270 mCollections.
insert(col.id(), col);
276 auto it = mCollections.begin();
277 while (it != mCollections.end()) {
278 if (topParent.isValid()) {
280 bool foundParent =
false;
284 if (
id == parentId) {
289 if (!col.isValid()) {
290 col = Collection::retrieveById(
id);
295 it = mCollections.erase(it);
303 QVariantList mimeTypeIds;
304 QVariantList attributeIds;
305 QVariantList ancestorIds;
306 const auto collectionSize{mCollections.size()};
307 mimeTypeIds.reserve(collectionSize);
308 attributeIds.reserve(collectionSize);
310 ancestorIds.reserve(collectionSize);
311 for (
auto it = mCollections.cbegin(), end = mCollections.cend(); it != end; ++it) {
312 mimeTypeIds << it.key();
313 attributeIds << it.key();
314 ancestorIds << it.key();
317 if (mAncestorDepth > 0 && topParent.isValid()) {
319 mAncestors.insert(topParent.id(), topParent);
320 ancestorIds << topParent.id();
323 for (
int i = 0; i < mAncestorDepth; ++i) {
324 if (parent.parentId() == 0) {
327 parent = parent.parent();
328 mAncestors.insert(parent.id(), parent);
330 ancestorIds << parent.id();
336 for (
const Collection &col : std::as_const(mCollections)) {
337 if (col.parentId() != parentId && !mCollections.contains(col.parentId())) {
338 missingCollections.
insert(col.parentId());
353 while (!missingCollections.
isEmpty()) {
356 ids.reserve(missingCollections.
size());
357 for (qint64
id : std::as_const(missingCollections)) {
362 throw HandlerException(
"Unable to retrieve collections for listing");
365 missingCollections.
clear();
366 const auto missingCols = qb.
result();
367 for (
const Collection &missingCol : missingCols) {
368 mCollections.
insert(missingCol.id(), missingCol);
369 ancestorIds << missingCol.id();
370 attributeIds << missingCol.id();
371 mimeTypeIds << missingCol.id();
373 if (missingCol.parentId() != parentId && !mCollections.contains(missingCol.parentId())) {
374 missingCollections.
insert(missingCol.parentId());
381 if (!mAncestorAttributes.isEmpty()) {
382 retrieveAttributes(ancestorIds);
386 const int querySizeLimit = 999;
387 int mimetypeQueryStart = 0;
388 int attributeQueryStart = 0;
389 QSqlQuery mimeTypeQuery(storageBackend()->database());
390 QSqlQuery attributeQuery(storageBackend()->database());
391 auto it = mCollections.begin();
392 while (it != mCollections.end()) {
398 if (!mimeTypeQuery.isValid() && mimetypeQueryStart < mimeTypeIds.size()) {
399 const QVariantList ids = mimeTypeIds.
mid(mimetypeQueryStart, querySizeLimit);
400 mimetypeQueryStart += querySizeLimit;
401 mimeTypeQuery = getMimeTypeQuery(ids);
402 mimeTypeQuery.next();
405 while (mimeTypeQuery.isValid() && mimeTypeQuery.value(0).toLongLong() < col.id()) {
406 if (!mimeTypeQuery.next()) {
411 while (mimeTypeQuery.isValid() && mimeTypeQuery.value(0).toLongLong() == col.id()) {
413 if (!mimeTypeQuery.next()) {
419 CollectionAttribute::List attributes;
422 if (!attributeQuery.
isValid() && attributeQueryStart < attributeIds.size()) {
423 const QVariantList ids = attributeIds.mid(attributeQueryStart, querySizeLimit);
424 attributeQueryStart += querySizeLimit;
426 attributeQuery.
next();
430 if (!attributeQuery.
next()) {
436 CollectionAttribute attr;
441 if (!attributeQuery.
next()) {
447 listCollection(col, ancestorsForCollection(col), mimeTypes, attributes);
451 mimeTypeQuery.finish();
454 bool CollectionFetchHandler::parseStream()
456 const auto &cmd = Protocol::cmdCast<Protocol::FetchCollectionsCommand>(m_command);
458 if (!cmd.resource().isEmpty()) {
459 mResource = Resource::retrieveByName(cmd.resource());
460 if (!mResource.isValid()) {
461 return failureResponse(
"Unknown resource");
465 for (
const QString &mtName : lstMimeTypes) {
466 const MimeType mt = MimeType::retrieveByNameOrCreate(mtName);
468 return failureResponse(
"Failed to create mimetype record");
470 mMimeTypes.append(mt.id());
473 mEnabledCollections = cmd.enabled();
474 mCollectionsToSynchronize = cmd.syncPref();
475 mCollectionsToDisplay = cmd.displayPref();
476 mCollectionsToIndex = cmd.indexPref();
477 mIncludeStatistics = cmd.fetchStats();
480 switch (cmd.depth()) {
481 case Protocol::FetchCollectionsCommand::BaseCollection:
484 case Protocol::FetchCollectionsCommand::ParentCollection:
487 case Protocol::FetchCollectionsCommand::AllCollections:
492 switch (cmd.ancestorsDepth()) {
493 case Protocol::Ancestor::NoAncestor:
496 case Protocol::Ancestor::ParentAncestor:
499 case Protocol::Ancestor::AllAncestors:
500 mAncestorDepth = INT_MAX;
503 mAncestorAttributes = cmd.ancestorsAttributes();
505 Scope scope = cmd.collections();
506 if (!scope.isEmpty()) {
508 if (scope.scope() == Scope::Uid) {
509 col = Collection::retrieveById(scope.uid());
510 }
else if (scope.scope() == Scope::Rid) {
512 qb.
addValueCondition(Collection::remoteIdFullColumnName(), Query::Equals, scope.rid());
513 qb.
addJoin(QueryBuilder::InnerJoin, Resource::tableName(), Collection::resourceIdFullColumnName(), Resource::idFullColumnName());
514 if (mCollectionsToSynchronize) {
515 qb.
addCondition(filterCondition(Collection::syncPrefFullColumnName()));
516 }
else if (mCollectionsToDisplay) {
517 qb.
addCondition(filterCondition(Collection::displayPrefFullColumnName()));
518 }
else if (mCollectionsToIndex) {
519 qb.
addCondition(filterCondition(Collection::indexPrefFullColumnName()));
521 if (mResource.isValid()) {
522 qb.
addValueCondition(Resource::idFullColumnName(), Query::Equals, mResource.id());
523 }
else if (connection()->context().resource().isValid()) {
524 qb.
addValueCondition(Resource::idFullColumnName(), Query::Equals, connection()->context().resource().
id());
526 return failureResponse(
"Cannot retrieve collection based on remote identifier without a resource context");
529 return failureResponse(
"Unable to retrieve collection for listing");
532 if (results.
count() != 1) {
533 return failureResponse(
QString::number(results.
count()) + QStringLiteral(
" collections found"));
535 col = results.
first();
536 }
else if (scope.scope() == Scope::HierarchicalRid) {
537 if (!connection()->context().resource().isValid()) {
538 return failureResponse(
"Cannot retrieve collection based on hierarchical remote identifier without a resource context");
540 col = CollectionQueryHelper::resolveHierarchicalRID(scope.hridChain(), connection()->context().resource().
id());
542 return failureResponse(
"Unexpected error");
545 if (!col.isValid()) {
546 return failureResponse(
"Collection does not exist");
549 retrieveCollections(col, depth);
556 return successResponse<Protocol::FetchCollectionsResponse>();