Sophie

Sophie

distrib > Mageia > 5 > i586 > media > core-updates-src > by-pkgid > 5c0256c7d518acfb46629b5e68a44699 > files > 38

akonadi-1.13.0-4.1.mga5.src.rpm

From cbff2ea45452d9505c58dd2019cbeeb3a2e1dfea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= <daniel.vratil@kdab.com>
Date: Thu, 5 Jan 2017 16:04:50 +0100
Subject: [PATCH 38/40] Check for orphan Resources and Collections on startup

If a user removes an agent and then shuts down Akonadi, it's possible that
Akonadi won't have enough time to clean up all the Collections from the DB,
so on next start there will be orphan Collections in the DB. To prevent that
we quickly check on startup that we don't have any Collections owned by a
Resource that's not listed in agentsrc and we remove any such Resources and
Collections.
---
 server/src/akonadi.cpp           | 51 ++++++++++++++++++++++++++++++++++++++++
 server/src/akonadi.h             |  1 +
 server/src/storage/datastore.cpp | 12 ++++++++++
 server/src/storage/datastore.h   |  2 ++
 4 files changed, 66 insertions(+)

diff --git a/server/src/akonadi.cpp b/server/src/akonadi.cpp
index faef3a51d..0144f7d1b 100644
--- a/server/src/akonadi.cpp
+++ b/server/src/akonadi.cpp
@@ -36,6 +36,7 @@
 #include "debuginterface.h"
 #include "storage/itemretrievalthread.h"
 #include "storage/collectionstatistics.h"
+#include "storage/selectquerybuilder.h"
 #include "preprocessormanager.h"
 #include "search/searchmanager.h"
 #include "search/searchtaskmanagerthread.h"
@@ -228,6 +229,9 @@ bool AkonadiServer::init()
     // Cleanup referenced collections from the last run
     CollectionReferenceManager::cleanup();
 
+    // Make sure we don't have any collections trees that are not owned by any resource
+    cleanOrphanCollections();
+
     // We are ready, now register org.freedesktop.Akonadi service to DBus and
     // the fun can begin
     if ( !QDBusConnection::sessionBus().registerService( AkDBus::serviceName( AkDBus::Server ) ) ) {
@@ -403,3 +407,50 @@ IntervalCheck* AkonadiServer::intervalChecker()
     return mIntervalChecker;
 }
 
+void AkonadiServer::cleanOrphanCollections()
+{
+    // We can't talk to akonadi_control, because it might not be on DBus yet
+    // at this stage
+    QSettings settings( AkStandardDirs::agentConfigFile( XdgBaseDirs::ReadOnly ), QSettings::IniFormat );
+    settings.beginGroup( QLatin1String( "Instances" ) );
+    const QStringList instances = settings.childGroups();
+    settings.endGroup();
+
+    SelectQueryBuilder<Collection> qb;
+
+    qb.addValueCondition(Collection::parentIdColumn(), Query::Is, QVariant());
+    if ( !qb.exec() ) {
+        akError() << "Failed to query root collections!";
+        akError() << qb.query().lastError().driverText();
+        return;
+    }
+
+    const QVector<Collection> cols = qb.result();
+
+    QVector<Collection> toDelete;
+    Q_FOREACH ( /* sic! */ Collection col, cols ) {
+        const Resource &res = col.resource();
+        // Collection refers to an non-existent Resource. Should really not
+        // happen since that violates the DB constraints, but there might be
+        // inconsistencies from the past.
+        if ( !res.isValid() ) {
+            akDebug() << "Found Collection" << col.id() << "with an invalid parent resource, cleaning up...";
+            DataStore::self()->cleanupCollectionsRecursively( col );
+            continue;
+        }
+
+        // Virtual resources are not tracked by AgentManager.
+        if ( res.isVirtual() ) {
+            continue;
+        }
+
+        // We have a collection tree owned by resource that is no longer registered
+        // in agentsrc, so remove it.
+        if ( !instances.contains( res.name() ) ) {
+            akDebug() << "Found Collection" << col.id() << "with unregistered parent resource" << res.name() << ", cleaning up...";
+            ResourceManager::self()->removeResourceInstance( res.name() );
+        }
+    }
+
+    akDebug() << "Orphan collection cleanup done";
+}
diff --git a/server/src/akonadi.h b/server/src/akonadi.h
index 17b4db29b..dd0fc0084 100644
--- a/server/src/akonadi.h
+++ b/server/src/akonadi.h
@@ -77,6 +77,7 @@ class AkonadiServer : public QLocalServer
     void startDatabaseProcess();
     void createDatabase();
     void stopDatabaseProcess();
+    void cleanOrphanCollections();
 
   protected:
     AkonadiServer( QObject *parent = 0 );
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
index 035395ee4..bce9912f3 100644
--- a/server/src/storage/datastore.cpp
+++ b/server/src/storage/datastore.cpp
@@ -649,6 +649,17 @@ bool DataStore::appendCollection( Collection &collection )
   return true;
 }
 
+bool DataStore::cleanupCollectionsRecursively( Collection &collection )
+{
+  Q_FOREACH ( Collection col, collection.children() ) {
+    if ( !cleanupCollectionsRecursively( col ) ) {
+      return false;
+    }
+  }
+
+  return cleanupCollection( collection );
+}
+
 bool DataStore::cleanupCollection( Collection &collection )
 {
   if ( !s_hasForeignKeyConstraints ) {
@@ -688,6 +699,7 @@ bool DataStore::cleanupCollection( Collection &collection )
   }
 
   // delete the collection itself, referential actions will do the rest
+
   mNotificationCollector->collectionRemoved( collection );
   return collection.remove();
 }
diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h
index a2d8a42e9..29aa2debf 100644
--- a/server/src/storage/datastore.h
+++ b/server/src/storage/datastore.h
@@ -146,6 +146,8 @@ class DataStore : public QObject
     /// same as the above but for database backends without working referential actions on foreign keys
     virtual bool cleanupCollection_slow( Collection &collection );
 
+    virtual bool cleanupCollectionsRecursively( Collection &collection );
+
     /// moves the collection @p collection to @p newParent.
     virtual bool moveCollection( Collection &collection, const Collection &newParent );
 
-- 
2.14.1