Rex Dieter adf30af
From c24329bb570ee16c033228588e6d22b0f6000f95 Mon Sep 17 00:00:00 2001
Rex Dieter adf30af
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
Rex Dieter adf30af
Date: Fri, 5 Dec 2014 18:23:33 +0100
Rex Dieter adf30af
Subject: [PATCH 22/30] Implement cache for CollectionStatistics to
Rex Dieter adf30af
 significantly reduce amount of SQL queries
Rex Dieter adf30af
Rex Dieter adf30af
Collection statistics are being requested extremely often (basically whenever
Rex Dieter adf30af
a PimItem is changed, or when a Collection itself is changed), and it's always
Rex Dieter adf30af
requested by at least 5 or so clients (including agents that listen to
Rex Dieter adf30af
everything).
Rex Dieter adf30af
Rex Dieter adf30af
To decrease the load on database we now cache the Collection statistics and
Rex Dieter adf30af
we only invalidate a cache entry when respective collection (or it's content)
Rex Dieter adf30af
is changed. The invalidation is invoked from NotificationCollector, which is
Rex Dieter adf30af
basically a hack, but performance-wise it's the best place to avoid additional
Rex Dieter adf30af
expensive queries.
Rex Dieter adf30af
Rex Dieter adf30af
This patch also optimizes the SQL query needed to get up-to-date statistics.
Rex Dieter adf30af
We now have only one query to get both full count and read items count, which
Rex Dieter adf30af
a bit is faster as the database only has to deal with one large JOIN.
Rex Dieter adf30af
Rex Dieter adf30af
Thanks to the cache the number of SQL queries for Collection statistics have
Rex Dieter adf30af
reduced by 70%-80%, and average query duration is now between 20 and 80ms
Rex Dieter adf30af
depending on average collection size and database used.
Rex Dieter adf30af
---
Rex Dieter adf30af
 server/CMakeLists.txt                        |   1 +
Rex Dieter adf30af
 server/src/handler/link.cpp                  |   2 +-
Rex Dieter adf30af
 server/src/handler/merge.cpp                 |   4 +-
Rex Dieter adf30af
 server/src/handler/select.cpp                |  14 ++--
Rex Dieter adf30af
 server/src/handler/status.cpp                |  20 ++---
Rex Dieter adf30af
 server/src/handlerhelper.cpp                 |  81 ++------------------
Rex Dieter adf30af
 server/src/handlerhelper.h                   |  22 ------
Rex Dieter adf30af
 server/src/storage/collectionstatistics.cpp  | 108 +++++++++++++++++++++++++++
Rex Dieter adf30af
 server/src/storage/collectionstatistics.h    |  70 +++++++++++++++++
Rex Dieter adf30af
 server/src/storage/datastore.cpp             |   8 +-
Rex Dieter adf30af
 server/src/storage/datastore.h               |   6 +-
Rex Dieter adf30af
 server/src/storage/notificationcollector.cpp |   8 ++
Rex Dieter adf30af
 server/tests/unittest/fakedatastore.cpp      |   8 +-
Rex Dieter adf30af
 server/tests/unittest/fakedatastore.h        |   2 +
Rex Dieter adf30af
 14 files changed, 224 insertions(+), 130 deletions(-)
Rex Dieter adf30af
 create mode 100644 server/src/storage/collectionstatistics.cpp
Rex Dieter adf30af
 create mode 100644 server/src/storage/collectionstatistics.h
Rex Dieter adf30af
Rex Dieter adf30af
diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt
Rex Dieter adf30af
index 275938d..f0e0093 100644
Rex Dieter adf30af
--- a/server/CMakeLists.txt
Rex Dieter adf30af
+++ b/server/CMakeLists.txt
Rex Dieter adf30af
@@ -161,6 +161,7 @@ set(libakonadiprivate_SRCS
Rex Dieter adf30af
   src/search/searchmanager.cpp
Rex Dieter adf30af
 
Rex Dieter adf30af
   src/storage/collectionqueryhelper.cpp
Rex Dieter adf30af
+  src/storage/collectionstatistics.cpp
Rex Dieter adf30af
   src/storage/entity.cpp
Rex Dieter adf30af
   ${CMAKE_CURRENT_BINARY_DIR}/entities.cpp
Rex Dieter adf30af
   ${CMAKE_CURRENT_BINARY_DIR}/akonadischema.cpp
Rex Dieter adf30af
diff --git a/server/src/handler/link.cpp b/server/src/handler/link.cpp
Rex Dieter adf30af
index ce18e47..227de11 100644
Rex Dieter adf30af
--- a/server/src/handler/link.cpp
Rex Dieter adf30af
+++ b/server/src/handler/link.cpp
Rex Dieter adf30af
@@ -25,10 +25,10 @@
Rex Dieter adf30af
 #include "storage/itemqueryhelper.h"
Rex Dieter adf30af
 #include "storage/transaction.h"
Rex Dieter adf30af
 #include "storage/selectquerybuilder.h"
Rex Dieter adf30af
+#include "storage/collectionqueryhelper.h"
Rex Dieter adf30af
 #include "entities.h"
Rex Dieter adf30af
 
Rex Dieter adf30af
 #include "imapstreamparser.h"
Rex Dieter adf30af
-#include <storage/collectionqueryhelper.h>
Rex Dieter adf30af
 
Rex Dieter adf30af
 using namespace Akonadi::Server;
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/src/handler/merge.cpp b/server/src/handler/merge.cpp
Rex Dieter adf30af
index c26917d..5149916 100644
Rex Dieter adf30af
--- a/server/src/handler/merge.cpp
Rex Dieter adf30af
+++ b/server/src/handler/merge.cpp
Rex Dieter adf30af
@@ -88,7 +88,7 @@ bool Merge::mergeItem( PimItem &newItem, PimItem &currentItem,
Rex Dieter adf30af
       if ( !itemFlags.removed.isEmpty() ) {
Rex Dieter adf30af
         const Flag::List removedFlags = HandlerHelper::resolveFlags( itemFlags.removed );
Rex Dieter adf30af
         DataStore::self()->removeItemsFlags( PimItem::List() << currentItem, removedFlags,
Rex Dieter adf30af
-                                             &flagsRemoved, true );
Rex Dieter adf30af
+                                             &flagsRemoved, col, true );
Rex Dieter adf30af
       }
Rex Dieter adf30af
 
Rex Dieter adf30af
       if ( flagsAdded || flagsRemoved ) {
Rex Dieter adf30af
@@ -98,7 +98,7 @@ bool Merge::mergeItem( PimItem &newItem, PimItem &currentItem,
Rex Dieter adf30af
       bool flagsChanged = false;
Rex Dieter adf30af
       const Flag::List flags = HandlerHelper::resolveFlags( itemFlags.added );
Rex Dieter adf30af
       DataStore::self()->setItemsFlags( PimItem::List() << currentItem, flags,
Rex Dieter adf30af
-                                        &flagsChanged, true );
Rex Dieter adf30af
+                                        &flagsChanged, col, true );
Rex Dieter adf30af
       if ( flagsChanged ) {
Rex Dieter adf30af
         mChangedParts << AKONADI_PARAM_FLAGS;
Rex Dieter adf30af
       }
Rex Dieter adf30af
diff --git a/server/src/handler/select.cpp b/server/src/handler/select.cpp
Rex Dieter adf30af
index 1c5dd8a..f1ecc44 100644
Rex Dieter adf30af
--- a/server/src/handler/select.cpp
Rex Dieter adf30af
+++ b/server/src/handler/select.cpp
Rex Dieter adf30af
@@ -27,6 +27,7 @@
Rex Dieter adf30af
 #include "handlerhelper.h"
Rex Dieter adf30af
 #include "imapstreamparser.h"
Rex Dieter adf30af
 #include "storage/selectquerybuilder.h"
Rex Dieter adf30af
+#include "storage/collectionstatistics.h"
Rex Dieter adf30af
 #include "commandcontext.h"
Rex Dieter adf30af
 
Rex Dieter adf30af
 #include "response.h"
Rex Dieter adf30af
@@ -96,19 +97,14 @@ bool Select::parseStream()
Rex Dieter adf30af
     response.setString( "FLAGS (" + Flag::joinByName( Flag::retrieveAll(), QLatin1String( " " ) ).toLatin1() + ")" );
Rex Dieter adf30af
     Q_EMIT responseAvailable( response );
Rex Dieter adf30af
 
Rex Dieter adf30af
-    const int itemCount = HandlerHelper::itemCount( col );
Rex Dieter adf30af
-    if ( itemCount < 0 ) {
Rex Dieter adf30af
+    const CollectionStatistics::Statistics stats = CollectionStatistics::instance()->statistics(col);
Rex Dieter adf30af
+    if ( stats.count == -1 ) {
Rex Dieter adf30af
       return failureResponse( "Unable to determine item count" );
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    response.setString( QByteArray::number( itemCount ) + " EXISTS" );
Rex Dieter adf30af
+    response.setString( QByteArray::number( stats.count ) + " EXISTS" );
Rex Dieter adf30af
     Q_EMIT responseAvailable( response );
Rex Dieter adf30af
 
Rex Dieter adf30af
-    int readCount = HandlerHelper::itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
Rex Dieter adf30af
-                                                                          << QLatin1String( AKONADI_FLAG_IGNORED ) );
Rex Dieter adf30af
-    if ( readCount < 0 || itemCount < readCount ) {
Rex Dieter adf30af
-      return failureResponse( "Unable to retrieve unseen count" );
Rex Dieter adf30af
-    }
Rex Dieter adf30af
-    response.setString( "OK [UNSEEN " + QByteArray::number( itemCount - readCount ) + "] Message 0 is first unseen" );
Rex Dieter adf30af
+    response.setString( "OK [UNSEEN " + QByteArray::number( stats.count - stats.read ) + "] Message 0 is first unseen" );
Rex Dieter adf30af
     Q_EMIT responseAvailable( response );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/src/handler/status.cpp b/server/src/handler/status.cpp
Rex Dieter adf30af
index 8c6823d..283532c 100644
Rex Dieter adf30af
--- a/server/src/handler/status.cpp
Rex Dieter adf30af
+++ b/server/src/handler/status.cpp
Rex Dieter adf30af
@@ -25,6 +25,7 @@
Rex Dieter adf30af
 #include "storage/datastore.h"
Rex Dieter adf30af
 #include "storage/entity.h"
Rex Dieter adf30af
 #include "storage/countquerybuilder.h"
Rex Dieter adf30af
+#include "storage/collectionstatistics.h"
Rex Dieter adf30af
 
Rex Dieter adf30af
 #include "response.h"
Rex Dieter adf30af
 #include "handlerhelper.h"
Rex Dieter adf30af
@@ -62,9 +63,9 @@ bool Status::parseStream()
Rex Dieter adf30af
     // Responses:
Rex Dieter adf30af
     // REQUIRED untagged responses: STATUS
Rex Dieter adf30af
 
Rex Dieter adf30af
-  qint64 itemCount, itemSize;
Rex Dieter adf30af
-  if ( !HandlerHelper::itemStatistics( col, itemCount, itemSize ) ) {
Rex Dieter adf30af
-    return failureResponse( "Failed to query statistics." );
Rex Dieter adf30af
+  const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
Rex Dieter adf30af
+  if (stats.count == -1) {
Rex Dieter adf30af
+      return failureResponse( "Failed to query statistics." );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
     // build STATUS response
Rex Dieter adf30af
@@ -72,7 +73,7 @@ bool Status::parseStream()
Rex Dieter adf30af
     // MESSAGES - The number of messages in the mailbox
Rex Dieter adf30af
   if ( attributeList.contains( AKONADI_ATTRIBUTE_MESSAGES ) ) {
Rex Dieter adf30af
     statusResponse += AKONADI_ATTRIBUTE_MESSAGES " ";
Rex Dieter adf30af
-    statusResponse += QByteArray::number( itemCount );
Rex Dieter adf30af
+    statusResponse += QByteArray::number( stats.count );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( attributeList.contains( AKONADI_ATTRIBUTE_UNSEEN ) ) {
Rex Dieter adf30af
@@ -80,21 +81,14 @@ bool Status::parseStream()
Rex Dieter adf30af
       statusResponse += " ";
Rex Dieter adf30af
     }
Rex Dieter adf30af
     statusResponse += AKONADI_ATTRIBUTE_UNSEEN " ";
Rex Dieter adf30af
-
Rex Dieter adf30af
-    // itemWithFlagCount is twice as fast as itemWithoutFlagCount...
Rex Dieter adf30af
-    const int count = HandlerHelper::itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
Rex Dieter adf30af
-                                                                            << QLatin1String( AKONADI_FLAG_IGNORED ) );
Rex Dieter adf30af
-    if ( count < 0 ) {
Rex Dieter adf30af
-      return failureResponse( "Unable to retrieve unread count" );
Rex Dieter adf30af
-    }
Rex Dieter adf30af
-    statusResponse += QByteArray::number( itemCount - count );
Rex Dieter adf30af
+    statusResponse += QByteArray::number( stats.count - stats.read );
Rex Dieter adf30af
   }
Rex Dieter adf30af
   if ( attributeList.contains( AKONADI_PARAM_SIZE ) ) {
Rex Dieter adf30af
     if ( !statusResponse.isEmpty() ) {
Rex Dieter adf30af
       statusResponse += " ";
Rex Dieter adf30af
     }
Rex Dieter adf30af
     statusResponse += AKONADI_PARAM_SIZE " ";
Rex Dieter adf30af
-    statusResponse += QByteArray::number( itemSize );
Rex Dieter adf30af
+    statusResponse += QByteArray::number( stats.size );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   Response response;
Rex Dieter adf30af
diff --git a/server/src/handlerhelper.cpp b/server/src/handlerhelper.cpp
Rex Dieter adf30af
index 82347b4..39583ce 100644
Rex Dieter adf30af
--- a/server/src/handlerhelper.cpp
Rex Dieter adf30af
+++ b/server/src/handlerhelper.cpp
Rex Dieter adf30af
@@ -22,6 +22,7 @@
Rex Dieter adf30af
 #include "storage/countquerybuilder.h"
Rex Dieter adf30af
 #include "storage/datastore.h"
Rex Dieter adf30af
 #include "storage/selectquerybuilder.h"
Rex Dieter adf30af
+#include "storage/collectionstatistics.h"
Rex Dieter adf30af
 #include "storage/queryhelper.h"
Rex Dieter adf30af
 #include "libs/imapparser_p.h"
Rex Dieter adf30af
 #include "libs/protocol_p.h"
Rex Dieter adf30af
@@ -78,74 +79,6 @@ QString HandlerHelper::pathForCollection( const Collection &col )
Rex Dieter adf30af
   return parts.join( QLatin1String( "/" ) );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-bool HandlerHelper::itemStatistics( const Collection &col, qint64 &count, qint64 &size )
Rex Dieter adf30af
-{
Rex Dieter adf30af
-  QueryBuilder qb( PimItem::tableName() );
Rex Dieter adf30af
-  qb.addAggregation( PimItem::idColumn(), QLatin1String( "count" ) );
Rex Dieter adf30af
-  qb.addAggregation( PimItem::sizeColumn(), QLatin1String( "sum" ) );
Rex Dieter adf30af
-
Rex Dieter adf30af
-  if ( col.isVirtual() ) {
Rex Dieter adf30af
-    qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
Rex Dieter adf30af
-                CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
Rex Dieter adf30af
-    qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
Rex Dieter adf30af
-  } else {
Rex Dieter adf30af
-    qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-
Rex Dieter adf30af
-  if ( !qb.exec() ) {
Rex Dieter adf30af
-    return false;
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  if ( !qb.query().next() ) {
Rex Dieter adf30af
-    akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
Rex Dieter adf30af
-    return false;
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  count = qb.query().value( 0 ).toLongLong();
Rex Dieter adf30af
-  size = qb.query().value( 1 ).toLongLong();
Rex Dieter adf30af
-  return true;
Rex Dieter adf30af
-}
Rex Dieter adf30af
-
Rex Dieter adf30af
-int HandlerHelper::itemWithFlagsCount( const Collection &col, const QStringList &flags )
Rex Dieter adf30af
-{
Rex Dieter adf30af
-  CountQueryBuilder qb( PimItem::tableName(), PimItem::idFullColumnName(), CountQueryBuilder::Distinct );
Rex Dieter adf30af
-  qb.addJoin( QueryBuilder::InnerJoin, PimItemFlagRelation::tableName(),
Rex Dieter adf30af
-              PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName() );
Rex Dieter adf30af
-  if ( col.isVirtual() ) {
Rex Dieter adf30af
-    qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
Rex Dieter adf30af
-                CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
Rex Dieter adf30af
-    qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
Rex Dieter adf30af
-  } else {
Rex Dieter adf30af
-    qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, col.id() );
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  Query::Condition cond( Query::Or );
Rex Dieter adf30af
-  // We use the below instead of an inner join in the query above because postgres seems
Rex Dieter adf30af
-  // to struggle to optimize the two inner joins, despite having indices that should
Rex Dieter adf30af
-  // facilitate that. This exploits the fact that the Flag::retrieveByName is fast because
Rex Dieter adf30af
-  // it hits an in-memory cache.
Rex Dieter adf30af
-  Q_FOREACH ( const QString &flag, flags ) {
Rex Dieter adf30af
-    const Flag f = Flag::retrieveByName( flag );
Rex Dieter adf30af
-    if (!f.isValid()) {
Rex Dieter adf30af
-      // since we OR this condition, we can skip invalid flags to speed up the query
Rex Dieter adf30af
-      continue;
Rex Dieter adf30af
-    }
Rex Dieter adf30af
-    cond.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, f.id() );
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  qb.addCondition( cond );
Rex Dieter adf30af
-  if ( !qb.exec() ) {
Rex Dieter adf30af
-    return -1;
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  return qb.result();
Rex Dieter adf30af
-}
Rex Dieter adf30af
-
Rex Dieter adf30af
-int HandlerHelper::itemCount( const Collection &col )
Rex Dieter adf30af
-{
Rex Dieter adf30af
-  CountQueryBuilder qb( PimItem::tableName() );
Rex Dieter adf30af
-  qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
Rex Dieter adf30af
-  if ( !qb.exec() ) {
Rex Dieter adf30af
-    return -1;
Rex Dieter adf30af
-  }
Rex Dieter adf30af
-  return qb.result();
Rex Dieter adf30af
-}
Rex Dieter adf30af
-
Rex Dieter adf30af
 int HandlerHelper::parseCachePolicy( const QByteArray &data, Collection &col, int start, bool *changed )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   bool inheritChanged = false;
Rex Dieter adf30af
@@ -233,14 +166,12 @@ QByteArray HandlerHelper::collectionToByteArray( const Collection &col, bool hid
Rex Dieter adf30af
   b += " " AKONADI_PARAM_VIRTUAL " " + QByteArray::number( col.isVirtual() ) + ' ';
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( includeStatistics ) {
Rex Dieter adf30af
-    qint64 itemCount, itemSize;
Rex Dieter adf30af
-    if ( itemStatistics( col, itemCount, itemSize ) ) {
Rex Dieter adf30af
-      b += AKONADI_ATTRIBUTE_MESSAGES " " + QByteArray::number( itemCount ) + ' ';
Rex Dieter adf30af
-      // itemWithFlagCount is twice as fast as itemWithoutFlagCount, so emulated that...
Rex Dieter adf30af
+    const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
Rex Dieter adf30af
+    if (stats.count > -1) {
Rex Dieter adf30af
+      b += AKONADI_ATTRIBUTE_MESSAGES " " + QByteArray::number( stats.count ) + ' ';
Rex Dieter adf30af
       b += AKONADI_ATTRIBUTE_UNSEEN " ";
Rex Dieter adf30af
-      b += QByteArray::number( itemCount - itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
Rex Dieter adf30af
-                                                                                  << QLatin1String( AKONADI_FLAG_IGNORED ) ) );
Rex Dieter adf30af
-      b += " " AKONADI_PARAM_SIZE " " + QByteArray::number( itemSize ) + ' ';
Rex Dieter adf30af
+      b += QByteArray::number( stats.count - stats.read) ;
Rex Dieter adf30af
+      b += " " AKONADI_PARAM_SIZE " " + QByteArray::number( stats.size ) + ' ';
Rex Dieter adf30af
     }
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/src/handlerhelper.h b/server/src/handlerhelper.h
Rex Dieter adf30af
index 22e6e1c..cf9ac22 100644
Rex Dieter adf30af
--- a/server/src/handlerhelper.h
Rex Dieter adf30af
+++ b/server/src/handlerhelper.h
Rex Dieter adf30af
@@ -52,28 +52,6 @@ class HandlerHelper
Rex Dieter adf30af
     static QString pathForCollection( const Collection &col );
Rex Dieter adf30af
 
Rex Dieter adf30af
     /**
Rex Dieter adf30af
-      Returns the amount of existing items in the given collection.
Rex Dieter adf30af
-      @return -1 on error
Rex Dieter adf30af
-    */
Rex Dieter adf30af
-    static int itemCount( const Collection &col );
Rex Dieter adf30af
-
Rex Dieter adf30af
-    /**
Rex Dieter adf30af
-     * Queries for collection statistics.
Rex Dieter adf30af
-     * @param col The collection to query.
Rex Dieter adf30af
-     * @param count The total amount of items in this collection.
Rex Dieter adf30af
-     * @param size The size of all items in this collection.
Rex Dieter adf30af
-     * @return @c false on a query error, @c true otherwise
Rex Dieter adf30af
-     */
Rex Dieter adf30af
-    static bool itemStatistics( const Collection &col, qint64 &count, qint64 &size );
Rex Dieter adf30af
-
Rex Dieter adf30af
-    /**
Rex Dieter adf30af
-      Returns the amount of existing items in the given collection
Rex Dieter adf30af
-      which have a given flag set.
Rex Dieter adf30af
-      @return -1 on error.
Rex Dieter adf30af
-    */
Rex Dieter adf30af
-    static int itemWithFlagsCount( const Collection &col, const QStringList &flags );
Rex Dieter adf30af
-
Rex Dieter adf30af
-    /**
Rex Dieter adf30af
       Parse cache policy and update the given Collection object accoordingly.
Rex Dieter adf30af
       @param changed Indicates whether or not the cache policy already available in @p col
Rex Dieter adf30af
       has actually changed
Rex Dieter adf30af
diff --git a/server/src/storage/collectionstatistics.cpp b/server/src/storage/collectionstatistics.cpp
Rex Dieter adf30af
new file mode 100644
Rex Dieter adf30af
index 0000000..85ee449
Rex Dieter adf30af
--- /dev/null
Rex Dieter adf30af
+++ b/server/src/storage/collectionstatistics.cpp
Rex Dieter adf30af
@@ -0,0 +1,108 @@
Rex Dieter adf30af
+/*
Rex Dieter adf30af
+ * Copyright (C) 2014  Daniel Vrátil <dvratil@redhat.com>
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * This library is free software; you can redistribute it and/or
Rex Dieter adf30af
+ * modify it under the terms of the GNU Lesser General Public
Rex Dieter adf30af
+ * License as published by the Free Software Foundation; either
Rex Dieter adf30af
+ * version 2.1 of the License, or (at your option) any later version.
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * This library is distributed in the hope that it will be useful,
Rex Dieter adf30af
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
Rex Dieter adf30af
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Rex Dieter adf30af
+ * Lesser General Public License for more details.
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * You should have received a copy of the GNU Lesser General Public
Rex Dieter adf30af
+ * License along with this library; if not, write to the Free Software
Rex Dieter adf30af
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ */
Rex Dieter adf30af
+
Rex Dieter adf30af
+#include "collectionstatistics.h"
Rex Dieter adf30af
+#include "querybuilder.h"
Rex Dieter adf30af
+#include "countquerybuilder.h"
Rex Dieter adf30af
+#include "akdebug.h"
Rex Dieter adf30af
+#include "entities.h"
Rex Dieter adf30af
+
Rex Dieter adf30af
+#include <libs/protocol_p.h>
Rex Dieter adf30af
+
Rex Dieter adf30af
+#include <QDateTime>
Rex Dieter adf30af
+
Rex Dieter adf30af
+using namespace Akonadi::Server;
Rex Dieter adf30af
+
Rex Dieter adf30af
+CollectionStatistics *CollectionStatistics::sInstance = 0;
Rex Dieter adf30af
+
Rex Dieter adf30af
+CollectionStatistics* CollectionStatistics::instance()
Rex Dieter adf30af
+{
Rex Dieter adf30af
+    static QMutex lock;
Rex Dieter adf30af
+    lock.lock();
Rex Dieter adf30af
+    if (sInstance == 0) {
Rex Dieter adf30af
+        sInstance = new CollectionStatistics();
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+    lock.unlock();
Rex Dieter adf30af
+    return sInstance;
Rex Dieter adf30af
+}
Rex Dieter adf30af
+
Rex Dieter adf30af
+void CollectionStatistics::invalidateCollection(const Collection &col)
Rex Dieter adf30af
+{
Rex Dieter adf30af
+    QMutexLocker lock(&mCacheLock);
Rex Dieter adf30af
+    mCache.remove(col.id());
Rex Dieter adf30af
+}
Rex Dieter adf30af
+
Rex Dieter adf30af
+const CollectionStatistics::Statistics& CollectionStatistics::statistics(const Collection &col)
Rex Dieter adf30af
+{
Rex Dieter adf30af
+    QMutexLocker lock(&mCacheLock);
Rex Dieter adf30af
+    auto it = mCache.find(col.id());
Rex Dieter adf30af
+    if (it == mCache.constEnd()) {
Rex Dieter adf30af
+        it = mCache.insert(col.id(), getCollectionStatistics(col));
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+    return it.value();
Rex Dieter adf30af
+}
Rex Dieter adf30af
+
Rex Dieter adf30af
+CollectionStatistics::Statistics CollectionStatistics::getCollectionStatistics(const Collection &col)
Rex Dieter adf30af
+{
Rex Dieter adf30af
+    QueryBuilder qb(PimItem::tableName());
Rex Dieter adf30af
+    // COUNT(DISTINCT PimItemTable.id)
Rex Dieter adf30af
+    qb.addAggregation(QString::fromLatin1("DISTINCT %1")
Rex Dieter adf30af
+                          .arg(PimItem::idFullColumnName()),
Rex Dieter adf30af
+                      QLatin1String("count"));
Rex Dieter adf30af
+    // SUM(PimItemTable.size)
Rex Dieter adf30af
+    qb.addAggregation(PimItem::sizeFullColumnName(), QLatin1String("sum"));
Rex Dieter adf30af
+    // SUM(CASE WHEN FlagTable.name IN ('\SEEN', '$IGNORED') THEN 1 ELSE 0 END)
Rex Dieter adf30af
+    // This allows us to get read messages count in a single query with the other
Rex Dieter adf30af
+    // statistics. It is much than doing two queries, because the database
Rex Dieter adf30af
+    // only has to calculate the JOINs once.
Rex Dieter adf30af
+    //
Rex Dieter adf30af
+    // Flag::retrieveByName() will hit the Entity cache, which allows us to avoid
Rex Dieter adf30af
+    // a second JOIN with FlagTable, which PostgreSQL seems to struggle to optimize.
Rex Dieter adf30af
+    Query::Condition cond(Query::Or);
Rex Dieter adf30af
+    cond.addValueCondition(PimItemFlagRelation::rightFullColumnName(),
Rex Dieter adf30af
+                           Query::Equals,
Rex Dieter adf30af
+                           Flag::retrieveByName(QLatin1String(AKONADI_FLAG_SEEN)).id());
Rex Dieter adf30af
+    cond.addValueCondition(PimItemFlagRelation::rightFullColumnName(),
Rex Dieter adf30af
+                           Query::Equals,
Rex Dieter adf30af
+                           Flag::retrieveByName(QLatin1String(AKONADI_FLAG_IGNORED)).id());
Rex Dieter adf30af
+    Query::Case caseStmt(cond, QLatin1String("1"), QLatin1String("0"));
Rex Dieter adf30af
+    qb.addAggregation(caseStmt, QLatin1String("sum"));
Rex Dieter adf30af
+
Rex Dieter adf30af
+    qb.addJoin(QueryBuilder::LeftJoin, PimItemFlagRelation::tableName(),
Rex Dieter adf30af
+               PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName());
Rex Dieter adf30af
+    if (col.isVirtual()) {
Rex Dieter adf30af
+        qb.addJoin(QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
Rex Dieter adf30af
+                   CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName());
Rex Dieter adf30af
+        qb.addValueCondition(CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id());
Rex Dieter adf30af
+    } else {
Rex Dieter adf30af
+        qb.addValueCondition(PimItem::collectionIdColumn(), Query::Equals, col.id());
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+
Rex Dieter adf30af
+    if (!qb.exec()) {
Rex Dieter adf30af
+        return { -1, -1, -1 };
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+    if (!qb.query().next()) {
Rex Dieter adf30af
+        akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
Rex Dieter adf30af
+        return { -1, -1, -1 };
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+
Rex Dieter adf30af
+    return { qb.query().value(0).toLongLong(),
Rex Dieter adf30af
+             qb.query().value(1).toLongLong(),
Rex Dieter adf30af
+             qb.query().value(2).toLongLong() };
Rex Dieter adf30af
+}
Rex Dieter adf30af
diff --git a/server/src/storage/collectionstatistics.h b/server/src/storage/collectionstatistics.h
Rex Dieter adf30af
new file mode 100644
Rex Dieter adf30af
index 0000000..2c0af6a
Rex Dieter adf30af
--- /dev/null
Rex Dieter adf30af
+++ b/server/src/storage/collectionstatistics.h
Rex Dieter adf30af
@@ -0,0 +1,70 @@
Rex Dieter adf30af
+/*
Rex Dieter adf30af
+ * Copyright (C) 2014  Daniel Vrátil <dvratil@redhat.com>
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * This library is free software; you can redistribute it and/or
Rex Dieter adf30af
+ * modify it under the terms of the GNU Lesser General Public
Rex Dieter adf30af
+ * License as published by the Free Software Foundation; either
Rex Dieter adf30af
+ * version 2.1 of the License, or (at your option) any later version.
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * This library is distributed in the hope that it will be useful,
Rex Dieter adf30af
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
Rex Dieter adf30af
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Rex Dieter adf30af
+ * Lesser General Public License for more details.
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * You should have received a copy of the GNU Lesser General Public
Rex Dieter adf30af
+ * License along with this library; if not, write to the Free Software
Rex Dieter adf30af
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ */
Rex Dieter adf30af
+
Rex Dieter adf30af
+#ifndef AKONADI_SERVER_COLLECTIONSTATISTICS_H
Rex Dieter adf30af
+#define AKONADI_SERVER_COLLECTIONSTATISTICS_H
Rex Dieter adf30af
+
Rex Dieter adf30af
+class QMutex;
Rex Dieter adf30af
+
Rex Dieter adf30af
+#include <QHash>
Rex Dieter adf30af
+#include <QMutex>
Rex Dieter adf30af
+
Rex Dieter adf30af
+namespace Akonadi {
Rex Dieter adf30af
+namespace Server {
Rex Dieter adf30af
+
Rex Dieter adf30af
+class Collection;
Rex Dieter adf30af
+
Rex Dieter adf30af
+/**
Rex Dieter adf30af
+ * Provides cache for collection statistics
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * Collection statistics are requested very often, so to take some load from the
Rex Dieter adf30af
+ * database we cache the results until the statistics are invalidated (see
Rex Dieter adf30af
+ * NotificationCollector, which takes care for invalidating the statistics).
Rex Dieter adf30af
+ *
Rex Dieter adf30af
+ * The cache (together with optimization of the actual SQL query) seems to
Rex Dieter adf30af
+ * massively improve initial folder listing on system start (when IO and CPU loads
Rex Dieter adf30af
+ * are very high).
Rex Dieter adf30af
+ */
Rex Dieter adf30af
+class CollectionStatistics
Rex Dieter adf30af
+{
Rex Dieter adf30af
+public:
Rex Dieter adf30af
+    struct Statistics {
Rex Dieter adf30af
+        qint64 count;
Rex Dieter adf30af
+        qint64 size;
Rex Dieter adf30af
+        qint64 read;
Rex Dieter adf30af
+    };
Rex Dieter adf30af
+
Rex Dieter adf30af
+    static CollectionStatistics* instance();
Rex Dieter adf30af
+
Rex Dieter adf30af
+    const Statistics& statistics(const Collection &col);
Rex Dieter adf30af
+    void invalidateCollection(const Collection &col);
Rex Dieter adf30af
+
Rex Dieter adf30af
+private:
Rex Dieter adf30af
+    Statistics getCollectionStatistics(const Collection &col);
Rex Dieter adf30af
+
Rex Dieter adf30af
+    QMutex mCacheLock;
Rex Dieter adf30af
+    QHash<qint64, Statistics> mCache;
Rex Dieter adf30af
+
Rex Dieter adf30af
+    static CollectionStatistics *sInstance;
Rex Dieter adf30af
+};
Rex Dieter adf30af
+
Rex Dieter adf30af
+} // namespace Server
Rex Dieter adf30af
+} // namespace Akonadi
Rex Dieter adf30af
+
Rex Dieter adf30af
+#endif // AKONADI_SERVER_COLLECTIONSTATISTICS_H
Rex Dieter adf30af
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
Rex Dieter adf30af
index 304f0e8..0983d84 100644
Rex Dieter adf30af
--- a/server/src/storage/datastore.cpp
Rex Dieter adf30af
+++ b/server/src/storage/datastore.cpp
Rex Dieter adf30af
@@ -209,7 +209,7 @@ DataStore *DataStore::self()
Rex Dieter adf30af
 /* --- ItemFlags ----------------------------------------------------- */
Rex Dieter adf30af
 
Rex Dieter adf30af
 bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
Rex Dieter adf30af
-                               bool *flagsChanged, bool silent )
Rex Dieter adf30af
+                               bool *flagsChanged, const Collection &col, bool silent )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   QSet<QByteArray> removedFlags;
Rex Dieter adf30af
   QSet<QByteArray> addedFlags;
Rex Dieter adf30af
@@ -258,7 +258,7 @@ bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( !silent && ( !addedFlags.isEmpty() || !removedFlags.isEmpty() ) ) {
Rex Dieter adf30af
-    mNotificationCollector->itemsFlagsChanged( items, addedFlags, removedFlags );
Rex Dieter adf30af
+    mNotificationCollector->itemsFlagsChanged( items, addedFlags, removedFlags, col );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   setBoolPtr( flagsChanged, ( addedFlags != removedFlags ) );
Rex Dieter adf30af
@@ -361,7 +361,7 @@ bool DataStore::appendItemsFlags( const PimItem::List &items, const QVector
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 bool DataStore::removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
Rex Dieter adf30af
-                                  bool *flagsChanged, bool silent )
Rex Dieter adf30af
+                                  bool *flagsChanged, const Collection &col, bool silent )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   QSet<QByteArray> removedFlags;
Rex Dieter adf30af
   QVariantList itemsIds;
Rex Dieter adf30af
@@ -393,7 +393,7 @@ bool DataStore::removeItemsFlags( const PimItem::List &items, const QVector
Rex Dieter adf30af
   if ( qb.query().numRowsAffected() != 0 ) {
Rex Dieter adf30af
     setBoolPtr( flagsChanged, true );
Rex Dieter adf30af
     if ( !silent ) {
Rex Dieter adf30af
-      mNotificationCollector->itemsFlagsChanged( items, QSet<QByteArray>(), removedFlags );
Rex Dieter adf30af
+      mNotificationCollector->itemsFlagsChanged( items, QSet<QByteArray>(), removedFlags, col );
Rex Dieter adf30af
     }
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h
Rex Dieter adf30af
index 395b227..a2d8a42 100644
Rex Dieter adf30af
--- a/server/src/storage/datastore.h
Rex Dieter adf30af
+++ b/server/src/storage/datastore.h
Rex Dieter adf30af
@@ -119,10 +119,12 @@ class DataStore : public QObject
Rex Dieter adf30af
     static DataStore *self();
Rex Dieter adf30af
 
Rex Dieter adf30af
     /* --- ItemFlags ----------------------------------------------------- */
Rex Dieter adf30af
-    virtual bool setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *flagsChanged = 0, bool silent = false );
Rex Dieter adf30af
+    virtual bool setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
Rex Dieter adf30af
+                                bool *flagsChanged = 0, const Collection &col = Collection(), bool silent = false );
Rex Dieter adf30af
     virtual bool appendItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *flagsChanged = 0,
Rex Dieter adf30af
                                    bool checkIfExists = true, const Collection &col = Collection(), bool silent = false );
Rex Dieter adf30af
-    virtual bool removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *tagsChanged = 0, bool silent = false );
Rex Dieter adf30af
+    virtual bool removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *tagsChanged = 0,
Rex Dieter adf30af
+                                   const Collection &collection = Collection(), bool silent = false );
Rex Dieter adf30af
 
Rex Dieter adf30af
     /* --- ItemTags ----------------------------------------------------- */
Rex Dieter adf30af
     virtual bool setItemsTags( const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = 0, bool silent = false );
Rex Dieter adf30af
diff --git a/server/src/storage/notificationcollector.cpp b/server/src/storage/notificationcollector.cpp
Rex Dieter adf30af
index 67f57d1..dbc7883 100644
Rex Dieter adf30af
--- a/server/src/storage/notificationcollector.cpp
Rex Dieter adf30af
+++ b/server/src/storage/notificationcollector.cpp
Rex Dieter adf30af
@@ -20,6 +20,7 @@
Rex Dieter adf30af
 #include "notificationcollector.h"
Rex Dieter adf30af
 #include "storage/datastore.h"
Rex Dieter adf30af
 #include "storage/entity.h"
Rex Dieter adf30af
+#include "storage/collectionstatistics.h"
Rex Dieter adf30af
 #include "handlerhelper.h"
Rex Dieter adf30af
 #include "cachecleaner.h"
Rex Dieter adf30af
 #include "intervalcheck.h"
Rex Dieter adf30af
@@ -133,6 +134,7 @@ void NotificationCollector::collectionChanged( const Collection &collection,
Rex Dieter adf30af
   if ( AkonadiServer::instance()->intervalChecker() ) {
Rex Dieter adf30af
     AkonadiServer::instance()->intervalChecker()->collectionAdded( collection.id() );
Rex Dieter adf30af
   }
Rex Dieter adf30af
+  CollectionStatistics::instance()->invalidateCollection(collection);
Rex Dieter adf30af
   collectionNotification( NotificationMessageV2::Modify, collection, collection.parentId(), -1, resource, changes.toSet() );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
@@ -159,6 +161,8 @@ void NotificationCollector::collectionRemoved( const Collection &collection,
Rex Dieter adf30af
   if ( AkonadiServer::instance()->intervalChecker() ) {
Rex Dieter adf30af
     AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
Rex Dieter adf30af
   }
Rex Dieter adf30af
+  CollectionStatistics::instance()->invalidateCollection(collection);
Rex Dieter adf30af
+
Rex Dieter adf30af
   collectionNotification( NotificationMessageV2::Remove, collection, collection.parentId(), -1, resource );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
@@ -183,6 +187,8 @@ void NotificationCollector::collectionUnsubscribed( const Collection &collection
Rex Dieter adf30af
   if ( AkonadiServer::instance()->intervalChecker() ) {
Rex Dieter adf30af
     AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
Rex Dieter adf30af
   }
Rex Dieter adf30af
+  CollectionStatistics::instance()->invalidateCollection(collection);
Rex Dieter adf30af
+
Rex Dieter adf30af
   collectionNotification( NotificationMessageV2::Unsubscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>() );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
@@ -282,6 +288,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
Rex Dieter adf30af
     copy.setParentCollection( iter.key() );
Rex Dieter adf30af
     copy.setResource( resource );
Rex Dieter adf30af
 
Rex Dieter adf30af
+    CollectionStatistics::instance()->invalidateCollection(Collection::retrieveById(iter.key()));
Rex Dieter adf30af
     dispatchNotification( copy );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
@@ -304,6 +311,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
Rex Dieter adf30af
   }
Rex Dieter adf30af
   msg.setResource( res );
Rex Dieter adf30af
 
Rex Dieter adf30af
+  CollectionStatistics::instance()->invalidateCollection(col);
Rex Dieter adf30af
   dispatchNotification( msg );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/tests/unittest/fakedatastore.cpp b/server/tests/unittest/fakedatastore.cpp
Rex Dieter adf30af
index 12214fa..43ef7e6 100644
Rex Dieter adf30af
--- a/server/tests/unittest/fakedatastore.cpp
Rex Dieter adf30af
+++ b/server/tests/unittest/fakedatastore.cpp
Rex Dieter adf30af
@@ -91,13 +91,15 @@ bool FakeDataStore::init()
Rex Dieter adf30af
 bool FakeDataStore::setItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
                                    const QVector<Flag> &flags,
Rex Dieter adf30af
                                    bool *flagsChanged,
Rex Dieter adf30af
+                                   const Collection &col,
Rex Dieter adf30af
                                    bool silent )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   mChanges.insert( QLatin1String( "setItemsFlags" ),
Rex Dieter adf30af
                    QVariantList() << QVariant::fromValue( items )
Rex Dieter adf30af
                                   << QVariant::fromValue( flags )
Rex Dieter adf30af
+                                  << QVariant::fromValue( col )
Rex Dieter adf30af
                                   << silent );
Rex Dieter adf30af
-  return DataStore::setItemsFlags( items, flags, flagsChanged, silent );
Rex Dieter adf30af
+  return DataStore::setItemsFlags( items, flags, flagsChanged, col, silent );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 bool FakeDataStore::appendItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
@@ -119,13 +121,15 @@ bool FakeDataStore::appendItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
 bool FakeDataStore::removeItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
                                       const QVector<Flag> &flags,
Rex Dieter adf30af
                                       bool *flagsChanged,
Rex Dieter adf30af
+                                      const Collection &col,
Rex Dieter adf30af
                                       bool silent )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   mChanges.insert( QLatin1String( "removeItemsFlags" ),
Rex Dieter adf30af
                    QVariantList() << QVariant::fromValue( items )
Rex Dieter adf30af
                                   << QVariant::fromValue( flags )
Rex Dieter adf30af
+                                  << QVariant::fromValue( col )
Rex Dieter adf30af
                                   << silent );
Rex Dieter adf30af
-  return DataStore::removeItemsFlags( items, flags, flagsChanged, silent );
Rex Dieter adf30af
+  return DataStore::removeItemsFlags( items, flags, flagsChanged, col, silent );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/tests/unittest/fakedatastore.h b/server/tests/unittest/fakedatastore.h
Rex Dieter adf30af
index 62c5b75..cd9ab13 100644
Rex Dieter adf30af
--- a/server/tests/unittest/fakedatastore.h
Rex Dieter adf30af
+++ b/server/tests/unittest/fakedatastore.h
Rex Dieter adf30af
@@ -41,6 +41,7 @@ class FakeDataStore: public DataStore
Rex Dieter adf30af
     virtual bool setItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
                                 const QVector<Flag> &flags,
Rex Dieter adf30af
                                 bool *flagsChanged = 0,
Rex Dieter adf30af
+                                const Collection &col = Collection(),
Rex Dieter adf30af
                                 bool silent = false );
Rex Dieter adf30af
     virtual bool appendItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
                                    const QVector<Flag> &flags,
Rex Dieter adf30af
@@ -51,6 +52,7 @@ class FakeDataStore: public DataStore
Rex Dieter adf30af
     virtual bool removeItemsFlags( const PimItem::List &items,
Rex Dieter adf30af
                                    const QVector<Flag> &flags,
Rex Dieter adf30af
                                    bool *flagsChanged = 0,
Rex Dieter adf30af
+                                   const Collection &col = Collection(),
Rex Dieter adf30af
                                    bool silent = false );
Rex Dieter adf30af
 
Rex Dieter adf30af
     virtual bool setItemsTags( const PimItem::List &items,
Rex Dieter adf30af
-- 
Rex Dieter adf30af
2.1.0
Rex Dieter adf30af