7#include "collectiontreecache.h"
8#include "akonadiserver_debug.h"
9#include "commandcontext.h"
10#include "selectquerybuilder.h"
12#include "private/scope_p.h"
21using namespace Akonadi::Server;
38CollectionTreeCache::Node::Node()
45CollectionTreeCache::Node::Node(
const Collection &col)
53CollectionTreeCache::Node::~Node()
58void CollectionTreeCache::Node::appendChild(
Node *child)
64void CollectionTreeCache::Node::removeChild(
Node *child)
66 child->parent =
nullptr;
70CollectionTreeCache::CollectionTreeCache()
71 : AkThread(QStringLiteral(
"CollectionTreeCache"))
75CollectionTreeCache::~CollectionTreeCache()
80void CollectionTreeCache::init()
88 mRoot->parent =
nullptr;
89 mNodeLookup.
insert(0, mRoot);
92 qb.
addSortColumn(Collection::idFullColumnName(), Query::Ascending);
95 qCCritical(AKONADISERVER_LOG) <<
"Failed to initialize Collection tree cache!";
100 std::multimap<qint64 ,
Node *> pendingNodes;
101 const auto collections = qb.
result();
102 for (
const auto &col : collections) {
103 auto parent = mNodeLookup.
value(col.parentId(),
nullptr);
105 auto node =
new Node(col);
108 parent->appendChild(node);
109 mNodeLookup.
insert(node->id, node);
111 pendingNodes.insert({col.parentId(), node});
115 if (!pendingNodes.empty()) {
126 auto it = pendingNodes.rbegin();
127 while (!pendingNodes.empty()) {
128 auto parent = mNodeLookup.
value(it->first,
nullptr);
134 auto node = it->second;
135 parent->appendChild(node);
136 mNodeLookup.
insert(node->id, node);
137 pendingNodes.erase((++it).base());
141 if (it == pendingNodes.rend()) {
142 if (Q_UNLIKELY(inserts == 0)) {
149 qCWarning(AKONADISERVER_LOG) <<
"Found unreferenced Collections!";
150 auto unref = pendingNodes.begin();
151 while (unref != pendingNodes.end()) {
152 qCWarning(AKONADISERVER_LOG) <<
"\tCollection" << unref->second->id <<
"references an invalid parent" << it->first;
154 delete unref->second;
155 unref = pendingNodes.erase(unref);
157 qCWarning(AKONADISERVER_LOG) <<
"Please run \"akonadictl fsck\" to correct the inconsistencies!";
162 it = pendingNodes.rbegin();
168 Q_ASSERT(pendingNodes.empty());
169 Q_ASSERT(mNodeLookup.
size() == collections.count() + 1 );
173void CollectionTreeCache::quit()
180void CollectionTreeCache::collectionAdded(
const Collection &col)
184 auto parent = mNodeLookup.
value(col.parentId(),
nullptr);
186 qCWarning(AKONADISERVER_LOG) <<
"Received a new collection (" << col.id() <<
") with unknown parent (" << col.parentId() <<
")";
190 auto node =
new Node(col);
191 parent->appendChild(node);
192 mNodeLookup.
insert(node->id, node);
195void CollectionTreeCache::collectionChanged(
const Collection &col)
199 auto node = mNodeLookup.
value(col.id(),
nullptr);
201 qCWarning(AKONADISERVER_LOG) <<
"Received an unknown changed collection (" << col.id() <<
")";
206 if (node->collection.isValid()) {
207 node->collection = col;
211void CollectionTreeCache::collectionMoved(
const Collection &col)
215 auto node = mNodeLookup.
value(col.id(),
nullptr);
217 qCWarning(AKONADISERVER_LOG) <<
"Received an unknown moved collection (" << col.id() <<
")";
220 auto oldParent = node->parent;
222 auto newParent = mNodeLookup.
value(col.parentId(),
nullptr);
224 qCWarning(AKONADISERVER_LOG) <<
"Received a moved collection (" << col.id() <<
") with an unknown move destination (" << col.parentId() <<
")";
228 oldParent->removeChild(node);
229 newParent->appendChild(node);
230 if (node->collection.isValid()) {
231 node->collection = col;
235void CollectionTreeCache::collectionRemoved(
const Collection &col)
239 auto node = mNodeLookup.
value(col.id(),
nullptr);
241 qCWarning(AKONADISERVER_LOG) <<
"Received unknown removed collection (" << col.id() <<
")";
246 parent->removeChild(node);
247 mNodeLookup.
remove(node->id);
251CollectionTreeCache::Node *CollectionTreeCache::findNode(
const QString &rid,
const QString &resource)
const
256 auto root = std::find_if(mRoot->children.
cbegin(), mRoot->children.
cend(), [resource](
Node *node) {
260 return node->collection.resource().name() == resource;
262 if (root == mRoot->children.
cend()) {
266 return findNode((*root), [rid](
Node *node) {
267 return node->collection.remoteId() == rid;
271QList<Collection> CollectionTreeCache::retrieveCollections(CollectionTreeCache::Node *root,
int depth,
int ancestorDepth)
const
278 for (
int i = 0; i < ancestorDepth &&
parent !=
nullptr; ++i) {
288 stack.
push({root, 0});
290 auto c = stack.
pop();
291 if (c.depth > depth) {
295 if (c.node->id > 0) {
299 for (
auto child : std::as_const(c.node->children)) {
300 stack.
push({child, c.depth + 1});
306 for (
auto node : nodes) {
307 if (node->collection.isValid()) {
318 for (
auto node : std::as_const(missing)) {
319 cond.addValueCondition(Collection::idFullColumnName(), Query::Equals, node->id);
323 qCWarning(AKONADISERVER_LOG) <<
"Failed to retrieve collections from the database";
327 const auto results = qb.
result();
328 if (results.size() != missing.
size()) {
329 qCWarning(AKONADISERVER_LOG) <<
"Could not obtain all missing collections! Node tree refers to a non-existent collection";
338 for (
auto node : std::as_const(missing)) {
339 auto it = std::find_if(results.cbegin(), results.cend(), [node](
const Collection &col) {
340 return node->id == col.id();
342 if (Q_UNLIKELY(it == results.cend())) {
346 node->collection = *it;
354CollectionTreeCache::retrieveCollections(
const Scope &scope,
int depth,
int ancestorDepth,
const QString &resource, CommandContext *context)
const
356 if (scope.isEmpty()) {
357 return retrieveCollections(mRoot, depth, ancestorDepth);
358 }
else if (scope.scope() == Scope::Rid) {
360 Q_ASSERT(!resource.
isEmpty() || (context && context->resource().isValid()));
362 Node *node =
nullptr;
364 node = findNode(scope.rid(), resource);
365 }
else if (context && context->resource().isValid()) {
366 node = findNode(scope.rid(), context->resource().
name());
371 if (Q_LIKELY(node)) {
372 return retrieveCollections(node, depth, ancestorDepth);
374 }
else if (scope.scope() == Scope::Uid) {
375 Node *node = mNodeLookup.
value(scope.uid());
376 if (Q_LIKELY(node)) {
377 return retrieveCollections(node, depth, ancestorDepth);
384#include "moc_collectiontreecache.cpp"
Represents a collection of PIM items.
void addSortColumn(const QString &column, Query::SortOrder order=Query::Ascending)
Add sort column.
bool exec()
Executes the query, returns true on success.
void addCondition(const Query::Condition &condition, ConditionType type=WhereCondition)
Add a WHERE condition.
Represents a WHERE condition tree.
Helper class for creating and executing database SELECT queries.
QList< T > result()
Returns the result of this SELECT query.
iterator insert(const Key &key, const T &value)
bool remove(const Key &key)
qsizetype size() const const
T value(const Key &key) const const
const_iterator cbegin() const const
const_iterator cend() const const
bool isEmpty() const const
void push_back(parameter_type value)
qsizetype size() const const
const QObjectList & children() const const
QObject * parent() const const
bool isEmpty() const const