Compare commits
78 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f93f842792 | ||
|
|
04d7652051 | ||
|
|
d5433cf6e8 | ||
|
|
b94e5a0e93 | ||
|
|
547cef98b9 | ||
|
|
2bcdc9e18b | ||
|
|
aefd5236d8 | ||
|
|
8e0da1155d | ||
|
|
3e10cdbabe | ||
|
|
bad5a7962c | ||
|
|
24bd5578bf | ||
|
|
e513ef09a9 | ||
|
|
1c611726ac | ||
|
|
1d79ef60cf | ||
|
|
e0c683030d | ||
|
|
b53992e7f4 | ||
|
|
71184c15cb | ||
|
|
d4cb712128 | ||
|
|
5a7644bcb2 | ||
|
|
5f516d08a4 | ||
|
|
e63e99f1ef | ||
|
|
442c71f0ec | ||
|
|
c183a6e345 | ||
|
|
070820a23d | ||
|
|
e0386f5e78 | ||
|
|
99916b63be | ||
|
|
80b6168b8d | ||
|
|
b8f85d80a1 | ||
|
|
13aaf3464f | ||
|
|
cba6ce5ec1 | ||
|
|
2981736d02 | ||
|
|
80bf6d98ef | ||
|
|
04ca9f646e | ||
|
|
21ec943481 | ||
|
|
b97ca23711 | ||
|
|
7e082edf72 | ||
|
|
36b47e46be | ||
|
|
258eaca3ec | ||
|
|
8b4426d5d3 | ||
|
|
6505b31d44 | ||
|
|
eb7406d932 | ||
|
|
62a3cba7d7 | ||
|
|
8942b654a7 | ||
|
|
bc3af2d35c | ||
|
|
3845bc69ad | ||
|
|
3ceac1733c | ||
|
|
c9783c242f | ||
|
|
a4f5b0576f | ||
|
|
8b319ccb4c | ||
|
|
e63d049f49 | ||
|
|
adf30afcb0 | ||
|
|
ea4f267da4 | ||
|
|
f8437d9b98 | ||
|
|
8acc3d6ce7 | ||
|
|
002028f889 | ||
|
|
b80c244f30 | ||
|
|
a87e2990ff | ||
|
|
338a460da5 | ||
|
|
5c1d74f712 | ||
|
|
baf6f13ced | ||
|
|
04b405e175 | ||
|
|
65d3207aae | ||
|
|
e9c8aa0fe7 | ||
|
|
12eb55c0c8 | ||
|
|
78fb1bf7ea | ||
|
|
6b7be2b3ab | ||
|
|
7170ac89c4 | ||
|
|
660e892d22 | ||
|
|
fcd69dcca6 | ||
|
|
fdc262e626 | ||
|
|
215b266c1a | ||
|
|
9fdf2bb2a7 | ||
|
|
998c8cf861 | ||
|
|
ddb5148507 | ||
|
|
00571966d4 | ||
|
|
8a132797b8 | ||
|
|
6fef9757f6 | ||
|
|
5e14a94748 |
38 changed files with 4579 additions and 153 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,4 +1 @@
|
|||
/akonadi-1.9.2.tar.bz2
|
||||
/akonadi-1.10.1.tar.bz2
|
||||
/akonadi-1.10.2.tar.bz2
|
||||
/akonadi-1.10.3-1.tar.bz2
|
||||
/akonadi-1.13.0.tar.bz2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
From b60702e0b7041c56a3cb52c209204d28406f3ce5 Mon Sep 17 00:00:00 2001
|
||||
From: Raphael Kubo da Costa <rakuco@FreeBSD.org>
|
||||
Date: Wed, 13 Aug 2014 14:43:04 +0300
|
||||
Subject: [PATCH 01/30] FindSqlite: Use CMAKE_FLAGS the right way in
|
||||
try_compile().
|
||||
|
||||
This fixes f90774f1 ("Check whether Sqlite is compiled with
|
||||
SQLITE_ENABLE_UNLOCK_NOTIFY"), so that SQLITE_INCLUDE_DIR is really
|
||||
passed to the try_compile() call. So far, it was just a NOP and the
|
||||
compilation only worked if sqlite3.h was in a directory the compiler
|
||||
uses automatically.
|
||||
|
||||
try_compile()'s syntax is a bit complicated, and CMAKE_FLAGS expects a
|
||||
series of arguments as if they had been passed to the command line, so
|
||||
instead of "CMAKE_FLAGS INCLUDE_DIRECTORIES /some/dir" one needs to use
|
||||
"CMAKE_FLAGS -DINCLUDE_DIRECTORIES:PATH=/some/dir".
|
||||
|
||||
REVIEW: 119762
|
||||
---
|
||||
cmake/modules/FindSqlite.cmake | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/cmake/modules/FindSqlite.cmake b/cmake/modules/FindSqlite.cmake
|
||||
index ad8cdb4..c43a7b5 100644
|
||||
--- a/cmake/modules/FindSqlite.cmake
|
||||
+++ b/cmake/modules/FindSqlite.cmake
|
||||
@@ -94,7 +94,7 @@ if(EXISTS ${SQLITE_INCLUDE_DIR}/sqlite3.h)
|
||||
${CMAKE_BINARY_DIR}/sqlite_check_unlock_notify
|
||||
${CMAKE_BINARY_DIR}/sqlite_check_unlock_notify.cpp
|
||||
LINK_LIBRARIES ${SQLITE_LIBRARIES}
|
||||
- CMAKE_FLAGS INCLUDE_DIRECTORIES ${SQLITE_INCLUDE_DIR})
|
||||
+ CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:PATH=${SQLITE_INCLUDE_DIR}")
|
||||
if (NOT SQLITE_HAS_UNLOCK_NOTIFY)
|
||||
message(STATUS "Sqlite ${SQLITE_VERSION} was found, but it is not compiled with -DSQLITE_ENABLE_UNLOCK_NOTIFY")
|
||||
endif()
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
From 2146519108ec66300328b7b3979477c7789795d3 Mon Sep 17 00:00:00 2001
|
||||
From: Raphael Kubo da Costa <rakuco@FreeBSD.org>
|
||||
Date: Wed, 13 Aug 2014 23:22:11 +0300
|
||||
Subject: [PATCH 02/30] Do not enter the test/ directories if
|
||||
AKONADI_BUILD_TESTS is off.
|
||||
|
||||
enable_testing() only determines whether a "test" target and the related
|
||||
CTest files will be created. And in Akonadi's case it is actually
|
||||
invoked regardless of the value of the AKONADI_BUILD_TESTS option
|
||||
because Akonadi includes the CTest module, which calls enable_testing()
|
||||
based on the value of another variable, BUILD_TESTING.
|
||||
|
||||
In any case, whether the executables and libraries that compose
|
||||
Akonadi's unit tests will be built has nothing to do with
|
||||
enable_testing(). To make AKONADI_BUILD_TESTS really disable the build
|
||||
of the unit tests we now avoid entering the tests/ directories at all
|
||||
when it is off, so that neither tests nor targets they depend on get
|
||||
built.
|
||||
|
||||
REVIEW: 119776
|
||||
---
|
||||
CMakeLists.txt | 6 +-----
|
||||
libs/CMakeLists.txt | 4 +++-
|
||||
server/CMakeLists.txt | 5 ++++-
|
||||
3 files changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 0c52009..e081d23 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -14,7 +14,7 @@ include(FeatureSummary)
|
||||
|
||||
############### Build Options ###############
|
||||
|
||||
-include(CTest)
|
||||
+include(CTest) # Calls enable_testing().
|
||||
include(CTestConfig.cmake)
|
||||
option(AKONADI_BUILD_TESTS "Build the Akonadi unit tests." TRUE)
|
||||
option(AKONADI_BUILD_QSQLITE "Build the Sqlite backend." TRUE)
|
||||
@@ -27,10 +27,6 @@ if(NOT DEFINED DATABASE_BACKEND)
|
||||
set(DATABASE_BACKEND "MYSQL" CACHE STRING "The default database backend to use for Akonadi. Can be either MYSQL, POSTGRES or SQLITE")
|
||||
endif()
|
||||
|
||||
-if(AKONADI_BUILD_TESTS)
|
||||
- enable_testing()
|
||||
-endif()
|
||||
-
|
||||
############### CMake Macros ###############
|
||||
|
||||
include(InstallSettings)
|
||||
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
|
||||
index de6ab0d..74de6b2 100644
|
||||
--- a/libs/CMakeLists.txt
|
||||
+++ b/libs/CMakeLists.txt
|
||||
@@ -36,5 +36,7 @@ install(FILES
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/akonadi/private
|
||||
)
|
||||
|
||||
-add_subdirectory(tests)
|
||||
+if(AKONADI_BUILD_TESTS)
|
||||
+ add_subdirectory(tests)
|
||||
+endif()
|
||||
|
||||
diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt
|
||||
index e4829f3..275938d 100644
|
||||
--- a/server/CMakeLists.txt
|
||||
+++ b/server/CMakeLists.txt
|
||||
@@ -64,7 +64,10 @@ endmacro()
|
||||
add_subdirectory(akonadictl)
|
||||
add_subdirectory(control)
|
||||
add_subdirectory(src)
|
||||
-add_subdirectory(tests)
|
||||
+
|
||||
+if(AKONADI_BUILD_TESTS)
|
||||
+ add_subdirectory(tests)
|
||||
+endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS}")
|
||||
if(MYSQLD_EXECUTABLE)
|
||||
--
|
||||
2.1.0
|
||||
|
||||
201
0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch
Normal file
201
0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
From 9734074267bacd39aeb29c7a0d7df7cadb212d89 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 11 Jul 2014 18:33:39 +0200
|
||||
Subject: [PATCH 03/30] STORE: Allow modifying items tags via Tag RID or GID
|
||||
|
||||
Tags RID is of course allowed only to resources
|
||||
|
||||
(Cherry-picked from 1a619d4df010a4862621a03031176ad8759070d3)
|
||||
|
||||
Conflicts:
|
||||
CMakeLists.txt
|
||||
---
|
||||
libs/protocol_p.h | 2 ++
|
||||
server/src/handler/store.cpp | 76 ++++++++++++++++++++++++++++++--------------
|
||||
server/src/handler/store.h | 8 +++--
|
||||
server/src/handlerhelper.cpp | 4 +--
|
||||
4 files changed, 62 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/libs/protocol_p.h b/libs/protocol_p.h
|
||||
index 002abe4..2ec2a2e 100644
|
||||
--- a/libs/protocol_p.h
|
||||
+++ b/libs/protocol_p.h
|
||||
@@ -110,6 +110,7 @@
|
||||
#define AKONADI_PARAM_TAGS "TAGS"
|
||||
#define AKONADI_PARAM_FULLPAYLOAD "FULLPAYLOAD"
|
||||
#define AKONADI_PARAM_GID "GID"
|
||||
+#define AKONADI_PARAM_GTAGS "GTAGS"
|
||||
#define AKONADI_PARAM_IGNOREERRORS "IGNOREERRORS"
|
||||
#define AKONADI_PARAM_INDEX "INDEX"
|
||||
#define AKONADI_PARAM_INHERIT "INHERIT"
|
||||
@@ -137,6 +138,7 @@
|
||||
#define AKONADI_PARAM_REMOTEREVISION "REMOTEREVISION"
|
||||
#define AKONADI_PARAM_RESOURCE "RESOURCE"
|
||||
#define AKONADI_PARAM_REVISION "REV"
|
||||
+#define AKONADI_PARAM_RTAGS "RTAGS"
|
||||
#define AKONADI_PARAM_SILENT "SILENT"
|
||||
#define AKONADI_PARAM_DOT_SILENT ".SILENT"
|
||||
#define AKONADI_PARAM_CAPABILITY_SERVERSEARCH "SERVERSEARCH"
|
||||
diff --git a/server/src/handler/store.cpp b/server/src/handler/store.cpp
|
||||
index 6664a09..4a503a2 100644
|
||||
--- a/server/src/handler/store.cpp
|
||||
+++ b/server/src/handler/store.cpp
|
||||
@@ -115,35 +115,56 @@ bool Store::deleteFlags( const PimItem::List &items, const QVector<QByteArray> &
|
||||
return true;
|
||||
}
|
||||
|
||||
-bool Store::replaceTags( const PimItem::List &item, const ImapSet &tags )
|
||||
+bool Store::replaceTags( const PimItem::List &item, const Tag::List &tags )
|
||||
{
|
||||
- const Tag::List tagList = HandlerHelper::resolveTags( tags );
|
||||
- if ( !connection()->storageBackend()->setItemsTags( item, tagList ) ) {
|
||||
+ if ( !connection()->storageBackend()->setItemsTags( item, tags ) ) {
|
||||
throw HandlerException( "Store::replaceTags: Unable to set new item tags" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
-bool Store::addTags( const PimItem::List &items, const ImapSet &tags, bool &tagsChanged )
|
||||
+bool Store::addTags( const PimItem::List &items, const Tag::List &tags, bool &tagsChanged )
|
||||
{
|
||||
- const Tag::List tagList = HandlerHelper::resolveTags( tags );
|
||||
- if ( !connection()->storageBackend()->appendItemsTags( items, tagList, &tagsChanged ) ) {
|
||||
+ if ( !connection()->storageBackend()->appendItemsTags( items, tags, &tagsChanged ) ) {
|
||||
akDebug() << "Store::addTags: Unable to add new item tags";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
-bool Store::deleteTags( const PimItem::List &items, const ImapSet &tags )
|
||||
+bool Store::deleteTags( const PimItem::List &items, const Tag::List &tags )
|
||||
{
|
||||
- const Tag::List tagList = HandlerHelper::resolveTags( tags );
|
||||
- if ( !connection()->storageBackend()->removeItemsTags( items, tagList ) ) {
|
||||
+ if ( !connection()->storageBackend()->removeItemsTags( items, tags ) ) {
|
||||
akDebug() << "Store::deleteTags: Unable to remove item tags";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+bool Store::processTagsChange( Store::Operation op, const PimItem::List &items,
|
||||
+ const Tag::List &tags, QSet<QByteArray> &changes )
|
||||
+{
|
||||
+ bool tagsChanged = true;
|
||||
+ if ( op == Replace ) {
|
||||
+ tagsChanged = replaceTags( items, tags );
|
||||
+ } else if ( op == Add ) {
|
||||
+ if ( !addTags( items, tags, tagsChanged ) ) {
|
||||
+ return failureResponse( "Unable to add item tags." );
|
||||
+ }
|
||||
+ } else if ( op == Delete ) {
|
||||
+ if ( !( tagsChanged = deleteTags( items, tags ) ) ) {
|
||||
+ return failureResponse( "Unable to remove item tags." );
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if ( tagsChanged && !changes.contains( AKONADI_PARAM_TAGS ) ) {
|
||||
+ changes << AKONADI_PARAM_TAGS;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+
|
||||
bool Store::parseStream()
|
||||
{
|
||||
parseCommand();
|
||||
@@ -234,22 +255,31 @@ bool Store::parseStream()
|
||||
}
|
||||
|
||||
if ( command == AKONADI_PARAM_TAGS ) {
|
||||
- bool tagsChanged = true;
|
||||
- const ImapSet tags = m_streamParser->readSequenceSet();
|
||||
- if ( op == Replace ) {
|
||||
- tagsChanged = replaceTags( pimItems, tags );
|
||||
- } else if ( op == Add ) {
|
||||
- if ( !addTags( pimItems, tags, tagsChanged ) ) {
|
||||
- return failureResponse( "Unable to add item tags." );
|
||||
- }
|
||||
- } else if ( op == Delete ) {
|
||||
- if ( !( tagsChanged = deleteTags( pimItems, tags ) ) ) {
|
||||
- return failureResponse( "Unable to remove item tags." );
|
||||
- }
|
||||
+ const ImapSet tagsIds = m_streamParser->readSequenceSet();
|
||||
+ const Tag::List tags = HandlerHelper::resolveTags( tagsIds );
|
||||
+ if (!processTagsChange( op, pimItems, tags, changes )) {
|
||||
+ return false;
|
||||
}
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ( command == AKONADI_PARAM_RTAGS ) {
|
||||
+ if (!connection()->context()->resource().isValid()) {
|
||||
+ throw HandlerException( "Only resources can use RTAGS" );
|
||||
+ }
|
||||
+ const QVector<QByteArray> tagsIds = m_streamParser->readParenthesizedList().toVector();
|
||||
+ const Tag::List tags = HandlerHelper::resolveTagsByRID( tagsIds, connection()->context() );
|
||||
+ if (!processTagsChange( op, pimItems, tags, changes )) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if ( tagsChanged && !changes.contains( AKONADI_PARAM_TAGS ) ) {
|
||||
- changes << AKONADI_PARAM_TAGS;
|
||||
+ if ( command == AKONADI_PARAM_GTAGS ) {
|
||||
+ const QVector<QByteArray> tagsIds = m_streamParser->readParenthesizedList().toVector();
|
||||
+ const Tag::List tags = HandlerHelper::resolveTagsByGID( tagsIds );
|
||||
+ if (!processTagsChange( op, pimItems, tags, changes )) {
|
||||
+ return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
diff --git a/server/src/handler/store.h b/server/src/handler/store.h
|
||||
index ad3a5a0..c618a53 100644
|
||||
--- a/server/src/handler/store.h
|
||||
+++ b/server/src/handler/store.h
|
||||
@@ -115,12 +115,14 @@ class Store : public Handler
|
||||
bool replaceFlags( const PimItem::List &items, const QVector<QByteArray> &flags );
|
||||
bool addFlags( const PimItem::List &items, const QVector<QByteArray> &flags, bool &flagsChanged );
|
||||
bool deleteFlags( const PimItem::List &items, const QVector<QByteArray> &flags );
|
||||
- bool replaceTags( const PimItem::List &items, const ImapSet &tags );
|
||||
- bool addTags( const PimItem::List &items, const ImapSet &tags, bool &tagsChanged );
|
||||
- bool deleteTags( const PimItem::List &items, const ImapSet &tags );
|
||||
+ bool replaceTags( const PimItem::List &items, const Tag::List &tags );
|
||||
+ bool addTags( const PimItem::List &items, const Tag::List &tags, bool &tagsChanged );
|
||||
+ bool deleteTags( const PimItem::List &items, const Tag::List &tags );
|
||||
bool setGid( const PimItem &item, const QString &gid );
|
||||
void sendPimItemResponse( const PimItem &pimItem );
|
||||
|
||||
+ bool processTagsChange(Store::Operation operation, const PimItem::List &items, const Tag::List &tags, QSet<QByteArray> &changes);
|
||||
+
|
||||
private:
|
||||
Scope mScope;
|
||||
int mPos;
|
||||
diff --git a/server/src/handlerhelper.cpp b/server/src/handlerhelper.cpp
|
||||
index 763ea30..634a26c 100644
|
||||
--- a/server/src/handlerhelper.cpp
|
||||
+++ b/server/src/handlerhelper.cpp
|
||||
@@ -366,7 +366,7 @@ Tag::List HandlerHelper::resolveTagsByGID(const QVector<QByteArray> &tagsGIDs)
|
||||
}
|
||||
|
||||
Q_FOREACH (const QByteArray &tagGID, tagsGIDs) {
|
||||
- Tag::List tags = Tag::retrieveFiltered(Tag::gidColumn(), tagGID);
|
||||
+ Tag::List tags = Tag::retrieveFiltered(Tag::gidColumn(), QString::fromLatin1(tagGID));
|
||||
Tag tag;
|
||||
if (tags.isEmpty()) {
|
||||
tag.setGid(QString::fromUtf8(tagGID));
|
||||
@@ -413,7 +413,7 @@ Tag::List HandlerHelper::resolveTagsByRID(const QVector< QByteArray >& tagsRIDs,
|
||||
cond.addColumnCondition(Tag::idFullColumnName(), Query::Equals, TagRemoteIdResourceRelation::tagIdFullColumnName());
|
||||
cond.addValueCondition(TagRemoteIdResourceRelation::resourceIdFullColumnName(), Query::Equals, context->resource().id());
|
||||
qb.addJoin(QueryBuilder::LeftJoin, TagRemoteIdResourceRelation::tableName(), cond);
|
||||
- qb.addValueCondition(TagRemoteIdResourceRelation::remoteIdFullColumnName(), Query::Equals, tagRID);
|
||||
+ qb.addValueCondition(TagRemoteIdResourceRelation::remoteIdFullColumnName(), Query::Equals, QString::fromLatin1(tagRID));
|
||||
if (!qb.exec()) {
|
||||
throw HandlerException("Unable to resolve tags");
|
||||
}
|
||||
--
|
||||
2.1.0
|
||||
|
||||
27
0004-Fix-typo-in-if-condition.patch
Normal file
27
0004-Fix-typo-in-if-condition.patch
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
From e52f9be20e566e507e77421f1243f51aa2fe8e55 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 25 Aug 2014 14:35:14 +0200
|
||||
Subject: [PATCH 04/30] Fix typo in if condition
|
||||
|
||||
BUG: 338483
|
||||
FIXED-IN: 1.13.1
|
||||
---
|
||||
server/src/handler/akappend.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/src/handler/akappend.cpp b/server/src/handler/akappend.cpp
|
||||
index 43f03ba..ad3682f 100644
|
||||
--- a/server/src/handler/akappend.cpp
|
||||
+++ b/server/src/handler/akappend.cpp
|
||||
@@ -380,7 +380,7 @@ bool AkAppend::parseStream()
|
||||
if ( itemFlags.incremental ) {
|
||||
throw HandlerException( "Incremental flags changes are not allowed in AK-APPEND" );
|
||||
}
|
||||
- if ( itemTagsRID.incremental || itemTagsRID.incremental ) {
|
||||
+ if ( itemTagsRID.incremental || itemTagsGID.incremental ) {
|
||||
throw HandlerException( "Incremental tags changes are not allowed in AK-APPEND" );
|
||||
}
|
||||
|
||||
--
|
||||
2.1.0
|
||||
|
||||
25
0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch
Normal file
25
0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
From 01c86229f9e26d9e036f6f2ab405659ed836b5c0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 8 Sep 2014 15:36:18 +0200
|
||||
Subject: [PATCH 05/30] Fix buffer overflow in AKTEST_FAKESERVER_MAIN()
|
||||
|
||||
---
|
||||
shared/aktest.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/shared/aktest.h b/shared/aktest.h
|
||||
index b1b9caa..3026304 100644
|
||||
--- a/shared/aktest.h
|
||||
+++ b/shared/aktest.h
|
||||
@@ -57,7 +57,7 @@ int main(int argc, char **argv) \
|
||||
} \
|
||||
} \
|
||||
TestObject tc; \
|
||||
- char **fakeArgv = (char **) malloc(options.count()); \
|
||||
+ char **fakeArgv = (char **) malloc(options.count() * sizeof(char**)); \
|
||||
for (int i = 0; i < options.count(); ++i) { \
|
||||
fakeArgv[i] = options[i]; \
|
||||
} \
|
||||
--
|
||||
2.1.0
|
||||
|
||||
29
0006-Don-t-crash-when-setmntent-returns-NULL.patch
Normal file
29
0006-Don-t-crash-when-setmntent-returns-NULL.patch
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
From ca59eb345cfef368242929ea33beca4bff837e9d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Thu, 18 Sep 2014 16:54:26 +0200
|
||||
Subject: [PATCH 06/30] Don't crash when setmntent returns NULL
|
||||
|
||||
setmntent can fail when there's no /etc/mtab file for instance and
|
||||
passing NULL pointer to getmntent crashes, so we need to return when
|
||||
this happens.
|
||||
---
|
||||
server/src/utils.cpp | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/server/src/utils.cpp b/server/src/utils.cpp
|
||||
index b04a812..b51c330 100644
|
||||
--- a/server/src/utils.cpp
|
||||
+++ b/server/src/utils.cpp
|
||||
@@ -179,6 +179,9 @@ QString Utils::getDirectoryFileSystem(const QString &directory)
|
||||
QString bestMatchFS;
|
||||
|
||||
FILE *mtab = setmntent("/etc/mtab", "r");
|
||||
+ if (!mtab) {
|
||||
+ return QString();
|
||||
+ }
|
||||
while (mntent *mnt = getmntent(mtab)) {
|
||||
if (qstrcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) {
|
||||
continue;
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
From c516ec5c28d603aea0df6165f66a3a5d0a0191c4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 19 Sep 2014 10:50:23 +0200
|
||||
Subject: [PATCH 07/30] Don't call insert() from Q_ASSERT - breaks unit-tests
|
||||
in Release mode
|
||||
|
||||
---
|
||||
server/tests/unittest/collectionreferencetest.cpp | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/tests/unittest/collectionreferencetest.cpp b/server/tests/unittest/collectionreferencetest.cpp
|
||||
index 1700c75..1b10c55 100644
|
||||
--- a/server/tests/unittest/collectionreferencetest.cpp
|
||||
+++ b/server/tests/unittest/collectionreferencetest.cpp
|
||||
@@ -45,7 +45,8 @@ public:
|
||||
Resource res;
|
||||
res.setId(1);
|
||||
res.setName(QLatin1String(name));
|
||||
- Q_ASSERT(res.insert());
|
||||
+ const bool success = res.insert();
|
||||
+ Q_ASSERT(success);
|
||||
mResource = res;
|
||||
return res;
|
||||
}
|
||||
@@ -57,7 +58,8 @@ public:
|
||||
col.setName(QLatin1String(name));
|
||||
col.setRemoteId(QLatin1String(name));
|
||||
col.setResource(mResource);
|
||||
- Q_ASSERT(col.insert());
|
||||
+ const bool success = col.insert();
|
||||
+ Q_ASSERT(success);
|
||||
return col;
|
||||
}
|
||||
|
||||
--
|
||||
2.1.0
|
||||
|
||||
32
0008-Suppress-unused-variable-warnings-in-release-mode.patch
Normal file
32
0008-Suppress-unused-variable-warnings-in-release-mode.patch
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
From b35fcb64c3ba3df95f62d0d129adb791ce2bad15 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 19 Sep 2014 11:10:13 +0200
|
||||
Subject: [PATCH 08/30] Suppress unused variable warnings in release mode
|
||||
|
||||
---
|
||||
server/tests/unittest/collectionreferencetest.cpp | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/server/tests/unittest/collectionreferencetest.cpp b/server/tests/unittest/collectionreferencetest.cpp
|
||||
index 1b10c55..9c98f28 100644
|
||||
--- a/server/tests/unittest/collectionreferencetest.cpp
|
||||
+++ b/server/tests/unittest/collectionreferencetest.cpp
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
res.setName(QLatin1String(name));
|
||||
const bool success = res.insert();
|
||||
Q_ASSERT(success);
|
||||
+ Q_UNUSED(success);
|
||||
mResource = res;
|
||||
return res;
|
||||
}
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
col.setResource(mResource);
|
||||
const bool success = col.insert();
|
||||
Q_ASSERT(success);
|
||||
+ Q_UNUSED(success);
|
||||
return col;
|
||||
}
|
||||
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
From e1c69c277ea6005cc358434679b83fa1cb752756 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Tue, 23 Sep 2014 18:00:34 +0200
|
||||
Subject: [PATCH 09/30] Test whether compiler supports all required C++11
|
||||
features at configure time
|
||||
|
||||
To prevent ugly compilation errors when someone tries to compile Akonadi
|
||||
with a compiler that does not support all C++11 features we use, we run
|
||||
a try_compile check in CMakeLists.txt.
|
||||
---
|
||||
CMakeLists.txt | 18 ++++++++++++++++++
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index e081d23..2d790c9 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -216,6 +216,24 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc" OR (CMAKE_CXX_COMP
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wnon-virtual-dtor -Wundef -Wcast-align -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wformat-security -fno-common")
|
||||
|
||||
+ file(WRITE ${CMAKE_BINARY_DIR}/cxx11_check.cpp
|
||||
+ "enum Enum { Value = 1 };
|
||||
+ struct Class {
|
||||
+ Class(int val) { (void)val; };
|
||||
+ // Delegating constructor
|
||||
+ Class(): Class(42) {};
|
||||
+ // New-style enumerator
|
||||
+ Class(Enum e = Enum::Value) { (void)e; };
|
||||
+ };
|
||||
+ int main() {}
|
||||
+ ")
|
||||
+ try_compile(CXX11_SUPPORTED
|
||||
+ ${CMAKE_BINARY_DIR}/cxx11_check
|
||||
+ ${CMAKE_BINARY_DIR}/cxx11_check.cpp)
|
||||
+ if (NOT CXX11_SUPPORTED)
|
||||
+ message(FATAL_ERROR "Compiler does not support all required C++11 features")
|
||||
+ endif()
|
||||
+
|
||||
# debugfull target
|
||||
set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C++ compiler during debugfull builds." FORCE)
|
||||
set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C compiler during debugfull builds." FORCE)
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
From de9bd9043e8878fc472ced1669bc7d49b07c2062 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ren=C3=A9=20J=2EV=2E=20Bertin?= <rjvbertin@gmail.com>
|
||||
Date: Mon, 3 Nov 2014 16:56:56 +0100
|
||||
Subject: [PATCH 10/30] prevent starting a QTimer with a negative interval
|
||||
Review: 120800
|
||||
|
||||
---
|
||||
server/src/collectionscheduler.cpp | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/src/collectionscheduler.cpp b/server/src/collectionscheduler.cpp
|
||||
index 8d4cd5c..9ba632f 100644
|
||||
--- a/server/src/collectionscheduler.cpp
|
||||
+++ b/server/src/collectionscheduler.cpp
|
||||
@@ -82,7 +82,8 @@ class PauseableTimer : public QTimer
|
||||
return;
|
||||
}
|
||||
|
||||
- start( interval() - ( mStarted.secsTo( mPaused ) * 1000 ) );
|
||||
+ const int remainder = interval() - ( mStarted.secsTo( mPaused ) * 1000 );
|
||||
+ start( qMax( 0, remainder ) );
|
||||
mPaused = QDateTime();
|
||||
// Update mStarted so that pause() can be called repeatedly
|
||||
mStarted = QDateTime::currentDateTime();
|
||||
--
|
||||
2.1.0
|
||||
|
||||
119
0011-Convert-some-qDebugs-to-akDebugs.patch
Normal file
119
0011-Convert-some-qDebugs-to-akDebugs.patch
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
From 1d79c645ffbd858517f07cee3143dc64fac7c3e9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 10 Nov 2014 11:51:45 +0100
|
||||
Subject: [PATCH 11/30] Convert some qDebugs to akDebugs
|
||||
|
||||
This should make Akonadi in release mode even less chatty.
|
||||
---
|
||||
server/src/handler/merge.cpp | 3 ++-
|
||||
server/src/handler/modify.cpp | 6 +++---
|
||||
server/src/handler/remove.cpp | 4 ++--
|
||||
server/src/search/searchmanager.cpp | 12 ++++++------
|
||||
4 files changed, 13 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/server/src/handler/merge.cpp b/server/src/handler/merge.cpp
|
||||
index fffe100..c26917d 100644
|
||||
--- a/server/src/handler/merge.cpp
|
||||
+++ b/server/src/handler/merge.cpp
|
||||
@@ -328,8 +328,9 @@ bool Merge::parseStream()
|
||||
}
|
||||
|
||||
} else {
|
||||
+ akDebug() << "Multiple merge candidates:";
|
||||
Q_FOREACH (const PimItem &item, result) {
|
||||
- qDebug() << item.id() << item.remoteId() << item.gid();
|
||||
+ akDebug() << "\t" << item.id() << item.remoteId() << item.gid();
|
||||
}
|
||||
// Nor GID or RID are guaranteed to be unique, so make sure we don't merge
|
||||
// something we don't want
|
||||
diff --git a/server/src/handler/modify.cpp b/server/src/handler/modify.cpp
|
||||
index 9671fb9..ad329db 100644
|
||||
--- a/server/src/handler/modify.cpp
|
||||
+++ b/server/src/handler/modify.cpp
|
||||
@@ -216,9 +216,9 @@ bool Modify::parseStream()
|
||||
|
||||
queryAttributes = attrs.join( QLatin1String( " " ) );
|
||||
|
||||
- qDebug() << collection.queryAttributes() << queryAttributes;
|
||||
- qDebug() << collection.queryCollections() << queryCollections;
|
||||
- qDebug() << collection.queryString() << queryString;
|
||||
+ akDebug() << collection.queryAttributes() << queryAttributes;
|
||||
+ akDebug() << collection.queryCollections() << queryCollections;
|
||||
+ akDebug() << collection.queryString() << queryString;
|
||||
|
||||
if ( collection.queryAttributes() != queryAttributes
|
||||
|| collection.queryCollections() != queryCollections
|
||||
diff --git a/server/src/handler/remove.cpp b/server/src/handler/remove.cpp
|
||||
index 090531f..daec5a0 100644
|
||||
--- a/server/src/handler/remove.cpp
|
||||
+++ b/server/src/handler/remove.cpp
|
||||
@@ -40,8 +40,8 @@ bool Remove::parseStream()
|
||||
{
|
||||
mScope.parseScope( m_streamParser );
|
||||
connection()->context()->parseContext( m_streamParser );
|
||||
- qDebug() << "Tag context:" << connection()->context()->tagId();
|
||||
- qDebug() << "Collection context: " << connection()->context()->collectionId();
|
||||
+ akDebug() << "Tag context:" << connection()->context()->tagId();
|
||||
+ akDebug() << "Collection context: " << connection()->context()->collectionId();
|
||||
|
||||
|
||||
SelectQueryBuilder<PimItem> qb;
|
||||
diff --git a/server/src/search/searchmanager.cpp b/server/src/search/searchmanager.cpp
|
||||
index 35e76e1..c821aa3 100644
|
||||
--- a/server/src/search/searchmanager.cpp
|
||||
+++ b/server/src/search/searchmanager.cpp
|
||||
@@ -159,7 +159,7 @@ void SearchManager::loadSearchPlugins()
|
||||
Q_FOREACH ( const QString &pluginDir, dirs ) {
|
||||
QDir dir( pluginDir + QLatin1String( "/akonadi" ) );
|
||||
const QStringList desktopFiles = dir.entryList( QStringList() << QLatin1String( "*.desktop" ), QDir::Files );
|
||||
- qDebug() << "SEARCH MANAGER: searching in " << pluginDir + QLatin1String( "/akonadi" ) << ":" << desktopFiles;
|
||||
+ akDebug() << "SEARCH MANAGER: searching in " << pluginDir + QLatin1String( "/akonadi" ) << ":" << desktopFiles;
|
||||
|
||||
Q_FOREACH ( const QString &desktopFileName, desktopFiles ) {
|
||||
QSettings desktop( pluginDir + QLatin1String( "/akonadi/" ) + desktopFileName, QSettings::IniFormat );
|
||||
@@ -170,13 +170,13 @@ void SearchManager::loadSearchPlugins()
|
||||
|
||||
const QString libraryName = desktop.value( QLatin1String( "X-Akonadi-Library" ) ).toString();
|
||||
if ( loadedPlugins.contains( libraryName ) ) {
|
||||
- qDebug() << "Already loaded one version of this plugin, skipping: " << libraryName;
|
||||
+ akDebug() << "Already loaded one version of this plugin, skipping: " << libraryName;
|
||||
continue;
|
||||
}
|
||||
// When search plugin override is active, ignore all plugins except for the override
|
||||
if ( !pluginOverride.isEmpty() ) {
|
||||
if ( libraryName != pluginOverride ) {
|
||||
- qDebug() << desktopFileName << "skipped because of AKONADI_OVERRIDE_SEARCHPLUGIN";
|
||||
+ akDebug() << desktopFileName << "skipped because of AKONADI_OVERRIDE_SEARCHPLUGIN";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ void SearchManager::loadSearchPlugins()
|
||||
continue;
|
||||
}
|
||||
|
||||
- qDebug() << "SearchManager: loaded search plugin" << libraryName;
|
||||
+ akDebug() << "SearchManager: loaded search plugin" << libraryName;
|
||||
mPlugins << plugin;
|
||||
loadedPlugins << libraryName;
|
||||
}
|
||||
@@ -390,7 +390,7 @@ void SearchManager::searchUpdateResultsAvailable( const QSet<qint64> &results )
|
||||
}
|
||||
}
|
||||
|
||||
- qDebug() << "Got" << newMatches.count() << "results, out of which" << existingMatches.count() << "are already in the collection";
|
||||
+ akDebug() << "Got" << newMatches.count() << "results, out of which" << existingMatches.count() << "are already in the collection";
|
||||
|
||||
newMatches = newMatches - existingMatches;
|
||||
|
||||
@@ -405,7 +405,7 @@ void SearchManager::searchUpdateResultsAvailable( const QSet<qint64> &results )
|
||||
Collection::addPimItem( collection.id(), id );
|
||||
}
|
||||
|
||||
- qDebug() << "Added" << newMatches.count();
|
||||
+ akDebug() << "Added" << newMatches.count();
|
||||
|
||||
if ( !existingTransaction && !DataStore::self()->commitTransaction() ) {
|
||||
akDebug() << "Failed to commit transaction";
|
||||
--
|
||||
2.1.0
|
||||
|
||||
604
0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch
Normal file
604
0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
From 63f49d233ca8a4fdd3e8937ea1c80d5e57a1cbdc Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Tue, 25 Nov 2014 20:16:41 +0100
|
||||
Subject: [PATCH 12/30] Optimize: Reduce the amount of allocations required to
|
||||
build a query.
|
||||
|
||||
The initial implementation of the QueryBuilder was quite naive, when
|
||||
you look at the amount of string allocations it does to build the
|
||||
final query we sent to the SQL server.
|
||||
|
||||
This was found with Linux perf (no, not even heaptrack!). It
|
||||
showed a huge number of cycles spent in malloc/free, all called
|
||||
eventually by the QueryBuilder.
|
||||
|
||||
This patch removes most of these allocations. It can further be
|
||||
improved in the future, I bet. Also, the amount of queries we create
|
||||
is pretty large. I guess using stored procedures or something similar
|
||||
might also help the performance. At least, we should try to "remember"
|
||||
some of our queries, and make it possible to reuse them in the
|
||||
functions that run often.
|
||||
|
||||
The added benchmark shows that the cost is not as big as I'd initially
|
||||
assumed. There are simply many more allocation occurrences in Akonadi
|
||||
currently. Still, I think it's worth it, as it also decreases the
|
||||
memory fragmentation and improves cache locality:
|
||||
|
||||
Before:
|
||||
RESULT : QueryBuilderTest::benchQueryBuilder():
|
||||
0.0115 msecs per iteration (total: 116, iterations: 10000)
|
||||
|
||||
113.10MB bytes allocated in total (ignoring deallocations)
|
||||
over 1203089 calls to allocation functions.
|
||||
peak heap memory consumption: 254.46KB
|
||||
|
||||
After:
|
||||
RESULT : QueryBuilderTest::benchQueryBuilder():
|
||||
0.0065 msecs per iteration (total: 66, iterations: 10000)
|
||||
|
||||
62.42MB bytes allocated in total (ignoring deallocations)
|
||||
over 343089 calls to allocation functions.
|
||||
peak heap memory consumption: 254.96KB
|
||||
|
||||
So before, we had approx. 60 allocations per query build in the
|
||||
benchmark (note that Qt for some reason executes the loop twice,
|
||||
so while the time is measured for 10k iterations, heaptrack will
|
||||
see 20k). With this patch applied, we only need ~20 allocations
|
||||
per query we build up.
|
||||
|
||||
The remaining allocations are the various append operations to
|
||||
the QList/QVectors mostly, as well as QueryBuilder::addAggregation.
|
||||
|
||||
REVIEW: 121247
|
||||
---
|
||||
server/src/storage/querybuilder.cpp | 210 ++++++++++++++++-------------
|
||||
server/src/storage/querybuilder.h | 14 +-
|
||||
server/tests/unittest/querybuildertest.cpp | 58 ++++++--
|
||||
server/tests/unittest/querybuildertest.h | 2 +
|
||||
4 files changed, 173 insertions(+), 111 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp
|
||||
index c079059..3017867 100644
|
||||
--- a/server/src/storage/querybuilder.cpp
|
||||
+++ b/server/src/storage/querybuilder.cpp
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
using namespace Akonadi::Server;
|
||||
|
||||
-static QString compareOperatorToString( Query::CompareOperator op )
|
||||
+static QLatin1String compareOperatorToString( Query::CompareOperator op )
|
||||
{
|
||||
switch ( op ) {
|
||||
case Query::Equals:
|
||||
@@ -58,10 +58,10 @@ static QString compareOperatorToString( Query::CompareOperator op )
|
||||
return QLatin1String( " LIKE " );
|
||||
}
|
||||
Q_ASSERT_X( false, "QueryBuilder::compareOperatorToString()", "Unknown compare operator." );
|
||||
- return QString();
|
||||
+ return QLatin1String("");
|
||||
}
|
||||
|
||||
-static QString logicOperatorToString( Query::LogicOperator op )
|
||||
+static QLatin1String logicOperatorToString( Query::LogicOperator op )
|
||||
{
|
||||
switch ( op ) {
|
||||
case Query::And:
|
||||
@@ -70,10 +70,10 @@ static QString logicOperatorToString( Query::LogicOperator op )
|
||||
return QLatin1String( " OR " );
|
||||
}
|
||||
Q_ASSERT_X( false, "QueryBuilder::logicOperatorToString()", "Unknown logic operator." );
|
||||
- return QString();
|
||||
+ return QLatin1String("");
|
||||
}
|
||||
|
||||
-static QString sortOrderToString( Query::SortOrder order )
|
||||
+static QLatin1String sortOrderToString( Query::SortOrder order )
|
||||
{
|
||||
switch ( order ) {
|
||||
case Query::Ascending:
|
||||
@@ -82,7 +82,17 @@ static QString sortOrderToString( Query::SortOrder order )
|
||||
return QLatin1String( " DESC" );
|
||||
}
|
||||
Q_ASSERT_X( false, "QueryBuilder::sortOrderToString()", "Unknown sort order." );
|
||||
- return QString();
|
||||
+ return QLatin1String("");
|
||||
+}
|
||||
+
|
||||
+static void appendJoined( QString *statement, const QStringList &strings, const QLatin1String &glue = QLatin1String( ", " ) )
|
||||
+{
|
||||
+ for (int i = 0, c = strings.size(); i < c; ++i) {
|
||||
+ *statement += strings.at( i );
|
||||
+ if (i + 1 < c) {
|
||||
+ *statement += glue;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
QueryBuilder::QueryBuilder( const QString &table, QueryBuilder::QueryType type )
|
||||
@@ -94,10 +104,12 @@ QueryBuilder::QueryBuilder( const QString &table, QueryBuilder::QueryType type )
|
||||
, mDatabaseType( DbType::Unknown )
|
||||
#endif
|
||||
, mType( type )
|
||||
- , mIdentificationColumn( QLatin1String( "id" ) )
|
||||
+ , mIdentificationColumn( )
|
||||
, mLimit( -1 )
|
||||
, mDistinct( false )
|
||||
{
|
||||
+ static const QString defaultIdColumn = QLatin1String( "id" );
|
||||
+ mIdentificationColumn = defaultIdColumn;
|
||||
}
|
||||
|
||||
void QueryBuilder::setDatabaseType( DbType::Type type )
|
||||
@@ -175,60 +187,65 @@ void QueryBuilder::sqliteAdaptUpdateJoin( Query::Condition &condition )
|
||||
qb.addCondition( joinCondition.second );
|
||||
|
||||
// Convert the subquery to string
|
||||
- condition.mColumn = QLatin1String( "( " ) + qb.buildQuery() + QLatin1String( " )" );
|
||||
+ condition.mColumn.reserve(1024);
|
||||
+ condition.mColumn.resize(0);
|
||||
+ condition.mColumn += QLatin1String( "( " );
|
||||
+ qb.buildQuery(&condition.mColumn);
|
||||
+ condition.mColumn += QLatin1String( " )" );
|
||||
}
|
||||
|
||||
-
|
||||
-QString QueryBuilder::buildQuery()
|
||||
+void QueryBuilder::buildQuery(QString *statement)
|
||||
{
|
||||
- QString statement;
|
||||
-
|
||||
// we add the ON conditions of Inner Joins in a Update query here
|
||||
// but don't want to change the mRootCondition on each exec().
|
||||
Query::Condition whereCondition = mRootCondition[WhereCondition];
|
||||
|
||||
switch ( mType ) {
|
||||
case Select:
|
||||
- statement += QLatin1String( "SELECT " );
|
||||
+ *statement += QLatin1String( "SELECT " );
|
||||
if ( mDistinct ) {
|
||||
- statement += QLatin1String( "DISTINCT " );
|
||||
+ *statement += QLatin1String( "DISTINCT " );
|
||||
}
|
||||
Q_ASSERT_X( mColumns.count() > 0, "QueryBuilder::exec()", "No columns specified" );
|
||||
- statement += mColumns.join( QLatin1String( ", " ) );
|
||||
- statement += QLatin1String( " FROM " );
|
||||
- statement += mTable;
|
||||
+ appendJoined( statement, mColumns );
|
||||
+ *statement += QLatin1String( " FROM " );
|
||||
+ *statement += mTable;
|
||||
Q_FOREACH ( const QString &joinedTable, mJoinedTables ) {
|
||||
const QPair<JoinType, Query::Condition> &join = mJoins.value( joinedTable );
|
||||
switch ( join.first ) {
|
||||
case LeftJoin:
|
||||
- statement += QLatin1String( " LEFT JOIN " );
|
||||
+ *statement += QLatin1String( " LEFT JOIN " );
|
||||
break;
|
||||
case InnerJoin:
|
||||
- statement += QLatin1String( " INNER JOIN " );
|
||||
+ *statement += QLatin1String( " INNER JOIN " );
|
||||
break;
|
||||
}
|
||||
- statement += joinedTable;
|
||||
- statement += QLatin1String( " ON " );
|
||||
- statement += buildWhereCondition( join.second );
|
||||
+ *statement += joinedTable;
|
||||
+ *statement += QLatin1String( " ON " );
|
||||
+ buildWhereCondition( statement, join.second );
|
||||
}
|
||||
break;
|
||||
case Insert:
|
||||
{
|
||||
- statement += QLatin1String( "INSERT INTO " );
|
||||
- statement += mTable;
|
||||
- statement += QLatin1String( " (" );
|
||||
- typedef QPair<QString,QVariant> StringVariantPair;
|
||||
- QStringList cols, vals;
|
||||
- Q_FOREACH ( const StringVariantPair &p, mColumnValues ) {
|
||||
- cols.append( p.first );
|
||||
- vals.append( bindValue( p.second ) );
|
||||
+ *statement += QLatin1String( "INSERT INTO " );
|
||||
+ *statement += mTable;
|
||||
+ *statement += QLatin1String( " (" );
|
||||
+ for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
|
||||
+ *statement += mColumnValues.at(i).first;
|
||||
+ if (i + 1 < c) {
|
||||
+ *statement += QLatin1String( ", " );
|
||||
+ }
|
||||
+ }
|
||||
+ *statement += QLatin1String( ") VALUES (" );
|
||||
+ for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
|
||||
+ bindValue( statement, mColumnValues.at(i).second );
|
||||
+ if (i + 1 < c) {
|
||||
+ *statement += QLatin1String( ", " );
|
||||
+ }
|
||||
}
|
||||
- statement += cols.join( QLatin1String( ", " ) );
|
||||
- statement += QLatin1String( ") VALUES (" );
|
||||
- statement += vals.join( QLatin1String( ", " ) );
|
||||
- statement += QLatin1Char( ')' );
|
||||
+ *statement += QLatin1Char( ')' );
|
||||
if ( mDatabaseType == DbType::PostgreSQL && !mIdentificationColumn.isEmpty() ) {
|
||||
- statement += QLatin1String( " RETURNING " ) + mIdentificationColumn;
|
||||
+ *statement += QLatin1String( " RETURNING " ) + mIdentificationColumn;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -246,78 +263,75 @@ QString QueryBuilder::buildQuery()
|
||||
sqliteAdaptUpdateJoin( whereCondition );
|
||||
}
|
||||
|
||||
- statement += QLatin1String( "UPDATE " );
|
||||
- statement += mTable;
|
||||
+ *statement += QLatin1String( "UPDATE " );
|
||||
+ *statement += mTable;
|
||||
|
||||
if ( mDatabaseType == DbType::MySQL && !mJoinedTables.isEmpty() ) {
|
||||
// for mysql we list all tables directly
|
||||
- statement += QLatin1String( ", " );
|
||||
- statement += mJoinedTables.join( QLatin1String( ", " ) );
|
||||
+ *statement += QLatin1String( ", " );
|
||||
+ appendJoined( statement, mJoinedTables );
|
||||
}
|
||||
|
||||
- statement += QLatin1String( " SET " );
|
||||
+ *statement += QLatin1String( " SET " );
|
||||
Q_ASSERT_X( mColumnValues.count() >= 1, "QueryBuilder::exec()", "At least one column needs to be changed" );
|
||||
- typedef QPair<QString,QVariant> StringVariantPair;
|
||||
- QStringList updStmts;
|
||||
- Q_FOREACH ( const StringVariantPair &p, mColumnValues ) {
|
||||
- QString updStmt = p.first;
|
||||
- updStmt += QLatin1String( " = " );
|
||||
- updStmt += bindValue( p.second );
|
||||
- updStmts << updStmt;
|
||||
+ for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
|
||||
+ const QPair<QString, QVariant>& p = mColumnValues.at( i );
|
||||
+ *statement += p.first;
|
||||
+ *statement += QLatin1String( " = " );
|
||||
+ bindValue( statement, p.second );
|
||||
+ if (i + 1 < c) {
|
||||
+ *statement += QLatin1String( ", " );
|
||||
+ }
|
||||
}
|
||||
- statement += updStmts.join( QLatin1String( ", " ) );
|
||||
|
||||
if ( mDatabaseType == DbType::PostgreSQL && !mJoinedTables.isEmpty() ) {
|
||||
// PSQL have this syntax
|
||||
// FROM t1 JOIN t2 JOIN ...
|
||||
- statement += QLatin1String( " FROM " );
|
||||
- statement += mJoinedTables.join( QLatin1String( " JOIN " ) );
|
||||
+ *statement += QLatin1String( " FROM " );
|
||||
+ appendJoined( statement, mJoinedTables, QLatin1String( " JOIN " ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Delete:
|
||||
- statement += QLatin1String( "DELETE FROM " );
|
||||
- statement += mTable;
|
||||
+ *statement += QLatin1String( "DELETE FROM " );
|
||||
+ *statement += mTable;
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X( false, "QueryBuilder::exec()", "Unknown enum value" );
|
||||
}
|
||||
|
||||
if ( !whereCondition.isEmpty() ) {
|
||||
- statement += QLatin1String( " WHERE " );
|
||||
- statement += buildWhereCondition( whereCondition );
|
||||
+ *statement += QLatin1String( " WHERE " );
|
||||
+ buildWhereCondition( statement, whereCondition );
|
||||
}
|
||||
|
||||
if ( !mGroupColumns.isEmpty() ) {
|
||||
- statement += QLatin1String( " GROUP BY " );
|
||||
- statement += mGroupColumns.join( QLatin1String( ", " ) );
|
||||
+ *statement += QLatin1String( " GROUP BY " );
|
||||
+ appendJoined( statement, mGroupColumns );
|
||||
}
|
||||
|
||||
if ( !mRootCondition[HavingCondition].isEmpty() ) {
|
||||
- statement += QLatin1String( " HAVING " );
|
||||
- statement += buildWhereCondition( mRootCondition[HavingCondition] );
|
||||
+ *statement += QLatin1String( " HAVING " );
|
||||
+ buildWhereCondition( statement, mRootCondition[HavingCondition] );
|
||||
}
|
||||
|
||||
if ( !mSortColumns.isEmpty() ) {
|
||||
Q_ASSERT_X( mType == Select, "QueryBuilder::exec()", "Order statements are only valid for SELECT queries" );
|
||||
- QStringList orderStmts;
|
||||
- typedef QPair<QString, Query::SortOrder> SortColumnInfo;
|
||||
- Q_FOREACH ( const SortColumnInfo &order, mSortColumns ) {
|
||||
- QString orderStmt;
|
||||
- orderStmt += order.first;
|
||||
- orderStmt += sortOrderToString( order.second );
|
||||
- orderStmts << orderStmt;
|
||||
+ *statement += QLatin1String( " ORDER BY " );
|
||||
+ for (int i = 0, c = mSortColumns.size(); i < c; ++i) {
|
||||
+ const QPair<QString, Query::SortOrder>& order = mSortColumns.at( i );
|
||||
+ *statement += order.first;
|
||||
+ *statement += sortOrderToString( order.second );
|
||||
+ if (i + 1 < c) {
|
||||
+ *statement += QLatin1String( ", " );
|
||||
+ }
|
||||
}
|
||||
- statement += QLatin1String( " ORDER BY " );
|
||||
- statement += orderStmts.join( QLatin1String( ", " ) );
|
||||
}
|
||||
|
||||
if ( mLimit > 0 ) {
|
||||
- statement += QLatin1Literal( " LIMIT " ) + QString::number( mLimit );
|
||||
+ *statement += QLatin1Literal( " LIMIT " ) + QString::number( mLimit );
|
||||
}
|
||||
-
|
||||
- return statement;
|
||||
}
|
||||
|
||||
bool QueryBuilder::retryLastTransaction( bool rollback )
|
||||
@@ -334,7 +348,9 @@ bool QueryBuilder::retryLastTransaction( bool rollback )
|
||||
|
||||
bool QueryBuilder::exec()
|
||||
{
|
||||
- const QString statement = buildQuery();
|
||||
+ QString statement;
|
||||
+ statement.reserve(1024);
|
||||
+ buildQuery(&statement);
|
||||
|
||||
#ifndef QUERYBUILDER_UNITTEST
|
||||
if ( QueryCache::contains( statement ) ) {
|
||||
@@ -443,52 +459,54 @@ void QueryBuilder::addColumn( const QString &col )
|
||||
|
||||
void QueryBuilder::addAggregation( const QString &col, const QString &aggregate )
|
||||
{
|
||||
- QString s( aggregate );
|
||||
- s += QLatin1Char( '(' );
|
||||
- s += col;
|
||||
- s += QLatin1Char( ')' );
|
||||
- mColumns.append( s );
|
||||
+ mColumns.append( aggregate + QLatin1Char( '(' ) + col + QLatin1Char( ')' ) );
|
||||
}
|
||||
|
||||
-QString QueryBuilder::bindValue( const QVariant &value )
|
||||
+void QueryBuilder::bindValue( QString *query, const QVariant &value )
|
||||
{
|
||||
mBindValues << value;
|
||||
- return QLatin1Char( ':' ) + QString::number( mBindValues.count() - 1 );
|
||||
+ *query += QLatin1Char( ':' ) + QString::number( mBindValues.count() - 1 );
|
||||
}
|
||||
|
||||
-QString QueryBuilder::buildWhereCondition( const Query::Condition &cond )
|
||||
+void QueryBuilder::buildWhereCondition( QString *query, const Query::Condition &cond )
|
||||
{
|
||||
if ( !cond.isEmpty() ) {
|
||||
- QStringList conds;
|
||||
- Q_FOREACH ( const Query::Condition &c, cond.subConditions() ) {
|
||||
- conds << buildWhereCondition( c );
|
||||
+ *query += QLatin1String( "( " );
|
||||
+ const QLatin1String glue = logicOperatorToString( cond.mCombineOp );
|
||||
+ const Query::Condition::List& subConditions = cond.subConditions();
|
||||
+ for (int i = 0, c = subConditions.size(); i < c; ++i) {
|
||||
+ buildWhereCondition(query, subConditions.at(i));
|
||||
+ if (i + 1 < c) {
|
||||
+ *query += glue;
|
||||
+ }
|
||||
}
|
||||
- return QLatin1String( "( " ) + conds.join( logicOperatorToString( cond.mCombineOp ) ) + QLatin1String( " )" );
|
||||
+ *query += QLatin1String( " )" );
|
||||
} else {
|
||||
- QString stmt = cond.mColumn;
|
||||
- stmt += compareOperatorToString( cond.mCompareOp );
|
||||
+ *query += cond.mColumn;
|
||||
+ *query += compareOperatorToString( cond.mCompareOp );
|
||||
if ( cond.mComparedColumn.isEmpty() ) {
|
||||
if ( cond.mComparedValue.isValid() ) {
|
||||
if ( cond.mComparedValue.canConvert( QVariant::List ) ) {
|
||||
- stmt += QLatin1String( "( " );
|
||||
- QStringList entries;
|
||||
- Q_ASSERT_X( !cond.mComparedValue.toList().isEmpty(),
|
||||
+ *query += QLatin1String( "( " );
|
||||
+ const QVariantList& entries = cond.mComparedValue.toList();
|
||||
+ Q_ASSERT_X( !entries.isEmpty(),
|
||||
"QueryBuilder::buildWhereCondition()", "No values given for IN condition." );
|
||||
- Q_FOREACH ( const QVariant &entry, cond.mComparedValue.toList() ) {
|
||||
- entries << bindValue( entry );
|
||||
+ for (int i = 0, c = entries.size(); i < c; ++i) {
|
||||
+ bindValue( query, entries.at(i) );
|
||||
+ if (i + 1 < c) {
|
||||
+ *query += QLatin1String( ", " );
|
||||
+ }
|
||||
}
|
||||
- stmt += entries.join( QLatin1String( ", " ) );
|
||||
- stmt += QLatin1String( " )" );
|
||||
+ *query += QLatin1String( " )" );
|
||||
} else {
|
||||
- stmt += bindValue( cond.mComparedValue );
|
||||
+ bindValue( query, cond.mComparedValue );
|
||||
}
|
||||
} else {
|
||||
- stmt += QLatin1String( "NULL" );
|
||||
+ *query += QLatin1String( "NULL" );
|
||||
}
|
||||
} else {
|
||||
- stmt += cond.mComparedColumn;
|
||||
+ *query += cond.mComparedColumn;
|
||||
}
|
||||
- return stmt;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h
|
||||
index b380f93..df7c362 100644
|
||||
--- a/server/src/storage/querybuilder.h
|
||||
+++ b/server/src/storage/querybuilder.h
|
||||
@@ -70,7 +70,9 @@ class QueryBuilder
|
||||
WhereCondition,
|
||||
/// add condition to HAVING part of the query
|
||||
/// NOTE: only supported for SELECT queries
|
||||
- HavingCondition
|
||||
+ HavingCondition,
|
||||
+
|
||||
+ NUM_CONDITIONS
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -234,9 +236,9 @@ class QueryBuilder
|
||||
qint64 insertId();
|
||||
|
||||
private:
|
||||
- QString buildQuery();
|
||||
- QString bindValue( const QVariant &value );
|
||||
- QString buildWhereCondition( const Query::Condition &cond );
|
||||
+ void buildQuery( QString *query );
|
||||
+ void bindValue( QString *query, const QVariant &value );
|
||||
+ void buildWhereCondition( QString *query, const Query::Condition &cond );
|
||||
|
||||
/**
|
||||
* SQLite does not support JOINs with UPDATE, so we have to convert it into
|
||||
@@ -249,11 +251,11 @@ class QueryBuilder
|
||||
private:
|
||||
QString mTable;
|
||||
DbType::Type mDatabaseType;
|
||||
- QHash<ConditionType, Query::Condition> mRootCondition;
|
||||
+ Query::Condition mRootCondition[NUM_CONDITIONS];
|
||||
QSqlQuery mQuery;
|
||||
QueryType mType;
|
||||
QStringList mColumns;
|
||||
- QList<QVariant> mBindValues;
|
||||
+ QVector<QVariant> mBindValues;
|
||||
QVector<QPair<QString, Query::SortOrder> > mSortColumns;
|
||||
QStringList mGroupColumns;
|
||||
QVector<QPair<QString, QVariant> > mColumnValues;
|
||||
diff --git a/server/tests/unittest/querybuildertest.cpp b/server/tests/unittest/querybuildertest.cpp
|
||||
index 0aba8a1..92df2a2 100644
|
||||
--- a/server/tests/unittest/querybuildertest.cpp
|
||||
+++ b/server/tests/unittest/querybuildertest.cpp
|
||||
@@ -29,26 +29,29 @@
|
||||
|
||||
QTEST_MAIN( QueryBuilderTest )
|
||||
|
||||
+Q_DECLARE_METATYPE(QVector<QVariant>)
|
||||
+
|
||||
using namespace Akonadi::Server;
|
||||
|
||||
void QueryBuilderTest::testQueryBuilder_data()
|
||||
{
|
||||
+ qRegisterMetaType<QVector<QVariant> >();
|
||||
mBuilders.clear();
|
||||
QTest::addColumn<int>( "qbId" );
|
||||
QTest::addColumn<QString>( "sql" );
|
||||
- QTest::addColumn<QList<QVariant> >( "bindValues" );
|
||||
+ QTest::addColumn<QVector<QVariant> >( "bindValues" );
|
||||
|
||||
QueryBuilder qb( "table", QueryBuilder::Select );
|
||||
qb.addColumn( "col1" );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QVector<QVariant>();
|
||||
|
||||
qb.addColumn( "col2" );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QVector<QVariant>();
|
||||
|
||||
qb.addValueCondition( "col1", Query::Equals, QVariant( 5 ) );
|
||||
- QList<QVariant> bindVals;
|
||||
+ QVector<QVariant> bindVals;
|
||||
bindVals << QVariant( 5 );
|
||||
mBuilders << qb;
|
||||
QTest::newRow( "single where" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table WHERE ( col1 = :0 )" ) << bindVals;
|
||||
@@ -71,17 +74,17 @@ void QueryBuilderTest::testQueryBuilder_data()
|
||||
qb = QueryBuilder( "table" );
|
||||
qb.addAggregation( "col1", "count" );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "single aggregation" ) << mBuilders.count() << QString( "SELECT count(col1) FROM table" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "single aggregation" ) << mBuilders.count() << QString( "SELECT count(col1) FROM table" ) << QVector<QVariant>();
|
||||
|
||||
qb = QueryBuilder( "table" );
|
||||
qb.addColumn( "col1" );
|
||||
qb.addSortColumn( "col1" );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "single order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "single order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC" ) << QVector<QVariant>();
|
||||
|
||||
qb.addSortColumn( "col2", Query::Descending );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "multiple order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC, col2 DESC" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "multiple order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC, col2 DESC" ) << QVector<QVariant>();
|
||||
|
||||
qb = QueryBuilder( "table" );
|
||||
qb.addColumn( "col1" );
|
||||
@@ -98,7 +101,7 @@ void QueryBuilderTest::testQueryBuilder_data()
|
||||
qb.addColumn( "col1" );
|
||||
qb.setLimit( 1 );
|
||||
mBuilders << qb;
|
||||
- QTest::newRow( "SELECT with LIMIT" ) << mBuilders.count() << QString( "SELECT col1 FROM table LIMIT 1" ) << QList<QVariant>();
|
||||
+ QTest::newRow( "SELECT with LIMIT" ) << mBuilders.count() << QString( "SELECT col1 FROM table LIMIT 1" ) << QVector<QVariant>();
|
||||
|
||||
qb = QueryBuilder( "table", QueryBuilder::Update );
|
||||
qb.setColumnValue( "col1", QString( "bla" ) );
|
||||
@@ -263,7 +266,7 @@ void QueryBuilderTest::testQueryBuilder()
|
||||
{
|
||||
QFETCH( int, qbId );
|
||||
QFETCH( QString, sql );
|
||||
- QFETCH( QList<QVariant>, bindValues );
|
||||
+ QFETCH( QVector<QVariant>, bindValues );
|
||||
|
||||
--qbId;
|
||||
|
||||
@@ -271,3 +274,40 @@ void QueryBuilderTest::testQueryBuilder()
|
||||
QCOMPARE( mBuilders[qbId].mStatement, sql );
|
||||
QCOMPARE( mBuilders[qbId].mBindValues, bindValues );
|
||||
}
|
||||
+
|
||||
+void QueryBuilderTest::benchQueryBuilder()
|
||||
+{
|
||||
+ const QString table1 = QLatin1String("Table1");
|
||||
+ const QString table2 = QLatin1String("Table2");
|
||||
+ const QString table3 = QLatin1String("Table3");
|
||||
+ const QString table1_id = QLatin1String("Table1.id");
|
||||
+ const QString table2_id = QLatin1String("Table2.id");
|
||||
+ const QString table3_id = QLatin1String("Table3.id");
|
||||
+ const QString aggregate = QLatin1String("COUNT");
|
||||
+ const QVariant value = QVariant::fromValue(QString("asdf"));
|
||||
+
|
||||
+ const QStringList columns = QStringList()
|
||||
+ << QLatin1String("Table1.id")
|
||||
+ << QLatin1String("Table1.fooAsdf")
|
||||
+ << QLatin1String("Table2.barLala")
|
||||
+ << QLatin1String("Table3.xyzFsd");
|
||||
+
|
||||
+ bool executed = true;
|
||||
+
|
||||
+ QBENCHMARK {
|
||||
+ QueryBuilder builder( table1, QueryBuilder::Select );
|
||||
+ builder.setDatabaseType( DbType::MySQL );
|
||||
+ builder.addColumns( columns );
|
||||
+ builder.addJoin( QueryBuilder::InnerJoin, table2, table2_id, table1_id );
|
||||
+ builder.addJoin( QueryBuilder::LeftJoin, table3, table1_id, table3_id );
|
||||
+ builder.addAggregation( columns.first(), aggregate );
|
||||
+ builder.addColumnCondition( columns.at(1), Query::LessOrEqual, columns.last() );
|
||||
+ builder.addValueCondition( columns.at(3), Query::Equals, value );
|
||||
+ builder.addSortColumn( columns.at(2) );
|
||||
+ builder.setLimit( 10 );
|
||||
+ builder.addGroupColumn( columns.at(3) );
|
||||
+ executed = executed && builder.exec();
|
||||
+ }
|
||||
+
|
||||
+ QVERIFY(executed);
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/server/tests/unittest/querybuildertest.h b/server/tests/unittest/querybuildertest.h
|
||||
index 3bb6b22..1bca2cc 100644
|
||||
--- a/server/tests/unittest/querybuildertest.h
|
||||
+++ b/server/tests/unittest/querybuildertest.h
|
||||
@@ -37,6 +37,8 @@ class QueryBuilderTest : public QObject
|
||||
void testQueryBuilder_data();
|
||||
void testQueryBuilder();
|
||||
|
||||
+ void benchQueryBuilder();
|
||||
+
|
||||
private:
|
||||
QList< Akonadi::Server::QueryBuilder > mBuilders;
|
||||
};
|
||||
--
|
||||
2.1.0
|
||||
|
||||
176
0013-Intern-entity-strings-for-table-and-column-names.patch
Normal file
176
0013-Intern-entity-strings-for-table-and-column-names.patch
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
From a04809a44c235bed854adc3bd49ca75b9673bf1f Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Wed, 26 Nov 2014 13:20:05 +0100
|
||||
Subject: [PATCH 13/30] Intern entity strings for table and column names.
|
||||
|
||||
This should drastically cut down on the amount of allocations done
|
||||
by the AkonadiServer. Currently, the getters will do the conversion
|
||||
from QLatin1String to QString on every call. By reusing the data
|
||||
via a function-local static const QString object, we can eliminate
|
||||
all of these allocations and increase the cache locality as well.
|
||||
|
||||
REVIEW: 121255
|
||||
---
|
||||
server/src/storage/entities-source.xsl | 56 +++++++++++++++++++++-------------
|
||||
server/src/storage/entities.xsl | 4 +--
|
||||
2 files changed, 36 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entities-source.xsl b/server/src/storage/entities-source.xsl
|
||||
index 174cf4f..7090c31 100644
|
||||
--- a/server/src/storage/entities-source.xsl
|
||||
+++ b/server/src/storage/entities-source.xsl
|
||||
@@ -214,36 +214,41 @@ void <xsl:value-of select="$className"/>::<xsl:call-template name="setter-signat
|
||||
// SQL table information
|
||||
<xsl:text>QString </xsl:text><xsl:value-of select="$className"/>::tableName()
|
||||
{
|
||||
- return QLatin1String( "<xsl:value-of select="$tableName"/>" );
|
||||
+ static const QString tableName = QLatin1String( "<xsl:value-of select="$tableName"/>" );
|
||||
+ return tableName;
|
||||
}
|
||||
|
||||
QStringList <xsl:value-of select="$className"/>::columnNames()
|
||||
{
|
||||
- QStringList rv;
|
||||
+ static const QStringList columns = QStringList()
|
||||
<xsl:for-each select="column">
|
||||
- rv.append( QLatin1String( "<xsl:value-of select="@name"/>" ) );
|
||||
+ << <xsl:value-of select="@name"/>Column()
|
||||
</xsl:for-each>
|
||||
- return rv;
|
||||
+ ;
|
||||
+ return columns;
|
||||
}
|
||||
|
||||
QStringList <xsl:value-of select="$className"/>::fullColumnNames()
|
||||
{
|
||||
- QStringList rv;
|
||||
+ static const QStringList columns = QStringList()
|
||||
<xsl:for-each select="column">
|
||||
- rv.append( QLatin1String( "<xsl:value-of select="$tableName"/>.<xsl:value-of select="@name"/>" ) );
|
||||
+ << <xsl:value-of select="@name"/>FullColumnName()
|
||||
</xsl:for-each>
|
||||
- return rv;
|
||||
+ ;
|
||||
+ return columns;
|
||||
}
|
||||
|
||||
<xsl:for-each select="column">
|
||||
QString <xsl:value-of select="$className"/>::<xsl:value-of select="@name"/>Column()
|
||||
{
|
||||
- return QLatin1String( "<xsl:value-of select="@name"/>" );
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="@name"/>" );
|
||||
+ return column;
|
||||
}
|
||||
|
||||
QString <xsl:value-of select="$className"/>::<xsl:value-of select="@name"/>FullColumnName()
|
||||
{
|
||||
- return tableName() + QLatin1String( ".<xsl:value-of select="@name"/>" );
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="$tableName"/>.<xsl:value-of select="@name"/>" );
|
||||
+ return column;
|
||||
}
|
||||
</xsl:for-each>
|
||||
|
||||
@@ -399,7 +404,6 @@ QVector<<xsl:value-of select="@table"/>> <xsl:value-of select="$className"
|
||||
<xsl:variable name="relationName"><xsl:value-of select="@table1"/><xsl:value-of select="@table2"/>Relation</xsl:variable>
|
||||
<xsl:variable name="rightSideClass"><xsl:value-of select="@table2"/></xsl:variable>
|
||||
<xsl:variable name="rightSideEntity"><xsl:value-of select="@table2"/></xsl:variable>
|
||||
-<xsl:variable name="rightSideTable"><xsl:value-of select="@table2"/>Table</xsl:variable>
|
||||
|
||||
// data retrieval for n:m relations
|
||||
QVector<<xsl:value-of select="$rightSideClass"/>> <xsl:value-of select="$className"/>::<xsl:value-of select="concat(translate(substring(@table2,1,1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), substring(@table2,2))"/>s() const
|
||||
@@ -408,14 +412,17 @@ QVector<<xsl:value-of select="$rightSideClass"/>> <xsl:value-of select="$c
|
||||
if ( !db.isOpen() )
|
||||
return QVector<<xsl:value-of select="$rightSideClass"/>>();
|
||||
|
||||
- QueryBuilder qb( QLatin1String("<xsl:value-of select="$rightSideTable"/>"), QueryBuilder::Select );
|
||||
+ QueryBuilder qb( <xsl:value-of select="$rightSideClass"/>::tableName(), QueryBuilder::Select );
|
||||
+ static const QStringList columns = QStringList()
|
||||
<xsl:for-each select="/database/table[@name = $rightSideEntity]/column">
|
||||
- qb.addColumn( QLatin1String("<xsl:value-of select="$rightSideTable"/>.<xsl:value-of select="@name"/>" ) );
|
||||
+ << <xsl:value-of select="$rightSideClass"/>::<xsl:value-of select="@name"/>FullColumnName()
|
||||
</xsl:for-each>
|
||||
- qb.addJoin( QueryBuilder::InnerJoin, QLatin1String("<xsl:value-of select="$relationName"/>"),
|
||||
- QLatin1String("<xsl:value-of select="$relationName"/>.<xsl:value-of select="@table2"/>_<xsl:value-of select="@column2"/>"),
|
||||
- QLatin1String("<xsl:value-of select="$rightSideTable"/>.<xsl:value-of select="@column2"/>") );
|
||||
- qb.addValueCondition( QLatin1String("<xsl:value-of select="$relationName"/>.<xsl:value-of select="@table1"/>_<xsl:value-of select="@column1"/>"), Query::Equals, id() );
|
||||
+ ;
|
||||
+ qb.addColumns(columns);
|
||||
+ qb.addJoin( QueryBuilder::InnerJoin, <xsl:value-of select="$relationName"/>::tableName(),
|
||||
+ <xsl:value-of select="$relationName"/>::rightFullColumnName(),
|
||||
+ <xsl:value-of select="$rightSideClass"/>::<xsl:value-of select="@column2"/>FullColumnName() );
|
||||
+ qb.addValueCondition( <xsl:value-of select="$relationName"/>::leftFullColumnName(), Query::Equals, id() );
|
||||
|
||||
if ( !qb.exec() ) {
|
||||
akDebug() << "Error during selection of records from table <xsl:value-of select="@table1"/><xsl:value-of select="@table2"/>Relation"
|
||||
@@ -546,7 +553,7 @@ bool <xsl:value-of select="$className"/>::update()
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
- qb.addValueCondition( QLatin1String("id"), Query::Equals, id() );
|
||||
+ qb.addValueCondition( idColumn(), Query::Equals, id() );
|
||||
</xsl:if>
|
||||
|
||||
if ( !qb.exec() ) {
|
||||
@@ -622,27 +629,32 @@ void <xsl:value-of select="$className"/>::enableCache( bool enable )
|
||||
// SQL table information
|
||||
QString <xsl:value-of select="$className"/>::tableName()
|
||||
{
|
||||
- return QLatin1String( "<xsl:value-of select="$tableName"/>" );
|
||||
+ static const QString table = QLatin1String( "<xsl:value-of select="$tableName"/>" );
|
||||
+ return table;
|
||||
}
|
||||
|
||||
QString <xsl:value-of select="$className"/>::leftColumn()
|
||||
{
|
||||
- return QLatin1String( "<xsl:value-of select="@table1"/>_<xsl:value-of select="@column1"/>" );
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="@table1"/>_<xsl:value-of select="@column1"/>" );
|
||||
+ return column;
|
||||
}
|
||||
|
||||
QString <xsl:value-of select="$className"/>::leftFullColumnName()
|
||||
{
|
||||
- return tableName() + QLatin1String( "." ) + leftColumn();
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="$tableName"/>.<xsl:value-of select="@table1"/>_<xsl:value-of select="@column1"/>" );
|
||||
+ return column;
|
||||
}
|
||||
|
||||
QString <xsl:value-of select="$className"/>::rightColumn()
|
||||
{
|
||||
- return QLatin1String( "<xsl:value-of select="@table2"/>_<xsl:value-of select="@column2"/>" );
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="@table2"/>_<xsl:value-of select="@column2"/>" );
|
||||
+ return column;
|
||||
}
|
||||
|
||||
QString <xsl:value-of select="$className"/>::rightFullColumnName()
|
||||
{
|
||||
- return tableName() + QLatin1String( "." ) + rightColumn();
|
||||
+ static const QString column = QLatin1String( "<xsl:value-of select="$tableName"/>.<xsl:value-of select="@table2"/>_<xsl:value-of select="@column2"/>" );
|
||||
+ return column;
|
||||
}
|
||||
</xsl:template>
|
||||
|
||||
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
|
||||
index 033e292..8b0ed03 100644
|
||||
--- a/server/src/storage/entities.xsl
|
||||
+++ b/server/src/storage/entities.xsl
|
||||
@@ -114,7 +114,7 @@ using namespace Akonadi::Server;
|
||||
|
||||
QVector<QString> Akonadi::Server::allDatabaseTables()
|
||||
{
|
||||
- static QVector<QString> allTables = QVector<QString>()
|
||||
+ static const QVector<QString> allTables = QVector<QString>()
|
||||
<xsl:for-each select="database/table">
|
||||
<< QLatin1String( "<xsl:value-of select="@name"/>Table" )
|
||||
</xsl:for-each>
|
||||
@@ -182,7 +182,7 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
|
||||
QueryBuilder qb( tableName(), QueryBuilder::Select );
|
||||
qb.addColumns( columnNames() );
|
||||
- qb.addValueCondition( QLatin1String("<xsl:value-of select="$key"/>"), Query::Equals, <xsl:value-of select="$key"/> );
|
||||
+ qb.addValueCondition( <xsl:value-of select="$key"/>Column(), Query::Equals, <xsl:value-of select="$key"/> );
|
||||
if ( !qb.exec() ) {
|
||||
akDebug() << "Error during selection of record with <xsl:value-of select="$key"/>"
|
||||
<< <xsl:value-of select="$key"/> << "from table" << tableName()
|
||||
--
|
||||
2.1.0
|
||||
|
||||
38
0014-No-semicolon-after-Q_DECLARE_METATYPE.patch
Normal file
38
0014-No-semicolon-after-Q_DECLARE_METATYPE.patch
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
From f49b99f5a49da1a78b0ced930b6438bb53b49fdd Mon Sep 17 00:00:00 2001
|
||||
From: Volker Krause <vkrause@kde.org>
|
||||
Date: Sun, 30 Nov 2014 11:25:56 +0100
|
||||
Subject: [PATCH 14/30] No semicolon after Q_DECLARE_METATYPE.
|
||||
|
||||
---
|
||||
server/src/storage/entity.h | 2 +-
|
||||
server/tests/unittest/akappendhandlertest.cpp | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entity.h b/server/src/storage/entity.h
|
||||
index eb180e4..acebb0b 100644
|
||||
--- a/server/src/storage/entity.h
|
||||
+++ b/server/src/storage/entity.h
|
||||
@@ -192,6 +192,6 @@ namespace _detail {
|
||||
} // namespace Server
|
||||
} // namespace Akonadi
|
||||
|
||||
-Q_DECLARE_METATYPE(Akonadi::Server::Tristate);
|
||||
+Q_DECLARE_METATYPE(Akonadi::Server::Tristate)
|
||||
|
||||
#endif
|
||||
diff --git a/server/tests/unittest/akappendhandlertest.cpp b/server/tests/unittest/akappendhandlertest.cpp
|
||||
index c221a3a..d7f57f9 100644
|
||||
--- a/server/tests/unittest/akappendhandlertest.cpp
|
||||
+++ b/server/tests/unittest/akappendhandlertest.cpp
|
||||
@@ -41,7 +41,7 @@
|
||||
using namespace Akonadi;
|
||||
using namespace Akonadi::Server;
|
||||
|
||||
-Q_DECLARE_METATYPE(PimItem);
|
||||
+Q_DECLARE_METATYPE(PimItem)
|
||||
Q_DECLARE_METATYPE(QVector<Flag>)
|
||||
Q_DECLARE_METATYPE(QVector<FakePart>)
|
||||
Q_DECLARE_METATYPE(QVector<FakeTag>)
|
||||
--
|
||||
2.1.0
|
||||
|
||||
112
0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch
Normal file
112
0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
From f5a0e3f1f4787b6a48880e42463ae38dce336a8f Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Mon, 1 Dec 2014 11:36:31 +0100
|
||||
Subject: [PATCH 15/30] Use QMutexLocker instead of manual lock/unlock calls.
|
||||
|
||||
Just a minor cleanup patch, no change of behavior.
|
||||
---
|
||||
server/src/storage/entities-source.xsl | 17 +++++------------
|
||||
server/src/storage/entities.xsl | 4 +---
|
||||
2 files changed, 6 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entities-source.xsl b/server/src/storage/entities-source.xsl
|
||||
index 7090c31..05a8cb1 100644
|
||||
--- a/server/src/storage/entities-source.xsl
|
||||
+++ b/server/src/storage/entities-source.xsl
|
||||
@@ -125,14 +125,13 @@ void <xsl:value-of select="$className"/>::Private::addToCache( const <xsl:value-
|
||||
{
|
||||
Q_ASSERT( cacheEnabled );
|
||||
Q_UNUSED( entry ); <!-- in case the table has neither an id nor name column -->
|
||||
- cacheMutex.lock();
|
||||
+ QMutexLocker lock(&cacheMutex);
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
idCache.insert( entry.id(), entry );
|
||||
</xsl:if>
|
||||
<xsl:if test="column[@name = 'name']">
|
||||
nameCache.insert( entry.name(), entry );
|
||||
</xsl:if>
|
||||
- cacheMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -264,12 +263,10 @@ int <xsl:value-of select="$className"/>::count( const QString &column, const
|
||||
bool <xsl:value-of select="$className"/>::exists( qint64 id )
|
||||
{
|
||||
if ( Private::cacheEnabled ) {
|
||||
- Private::cacheMutex.lock();
|
||||
+ QMutexLocker lock(&Private::cacheMutex);
|
||||
if ( Private::idCache.contains( id ) ) {
|
||||
- Private::cacheMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
- Private::cacheMutex.unlock();
|
||||
}
|
||||
return count( idColumn(), id ) > 0;
|
||||
}
|
||||
@@ -278,12 +275,10 @@ bool <xsl:value-of select="$className"/>::exists( qint64 id )
|
||||
bool <xsl:value-of select="$className"/>::exists( const <xsl:value-of select="column[@name = 'name']/@type"/> &name )
|
||||
{
|
||||
if ( Private::cacheEnabled ) {
|
||||
- Private::cacheMutex.lock();
|
||||
+ QMutexLocker lock(&Private::cacheMutex);
|
||||
if ( Private::nameCache.contains( name ) ) {
|
||||
- Private::cacheMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
- Private::cacheMutex.unlock();
|
||||
}
|
||||
return count( nameColumn(), name ) > 0;
|
||||
}
|
||||
@@ -588,28 +583,26 @@ bool <xsl:value-of select="$className"/>::remove( qint64 id )
|
||||
void <xsl:value-of select="$className"/>::invalidateCache() const
|
||||
{
|
||||
if ( Private::cacheEnabled ) {
|
||||
- Private::cacheMutex.lock();
|
||||
+ QMutexLocker lock(&Private::cacheMutex);
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
Private::idCache.remove( id() );
|
||||
</xsl:if>
|
||||
<xsl:if test="column[@name = 'name']">
|
||||
Private::nameCache.remove( name() );
|
||||
</xsl:if>
|
||||
- Private::cacheMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void <xsl:value-of select="$className"/>::invalidateCompleteCache()
|
||||
{
|
||||
if ( Private::cacheEnabled ) {
|
||||
- Private::cacheMutex.lock();
|
||||
+ QMutexLocker lock(&Private::cacheMutex);
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
Private::idCache.clear();
|
||||
</xsl:if>
|
||||
<xsl:if test="column[@name = 'name']">
|
||||
Private::nameCache.clear();
|
||||
</xsl:if>
|
||||
- Private::cacheMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
|
||||
index 8b0ed03..a397544 100644
|
||||
--- a/server/src/storage/entities.xsl
|
||||
+++ b/server/src/storage/entities.xsl
|
||||
@@ -167,13 +167,11 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
<xsl:variable name="className"><xsl:value-of select="@name"/></xsl:variable>
|
||||
<xsl:if test="$cache != ''">
|
||||
if ( Private::cacheEnabled ) {
|
||||
- Private::cacheMutex.lock();
|
||||
+ QMutexLocker lock(&Private::cacheMutex);
|
||||
if ( Private::<xsl:value-of select="$cache"/>.contains( <xsl:value-of select="$key"/> ) ) {
|
||||
const <xsl:value-of select="$className"/> tmp = Private::<xsl:value-of select="$cache"/>.value( <xsl:value-of select="$key"/> );
|
||||
- Private::cacheMutex.unlock();
|
||||
return tmp;
|
||||
}
|
||||
- Private::cacheMutex.unlock();
|
||||
}
|
||||
</xsl:if>
|
||||
QSqlDatabase db = DataStore::self()->database();
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
From 8a113985cda1693c8158916065bd54e57d028cda Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Mon, 1 Dec 2014 11:39:33 +0100
|
||||
Subject: [PATCH 16/30] Use an QAtomicInt instead of a plain bool for
|
||||
Entity::cacheEnabled.
|
||||
|
||||
A plain bool is not thread safe and leads to undefined behavior.
|
||||
So better be safe than sorry and use a thread safe QAtomicInt.
|
||||
---
|
||||
server/src/storage/entities-source.xsl | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entities-source.xsl b/server/src/storage/entities-source.xsl
|
||||
index 05a8cb1..e398da5 100644
|
||||
--- a/server/src/storage/entities-source.xsl
|
||||
+++ b/server/src/storage/entities-source.xsl
|
||||
@@ -99,7 +99,7 @@ class <xsl:value-of select="$className"/>::Private : public QSharedData
|
||||
static void addToCache( const <xsl:value-of select="$className"/> & entry );
|
||||
|
||||
// cache
|
||||
- static bool cacheEnabled;
|
||||
+ static QAtomicInt cacheEnabled;
|
||||
static QMutex cacheMutex;
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
static QHash<qint64, <xsl:value-of select="$className"/> > idCache;
|
||||
@@ -111,7 +111,7 @@ class <xsl:value-of select="$className"/>::Private : public QSharedData
|
||||
|
||||
|
||||
// static members
|
||||
-bool <xsl:value-of select="$className"/>::Private::cacheEnabled = false;
|
||||
+QAtomicInt <xsl:value-of select="$className"/>::Private::cacheEnabled(0);
|
||||
QMutex <xsl:value-of select="$className"/>::Private::cacheMutex;
|
||||
<xsl:if test="column[@name = 'id']">
|
||||
QHash<qint64, <xsl:value-of select="$className"/> > <xsl:value-of select="$className"/>::Private::idCache;
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
From 202ffa522668087cc133026febf21a7de8963218 Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Mon, 1 Dec 2014 11:51:04 +0100
|
||||
Subject: [PATCH 17/30] Optimize: Only do one hash lookup to retrieve value
|
||||
from cache.
|
||||
|
||||
Compilers do not merge the call to contains() and the successive
|
||||
value() lookup. Using iterators thus saves us one QHash lookup.
|
||||
---
|
||||
server/src/storage/entities.xsl | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
|
||||
index a397544..9471293 100644
|
||||
--- a/server/src/storage/entities.xsl
|
||||
+++ b/server/src/storage/entities.xsl
|
||||
@@ -168,9 +168,9 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
<xsl:if test="$cache != ''">
|
||||
if ( Private::cacheEnabled ) {
|
||||
QMutexLocker lock(&Private::cacheMutex);
|
||||
- if ( Private::<xsl:value-of select="$cache"/>.contains( <xsl:value-of select="$key"/> ) ) {
|
||||
- const <xsl:value-of select="$className"/> tmp = Private::<xsl:value-of select="$cache"/>.value( <xsl:value-of select="$key"/> );
|
||||
- return tmp;
|
||||
+ QHash<<xsl:value-of select="column[@name = $key]/@type"/>, <xsl:value-of select="$className"/>>::const_iterator it = Private::<xsl:value-of select="$cache"/>.constFind(<xsl:value-of select="$key"/>);
|
||||
+ if ( it != Private::<xsl:value-of select="$cache"/>.constEnd() ) {
|
||||
+ return it.value();
|
||||
}
|
||||
}
|
||||
</xsl:if>
|
||||
--
|
||||
2.1.0
|
||||
|
||||
38
0018-Optimize-Skip-value-condition-on-invalid-flags.patch
Normal file
38
0018-Optimize-Skip-value-condition-on-invalid-flags.patch
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
From 7cbff48f5782d1f7f844678e6b785aeb419b0c47 Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Mon, 1 Dec 2014 11:59:12 +0100
|
||||
Subject: [PATCH 18/30] Optimize: Skip value condition on invalid flags.
|
||||
|
||||
HandlerHelper::itemWithFlagsCount gets called quite often apparently
|
||||
and I noticed that it was relatively slow from the Query Debugger
|
||||
in Akonadi Console. EXPLAIN'ing the query showed that it was using
|
||||
a slow-path for the WHERE FOO AND (BAR OR ASDF) condition. Here,
|
||||
ASDF was always id = -1, the id of the $IGNORED flag, which
|
||||
I apparently don't have. Getting rid of that condition simplifies
|
||||
the query to WHERE FOO AND BAR, which is apparently much better
|
||||
optimizable. Before, the query often showed a runtime of ~15ms.
|
||||
Now it is down to ~9ms.
|
||||
|
||||
REVIEW: 121306
|
||||
---
|
||||
server/src/handlerhelper.cpp | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/server/src/handlerhelper.cpp b/server/src/handlerhelper.cpp
|
||||
index 634a26c..82347b4 100644
|
||||
--- a/server/src/handlerhelper.cpp
|
||||
+++ b/server/src/handlerhelper.cpp
|
||||
@@ -123,6 +123,10 @@ int HandlerHelper::itemWithFlagsCount( const Collection &col, const QStringList
|
||||
// it hits an in-memory cache.
|
||||
Q_FOREACH ( const QString &flag, flags ) {
|
||||
const Flag f = Flag::retrieveByName( flag );
|
||||
+ if (!f.isValid()) {
|
||||
+ // since we OR this condition, we can skip invalid flags to speed up the query
|
||||
+ continue;
|
||||
+ }
|
||||
cond.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, f.id() );
|
||||
}
|
||||
qb.addCondition( cond );
|
||||
--
|
||||
2.1.0
|
||||
|
||||
107
0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch
Normal file
107
0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
From e52b57b7a9f0303c0c710e60870d0ec265d32541 Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Mon, 1 Dec 2014 14:11:19 +0100
|
||||
Subject: [PATCH 19/30] Optimize queries: Do not retrieve known key used in the
|
||||
condition.
|
||||
|
||||
There is no point in doing a select like:
|
||||
|
||||
SELECT foo, bar FROM table WHERE foo = needle;
|
||||
|
||||
That can be rewritten to say
|
||||
|
||||
SELECT bar FROM table WHERE foo = needle;
|
||||
|
||||
This reduces the data traffic with the mysql server. Additionally, it
|
||||
work-arounds some issues in Qt SQL, which lead to bad performance:
|
||||
QSqlResult::value incurs multiple temporary allocations, and string
|
||||
conversions, even to read a simple integer ID for example. Finally,
|
||||
by reusing an externally provided QString name e.g., we can leverage
|
||||
Qt's implicit sharing, instead of duplicating the string in a separate
|
||||
QString instance, with the contents read from SQL server.
|
||||
|
||||
REVIEW: 121310
|
||||
---
|
||||
server/src/storage/entities.xsl | 50 +++++++++++++++++++++++++++++------------
|
||||
1 file changed, 36 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
|
||||
index 9471293..c8fb1fd 100644
|
||||
--- a/server/src/storage/entities.xsl
|
||||
+++ b/server/src/storage/entities.xsl
|
||||
@@ -104,6 +104,12 @@ Q_DECLARE_TYPEINFO( Akonadi::Server::<xsl:value-of select="@name"/>, Q_MOVABLE_T
|
||||
|
||||
using namespace Akonadi::Server;
|
||||
|
||||
+static QStringList removeEntry(QStringList list, const QString& entry)
|
||||
+{
|
||||
+ list.removeOne(entry);
|
||||
+ return list;
|
||||
+}
|
||||
+
|
||||
<xsl:for-each select="database/table">
|
||||
<xsl:call-template name="table-source"/>
|
||||
</xsl:for-each>
|
||||
@@ -179,7 +185,8 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
return <xsl:value-of select="$className"/>();
|
||||
|
||||
QueryBuilder qb( tableName(), QueryBuilder::Select );
|
||||
- qb.addColumns( columnNames() );
|
||||
+ static const QStringList columns = removeEntry(columnNames(), <xsl:value-of select="$key"/>Column());
|
||||
+ qb.addColumns( columns );
|
||||
qb.addValueCondition( <xsl:value-of select="$key"/>Column(), Query::Equals, <xsl:value-of select="$key"/> );
|
||||
if ( !qb.exec() ) {
|
||||
akDebug() << "Error during selection of record with <xsl:value-of select="$key"/>"
|
||||
@@ -191,21 +198,36 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
return <xsl:value-of select="$className"/>();
|
||||
}
|
||||
|
||||
+ <!-- this indirection is required to prevent off-by-one access now that we skip the key column -->
|
||||
+ int valueIndex = 0;
|
||||
+ <xsl:for-each select="column">
|
||||
+ const <xsl:value-of select="@type"/> value<xsl:value-of select="position()"/> =
|
||||
+ <xsl:choose>
|
||||
+ <xsl:when test="@name=$key">
|
||||
+ <xsl:value-of select="$key"/>;
|
||||
+ </xsl:when>
|
||||
+ <xsl:otherwise>
|
||||
+ (qb.query().isNull(valueIndex)) ?
|
||||
+ <xsl:value-of select="@type"/>() :
|
||||
+ <xsl:choose>
|
||||
+ <xsl:when test="starts-with(@type,'QString')">
|
||||
+ Utils::variantToString( qb.query().value( valueIndex ) )
|
||||
+ </xsl:when>
|
||||
+ <xsl:when test="starts-with(@type, 'Tristate')">
|
||||
+ static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
|
||||
+ </xsl:when>
|
||||
+ <xsl:otherwise>
|
||||
+ qb.query().value( valueIndex ).value<<xsl:value-of select="@type"/>>()
|
||||
+ </xsl:otherwise>
|
||||
+ </xsl:choose>
|
||||
+ ; ++valueIndex;
|
||||
+ </xsl:otherwise>
|
||||
+ </xsl:choose>
|
||||
+ </xsl:for-each>
|
||||
+
|
||||
<xsl:value-of select="$className"/> rv(
|
||||
<xsl:for-each select="column">
|
||||
- (qb.query().isNull(<xsl:value-of select="position() - 1"/>)) ?
|
||||
- <xsl:value-of select="@type"/>() :
|
||||
- <xsl:choose>
|
||||
- <xsl:when test="starts-with(@type,'QString')">
|
||||
- Utils::variantToString( qb.query().value( <xsl:value-of select="position() - 1"/> ) )
|
||||
- </xsl:when>
|
||||
- <xsl:when test="starts-with(@type, 'Tristate')">
|
||||
- static_cast<Tristate>(qb.query().value( <xsl:value-of select="position() - 1"/> ).value<int>())
|
||||
- </xsl:when>
|
||||
- <xsl:otherwise>
|
||||
- qb.query().value( <xsl:value-of select="position() - 1"/> ).value<<xsl:value-of select="@type"/>>()
|
||||
- </xsl:otherwise>
|
||||
- </xsl:choose>
|
||||
+ value<xsl:value-of select="position()"/>
|
||||
<xsl:if test="position() != last()">,</xsl:if>
|
||||
</xsl:for-each>
|
||||
);
|
||||
--
|
||||
2.1.0
|
||||
|
||||
262
0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch
Normal file
262
0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
From 215b188d891d5236fe94131d176d7ddc3ae02d5d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 5 Dec 2014 17:12:28 +0100
|
||||
Subject: [PATCH 20/30] Avoid ridiculous amount of SQL queries by caching
|
||||
PartTypes
|
||||
|
||||
PartTypes are identified by their FQ name, which is in form NAMESPACE:NAME,
|
||||
where namespace and name are stored in individual columns. For this reason
|
||||
the standard ::retrieveByName() and name cache generated from entities.xslt
|
||||
does not work. This patch adds special handling for PartType table, so that
|
||||
a special PartType::retrieveByFQName() method as well as PartType name cache
|
||||
handling are generated during the XSL Transformation, allowing us to cache
|
||||
all the PartTypes.
|
||||
|
||||
This reduces the amount of SQL queries by at least two for each single AKAPPEND,
|
||||
MERGE, STORE and FETCH command, providing a nice performance boost during
|
||||
sync.
|
||||
---
|
||||
server/src/handler/append.cpp | 4 ++--
|
||||
server/src/storage/datastore.cpp | 4 +++-
|
||||
server/src/storage/entities-header.xsl | 7 ++++++-
|
||||
server/src/storage/entities-source.xsl | 31 ++++++++++++++++++++++++++++++-
|
||||
server/src/storage/entities.xsl | 7 ++++++-
|
||||
server/src/storage/parttypehelper.cpp | 29 +----------------------------
|
||||
server/src/storage/parttypehelper.h | 13 -------------
|
||||
7 files changed, 48 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/server/src/handler/append.cpp b/server/src/handler/append.cpp
|
||||
index c503216..b594e27 100644
|
||||
--- a/server/src/handler/append.cpp
|
||||
+++ b/server/src/handler/append.cpp
|
||||
@@ -134,7 +134,7 @@ bool Append::commit()
|
||||
|
||||
// wrap data into a part
|
||||
Part part;
|
||||
- part.setPartType( PartTypeHelper::fromName( "PLD", "RFC822" ) );
|
||||
+ part.setPartType( PartType::retrieveByFQName( QLatin1String("PLD"), QLatin1String("RFC822") ) );
|
||||
part.setData( m_data );
|
||||
part.setPimItemId( item.id() );
|
||||
part.setDatasize( dataSize );
|
||||
@@ -148,7 +148,7 @@ bool Append::commit()
|
||||
//akDebug() << "Append handler: doPreprocessing is" << doPreprocessing;
|
||||
if ( doPreprocessing ) {
|
||||
Part hiddenAttribute;
|
||||
- hiddenAttribute.setPartType( PartTypeHelper::fromName( "ATR", "HIDDEN" ) );
|
||||
+ hiddenAttribute.setPartType( PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ) );
|
||||
hiddenAttribute.setData( QByteArray() );
|
||||
hiddenAttribute.setPimItemId( item.id() );
|
||||
hiddenAttribute.setDatasize( 0 );
|
||||
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
|
||||
index ae78bab..304f0e8 100644
|
||||
--- a/server/src/storage/datastore.cpp
|
||||
+++ b/server/src/storage/datastore.cpp
|
||||
@@ -183,6 +183,7 @@ bool DataStore::init()
|
||||
Flag::enableCache( true );
|
||||
Resource::enableCache( true );
|
||||
Collection::enableCache( true );
|
||||
+ PartType::enableCache( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1025,7 +1026,8 @@ bool DataStore::unhideAllPimItems()
|
||||
akDebug() << "DataStore::unhideAllPimItems()";
|
||||
|
||||
try {
|
||||
- return PartHelper::remove( Part::partTypeIdFullColumnName(), PartTypeHelper::fromName( "ATR", "HIDDEN" ).id() );
|
||||
+ return PartHelper::remove( Part::partTypeIdFullColumnName(),
|
||||
+ PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ).id() );
|
||||
} catch ( ... ) {} // we can live with this failing
|
||||
|
||||
return false;
|
||||
diff --git a/server/src/storage/entities-header.xsl b/server/src/storage/entities-header.xsl
|
||||
index 4966966..d515fd3 100644
|
||||
--- a/server/src/storage/entities-header.xsl
|
||||
+++ b/server/src/storage/entities-header.xsl
|
||||
@@ -133,11 +133,16 @@ class <xsl:value-of select="$className"/> : private Entity
|
||||
<xsl:text>static </xsl:text><xsl:value-of select="$className"/> retrieveById( qint64 id );
|
||||
</xsl:if>
|
||||
|
||||
- <xsl:if test="column[@name = 'name']">
|
||||
+ <xsl:if test="column[@name = 'name'] and $className != 'PartType'">
|
||||
/** Returns the record with name @p name. */
|
||||
<xsl:text>static </xsl:text><xsl:value-of select="$className"/> retrieveByName( const <xsl:value-of select="column[@name = 'name']/@type"/> &name );
|
||||
</xsl:if>
|
||||
|
||||
+ <xsl:if test="column[@name = 'name'] and $className = 'PartType'">
|
||||
+ <!-- Special case for PartTypes, which are identified by "NS:NAME" -->
|
||||
+ <xsl:text>static PartType retrieveByFQName( const QString &ns, const QString &name );</xsl:text>
|
||||
+ </xsl:if>
|
||||
+
|
||||
/** Retrieve all records from this table. */
|
||||
static <xsl:value-of select="$className"/>::List retrieveAll();
|
||||
/** Retrieve all records with value @p value in column @p key. */
|
||||
diff --git a/server/src/storage/entities-source.xsl b/server/src/storage/entities-source.xsl
|
||||
index e398da5..46ef3a6 100644
|
||||
--- a/server/src/storage/entities-source.xsl
|
||||
+++ b/server/src/storage/entities-source.xsl
|
||||
@@ -130,7 +130,15 @@ void <xsl:value-of select="$className"/>::Private::addToCache( const <xsl:value-
|
||||
idCache.insert( entry.id(), entry );
|
||||
</xsl:if>
|
||||
<xsl:if test="column[@name = 'name']">
|
||||
+ <xsl:choose>
|
||||
+ <xsl:when test="$className = 'PartType'">
|
||||
+ <!-- special case for PartType, which is identified as "NS:NAME" -->
|
||||
+ nameCache.insert( entry.ns() + QLatin1Char(':') + entry.name(), entry );
|
||||
+ </xsl:when>
|
||||
+ <xsl:otherwise>
|
||||
nameCache.insert( entry.name(), entry );
|
||||
+ </xsl:otherwise>
|
||||
+ </xsl:choose>
|
||||
</xsl:if>
|
||||
}
|
||||
|
||||
@@ -323,7 +331,7 @@ QVector< <xsl:value-of select="$className"/> > <xsl:value-of select="$clas
|
||||
}
|
||||
|
||||
</xsl:if>
|
||||
-<xsl:if test="column[@name = 'name']">
|
||||
+<xsl:if test="column[@name = 'name'] and $className != 'PartType'">
|
||||
<xsl:value-of select="$className"/><xsl:text> </xsl:text><xsl:value-of select="$className"/>::retrieveByName( const <xsl:value-of select="column[@name = 'name']/@type"/> &name )
|
||||
{
|
||||
<xsl:call-template name="data-retrieval">
|
||||
@@ -333,6 +341,19 @@ QVector< <xsl:value-of select="$className"/> > <xsl:value-of select="$clas
|
||||
}
|
||||
</xsl:if>
|
||||
|
||||
+<xsl:if test="column[@name = 'name'] and $className = 'PartType'">
|
||||
+<xsl:text>PartType PartType::retrieveByFQName( const QString & ns, const QString & name )</xsl:text>
|
||||
+{
|
||||
+ const QString fqname = ns + QLatin1Char(':') + name;
|
||||
+ <xsl:call-template name="data-retrieval">
|
||||
+ <xsl:with-param name="key">ns</xsl:with-param>
|
||||
+ <xsl:with-param name="key2">name</xsl:with-param>
|
||||
+ <xsl:with-param name="lookupKey">fqname</xsl:with-param>
|
||||
+ <xsl:with-param name="cache">nameCache</xsl:with-param>
|
||||
+ </xsl:call-template>
|
||||
+}
|
||||
+</xsl:if>
|
||||
+
|
||||
QVector<<xsl:value-of select="$className"/>> <xsl:value-of select="$className"/>::retrieveAll()
|
||||
{
|
||||
QSqlDatabase db = DataStore::self()->database();
|
||||
@@ -588,7 +609,15 @@ void <xsl:value-of select="$className"/>::invalidateCache() const
|
||||
Private::idCache.remove( id() );
|
||||
</xsl:if>
|
||||
<xsl:if test="column[@name = 'name']">
|
||||
+ <xsl:choose>
|
||||
+ <xsl:when test="$className = 'PartType'">
|
||||
+ <!-- Special handling for PartType, which is identified as "NS:NAME" -->
|
||||
+ Private::nameCache.remove( ns() + QLatin1Char(':') + name() );
|
||||
+ </xsl:when>
|
||||
+ <xsl:otherwise>
|
||||
Private::nameCache.remove( name() );
|
||||
+ </xsl:otherwise>
|
||||
+ </xsl:choose>
|
||||
</xsl:if>
|
||||
}
|
||||
}
|
||||
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
|
||||
index c8fb1fd..2cf96c4 100644
|
||||
--- a/server/src/storage/entities.xsl
|
||||
+++ b/server/src/storage/entities.xsl
|
||||
@@ -169,12 +169,14 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
<!-- data retrieval for a given key field -->
|
||||
<xsl:template name="data-retrieval">
|
||||
<xsl:param name="key"/>
|
||||
+<xsl:param name="key2"/>
|
||||
+<xsl:param name="lookupKey" select="$key"/>
|
||||
<xsl:param name="cache"/>
|
||||
<xsl:variable name="className"><xsl:value-of select="@name"/></xsl:variable>
|
||||
<xsl:if test="$cache != ''">
|
||||
if ( Private::cacheEnabled ) {
|
||||
QMutexLocker lock(&Private::cacheMutex);
|
||||
- QHash<<xsl:value-of select="column[@name = $key]/@type"/>, <xsl:value-of select="$className"/>>::const_iterator it = Private::<xsl:value-of select="$cache"/>.constFind(<xsl:value-of select="$key"/>);
|
||||
+ QHash<<xsl:value-of select="column[@name = $key]/@type"/>, <xsl:value-of select="$className"/>>::const_iterator it = Private::<xsl:value-of select="$cache"/>.constFind(<xsl:value-of select="$lookupKey"/>);
|
||||
if ( it != Private::<xsl:value-of select="$cache"/>.constEnd() ) {
|
||||
return it.value();
|
||||
}
|
||||
@@ -188,6 +190,9 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
|
||||
static const QStringList columns = removeEntry(columnNames(), <xsl:value-of select="$key"/>Column());
|
||||
qb.addColumns( columns );
|
||||
qb.addValueCondition( <xsl:value-of select="$key"/>Column(), Query::Equals, <xsl:value-of select="$key"/> );
|
||||
+ <xsl:if test="$key2 != ''">
|
||||
+ qb.addValueCondition( <xsl:value-of select="$key2"/>Column(), Query::Equals, <xsl:value-of select="$key2"/> );
|
||||
+ </xsl:if>
|
||||
if ( !qb.exec() ) {
|
||||
akDebug() << "Error during selection of record with <xsl:value-of select="$key"/>"
|
||||
<< <xsl:value-of select="$key"/> << "from table" << tableName()
|
||||
diff --git a/server/src/storage/parttypehelper.cpp b/server/src/storage/parttypehelper.cpp
|
||||
index b73dcd5..7654108 100644
|
||||
--- a/server/src/storage/parttypehelper.cpp
|
||||
+++ b/server/src/storage/parttypehelper.cpp
|
||||
@@ -37,7 +37,7 @@ QPair< QString, QString > PartTypeHelper::parseFqName(const QString& fqName)
|
||||
PartType PartTypeHelper::fromFqName(const QString& fqName)
|
||||
{
|
||||
const QPair<QString, QString> p = parseFqName( fqName );
|
||||
- return fromName( p.first, p.second );
|
||||
+ return PartType::retrieveByFQName(p.first, p.second);
|
||||
}
|
||||
|
||||
PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
|
||||
@@ -45,33 +45,6 @@ PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
|
||||
return fromFqName( QLatin1String(fqName) );
|
||||
}
|
||||
|
||||
-PartType PartTypeHelper::fromName(const QString& ns, const QString& typeName)
|
||||
-{
|
||||
- SelectQueryBuilder<PartType> qb;
|
||||
- qb.addValueCondition( PartType::nsColumn(), Query::Equals, ns );
|
||||
- qb.addValueCondition( PartType::nameColumn(), Query::Equals, typeName );
|
||||
- if ( !qb.exec() )
|
||||
- throw PartTypeException( "Unable to query part type table." );
|
||||
- const PartType::List result = qb.result();
|
||||
- if ( result.size() == 1 )
|
||||
- return result.first();
|
||||
- if ( result.size() > 1 )
|
||||
- throw PartTypeException( "Part type uniqueness constraint violation." );
|
||||
-
|
||||
- // doesn't exist yet, so let's create a new one
|
||||
- PartType type;
|
||||
- type.setName( typeName );
|
||||
- type.setNs( ns );
|
||||
- if ( !type.insert() )
|
||||
- throw PartTypeException( "Creating a new part type failed." );
|
||||
- return type;
|
||||
-}
|
||||
-
|
||||
-PartType PartTypeHelper::fromName(const char* ns, const char* typeName)
|
||||
-{
|
||||
- return fromName( QLatin1String(ns), QLatin1String(typeName) );
|
||||
-}
|
||||
-
|
||||
Query::Condition PartTypeHelper::conditionFromFqName(const QString& fqName)
|
||||
{
|
||||
const QPair<QString, QString> p = parseFqName( fqName );
|
||||
diff --git a/server/src/storage/parttypehelper.h b/server/src/storage/parttypehelper.h
|
||||
index 38cb858..4c4f42f 100644
|
||||
--- a/server/src/storage/parttypehelper.h
|
||||
+++ b/server/src/storage/parttypehelper.h
|
||||
@@ -48,19 +48,6 @@ namespace PartTypeHelper
|
||||
PartType fromFqName( const QByteArray &fqName );
|
||||
|
||||
/**
|
||||
- * Retrieve (or create) PartType for the given namespace and type name.
|
||||
- * @param ns Namespace
|
||||
- * @param typeName Part type name.
|
||||
- * @throws PartTypeException
|
||||
- */
|
||||
- PartType fromName( const QString &ns, const QString &typeName );
|
||||
-
|
||||
- /**
|
||||
- * Convenience overload of the above.
|
||||
- */
|
||||
- PartType fromName( const char *ns, const char *typeName );
|
||||
-
|
||||
- /**
|
||||
* Returns a query condition that matches the given part.
|
||||
* @param fqName fully-qualified part type name
|
||||
* @throws PartTypeException
|
||||
--
|
||||
2.1.0
|
||||
|
||||
241
0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch
Normal file
241
0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
From 9698d589e4c2b489f406fe1a823d4bb42c322f71 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 5 Dec 2014 18:21:18 +0100
|
||||
Subject: [PATCH 21/30] Implement support for CASE...WHEN...THEN SQL statements
|
||||
SELECT columns
|
||||
|
||||
CASE...WHEN...THEN is a useful construct especially for aggregation
|
||||
queries.
|
||||
---
|
||||
server/src/storage/query.cpp | 38 ++++++++++++++++++++++++++++++
|
||||
server/src/storage/query.h | 19 +++++++++++++++
|
||||
server/src/storage/querybuilder.cpp | 30 +++++++++++++++++++++++
|
||||
server/src/storage/querybuilder.h | 14 +++++++++++
|
||||
server/tests/unittest/querybuildertest.cpp | 38 +++++++++++++++++++++++++++++-
|
||||
5 files changed, 138 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/src/storage/query.cpp b/server/src/storage/query.cpp
|
||||
index 6fb6c6e..c938ade 100644
|
||||
--- a/server/src/storage/query.cpp
|
||||
+++ b/server/src/storage/query.cpp
|
||||
@@ -68,3 +68,41 @@ void Query::Condition::addCondition( const Condition &condition )
|
||||
{
|
||||
mSubConditions << condition;
|
||||
}
|
||||
+
|
||||
+
|
||||
+Case::Case(const Condition &when, const QString &then, const QString &elseBranch)
|
||||
+{
|
||||
+ addCondition(when, then);
|
||||
+ setElse(elseBranch);
|
||||
+}
|
||||
+
|
||||
+Case::Case(const QString &column, CompareOperator op, const QVariant &value, const QString &when, const QString &elseBranch)
|
||||
+{
|
||||
+ addValueCondition(column, op, value, when);
|
||||
+ setElse(elseBranch);
|
||||
+}
|
||||
+
|
||||
+void Case::addCondition(const Condition &when, const QString &then)
|
||||
+{
|
||||
+ mWhenThen.append(qMakePair(when, then));
|
||||
+}
|
||||
+
|
||||
+void Case::addValueCondition(const QString &column, CompareOperator op, const QVariant &value, const QString &then)
|
||||
+{
|
||||
+ Condition when;
|
||||
+ when.addValueCondition(column, op, value);
|
||||
+ addCondition(when, then);
|
||||
+}
|
||||
+
|
||||
+void Case::addColumnCondition(const QString &column, CompareOperator op, const QString &column2, const QString &then)
|
||||
+{
|
||||
+ Condition when;
|
||||
+ when.addColumnCondition(column, op, column2);
|
||||
+ addCondition(when, then);
|
||||
+}
|
||||
+
|
||||
+void Case::setElse(const QString &elseBranch)
|
||||
+{
|
||||
+ mElse = elseBranch;
|
||||
+}
|
||||
+
|
||||
diff --git a/server/src/storage/query.h b/server/src/storage/query.h
|
||||
index f4f1ac0..c8f35a7 100644
|
||||
--- a/server/src/storage/query.h
|
||||
+++ b/server/src/storage/query.h
|
||||
@@ -130,6 +130,25 @@ class Condition
|
||||
|
||||
}; // class Condition
|
||||
|
||||
+
|
||||
+class Case
|
||||
+{
|
||||
+ friend class Akonadi::Server::QueryBuilder;
|
||||
+ public:
|
||||
+ Case(const Condition &when, const QString &then, const QString &elseBranch = QString());
|
||||
+ Case(const QString &column, Query::CompareOperator op, const QVariant &value, const QString &when, const QString &elseBranch = QString());
|
||||
+
|
||||
+ void addCondition(const Condition &when, const QString &then);
|
||||
+ void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, const QString &then);
|
||||
+ void addColumnCondition(const QString &column, Query::CompareOperator op, const QString &column2, const QString &then);
|
||||
+
|
||||
+ void setElse(const QString &elseBranch);
|
||||
+
|
||||
+ private:
|
||||
+ QVector<QPair<Condition, QString> > mWhenThen;
|
||||
+ QString mElse;
|
||||
+};
|
||||
+
|
||||
} // namespace Query
|
||||
} // namespace Server
|
||||
} // namespace Akonadi
|
||||
diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp
|
||||
index 3017867..74ed2da 100644
|
||||
--- a/server/src/storage/querybuilder.cpp
|
||||
+++ b/server/src/storage/querybuilder.cpp
|
||||
@@ -457,11 +457,27 @@ void QueryBuilder::addColumn( const QString &col )
|
||||
mColumns << col;
|
||||
}
|
||||
|
||||
+void QueryBuilder::addColumn( const Query::Case &caseStmt )
|
||||
+{
|
||||
+ QString query;
|
||||
+ buildCaseStatement(&query, caseStmt);
|
||||
+ mColumns.append(query);
|
||||
+}
|
||||
+
|
||||
void QueryBuilder::addAggregation( const QString &col, const QString &aggregate )
|
||||
{
|
||||
mColumns.append( aggregate + QLatin1Char( '(' ) + col + QLatin1Char( ')' ) );
|
||||
}
|
||||
|
||||
+void QueryBuilder::addAggregation(const Query::Case &caseStmt, const QString &aggregate)
|
||||
+{
|
||||
+ QString query(aggregate + QLatin1Char('('));
|
||||
+ buildCaseStatement(&query, caseStmt);
|
||||
+ query += QLatin1Char(')');
|
||||
+
|
||||
+ mColumns.append(query);
|
||||
+}
|
||||
+
|
||||
void QueryBuilder::bindValue( QString *query, const QVariant &value )
|
||||
{
|
||||
mBindValues << value;
|
||||
@@ -510,6 +526,20 @@ void QueryBuilder::buildWhereCondition( QString *query, const Query::Condition &
|
||||
}
|
||||
}
|
||||
|
||||
+void QueryBuilder::buildCaseStatement(QString *query, const Query::Case &caseStmt)
|
||||
+{
|
||||
+ *query += QLatin1String("CASE ");
|
||||
+ for (const auto whenThen : caseStmt.mWhenThen) {
|
||||
+ *query += QLatin1String("WHEN ");
|
||||
+ buildWhereCondition(query, whenThen.first); // When
|
||||
+ *query += QLatin1String(" THEN ") + whenThen.second; // then
|
||||
+ }
|
||||
+ if (!caseStmt.mElse.isEmpty()) {
|
||||
+ *query += QLatin1String(" ELSE ") + caseStmt.mElse;
|
||||
+ }
|
||||
+ *query += QLatin1String(" END");
|
||||
+}
|
||||
+
|
||||
void QueryBuilder::setSubQueryMode( Query::LogicOperator op, ConditionType type )
|
||||
{
|
||||
Q_ASSERT( type == WhereCondition || ( type == HavingCondition && mType == Select ) );
|
||||
diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h
|
||||
index df7c362..0304108 100644
|
||||
--- a/server/src/storage/querybuilder.h
|
||||
+++ b/server/src/storage/querybuilder.h
|
||||
@@ -123,6 +123,12 @@ class QueryBuilder
|
||||
void addColumn( const QString &col );
|
||||
|
||||
/**
|
||||
+ * Adds the given case statement to a select query.
|
||||
+ * @param caseStmt The case statement to add.
|
||||
+ */
|
||||
+ void addColumn( const Query::Case &caseStmt );
|
||||
+
|
||||
+ /**
|
||||
* Adds an aggregation statement.
|
||||
* @param col The column to aggregate on
|
||||
* @param aggregate The aggregation function.
|
||||
@@ -130,6 +136,13 @@ class QueryBuilder
|
||||
void addAggregation( const QString &col, const QString &aggregate );
|
||||
|
||||
/**
|
||||
+ * Adds and aggregation statement with CASE
|
||||
+ * @param caseStmt The case statement to aggregate on
|
||||
+ * @param aggregate The aggregation function.
|
||||
+ */
|
||||
+ void addAggregation( const Query::Case &caseStmt, const QString &aggregate );
|
||||
+
|
||||
+ /**
|
||||
Add a WHERE or HAVING condition which compares a column with a given value.
|
||||
@param column The column that should be compared.
|
||||
@param op The operator used for comparison
|
||||
@@ -239,6 +252,7 @@ class QueryBuilder
|
||||
void buildQuery( QString *query );
|
||||
void bindValue( QString *query, const QVariant &value );
|
||||
void buildWhereCondition( QString *query, const Query::Condition &cond );
|
||||
+ void buildCaseStatement( QString *query, const Query::Case &caseStmt );
|
||||
|
||||
/**
|
||||
* SQLite does not support JOINs with UPDATE, so we have to convert it into
|
||||
diff --git a/server/tests/unittest/querybuildertest.cpp b/server/tests/unittest/querybuildertest.cpp
|
||||
index 92df2a2..848829d 100644
|
||||
--- a/server/tests/unittest/querybuildertest.cpp
|
||||
+++ b/server/tests/unittest/querybuildertest.cpp
|
||||
@@ -217,6 +217,42 @@ void QueryBuilderTest::testQueryBuilder_data()
|
||||
}
|
||||
|
||||
{
|
||||
+ /// SELECT with CASE
|
||||
+ QueryBuilder qbTpl = QueryBuilder("table1", QueryBuilder::Select );
|
||||
+ qbTpl.setDatabaseType( DbType::MySQL );
|
||||
+
|
||||
+ QueryBuilder qb = qbTpl;
|
||||
+ qb.addColumn( "col" );
|
||||
+ qb.addColumn( Query::Case( "col1", Query::Greater, 42, "1", "0" ) );
|
||||
+ bindVals.clear();
|
||||
+ bindVals << 42;
|
||||
+ mBuilders << qb;
|
||||
+ QTest::newRow( "select case simple") << mBuilders.count()
|
||||
+ << QString( "SELECT col, CASE WHEN ( col1 > :0 ) THEN 1 ELSE 0 END FROM table1" ) << bindVals;
|
||||
+
|
||||
+
|
||||
+ qb = qbTpl;
|
||||
+ qb.addAggregation( "table1.col1", "sum" );
|
||||
+ qb.addAggregation( "table1.col2", "count" );
|
||||
+ Query::Condition cond( Query::Or );
|
||||
+ cond.addValueCondition( "table3.col2", Query::Equals, "value1" );
|
||||
+ cond.addValueCondition( "table3.col2", Query::Equals, "value2" );\
|
||||
+ Query::Case caseStmt( cond, "1", "0" );
|
||||
+ qb.addAggregation( caseStmt, "sum" );
|
||||
+ qb.addJoin( QueryBuilder::LeftJoin, "table2", "table1.col3", "table2.col1" );
|
||||
+ qb.addJoin( QueryBuilder::LeftJoin, "table3", "table2.col2", "table3.col1" );
|
||||
+ bindVals.clear();
|
||||
+ bindVals << QString("value1") << QString("value2");
|
||||
+ mBuilders <<qb;
|
||||
+ QTest::newRow( "select case, aggregation and joins" ) << mBuilders.count()
|
||||
+ << QString( "SELECT sum(table1.col1), count(table1.col2), sum(CASE WHEN ( table3.col2 = :0 OR table3.col2 = :1 ) THEN 1 ELSE 0 END) "
|
||||
+ "FROM table1 "
|
||||
+ "LEFT JOIN table2 ON ( table1.col3 = table2.col1 ) "
|
||||
+ "LEFT JOIN table3 ON ( table2.col2 = table3.col1 )")
|
||||
+ << bindVals;
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
/// UPDATE with INNER JOIN
|
||||
QueryBuilder qbTpl = QueryBuilder( "table1", QueryBuilder::Update );
|
||||
qbTpl.setColumnValue( "col", 42 );
|
||||
@@ -310,4 +346,4 @@ void QueryBuilderTest::benchQueryBuilder()
|
||||
}
|
||||
|
||||
QVERIFY(executed);
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
--
|
||||
2.1.0
|
||||
|
||||
691
0022-Implement-cache-for-CollectionStatistics-to-signific.patch
Normal file
691
0022-Implement-cache-for-CollectionStatistics-to-signific.patch
Normal file
|
|
@ -0,0 +1,691 @@
|
|||
From c24329bb570ee16c033228588e6d22b0f6000f95 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 5 Dec 2014 18:23:33 +0100
|
||||
Subject: [PATCH 22/30] Implement cache for CollectionStatistics to
|
||||
significantly reduce amount of SQL queries
|
||||
|
||||
Collection statistics are being requested extremely often (basically whenever
|
||||
a PimItem is changed, or when a Collection itself is changed), and it's always
|
||||
requested by at least 5 or so clients (including agents that listen to
|
||||
everything).
|
||||
|
||||
To decrease the load on database we now cache the Collection statistics and
|
||||
we only invalidate a cache entry when respective collection (or it's content)
|
||||
is changed. The invalidation is invoked from NotificationCollector, which is
|
||||
basically a hack, but performance-wise it's the best place to avoid additional
|
||||
expensive queries.
|
||||
|
||||
This patch also optimizes the SQL query needed to get up-to-date statistics.
|
||||
We now have only one query to get both full count and read items count, which
|
||||
a bit is faster as the database only has to deal with one large JOIN.
|
||||
|
||||
Thanks to the cache the number of SQL queries for Collection statistics have
|
||||
reduced by 70%-80%, and average query duration is now between 20 and 80ms
|
||||
depending on average collection size and database used.
|
||||
---
|
||||
server/CMakeLists.txt | 1 +
|
||||
server/src/handler/link.cpp | 2 +-
|
||||
server/src/handler/merge.cpp | 4 +-
|
||||
server/src/handler/select.cpp | 14 ++--
|
||||
server/src/handler/status.cpp | 20 ++---
|
||||
server/src/handlerhelper.cpp | 81 ++------------------
|
||||
server/src/handlerhelper.h | 22 ------
|
||||
server/src/storage/collectionstatistics.cpp | 108 +++++++++++++++++++++++++++
|
||||
server/src/storage/collectionstatistics.h | 70 +++++++++++++++++
|
||||
server/src/storage/datastore.cpp | 8 +-
|
||||
server/src/storage/datastore.h | 6 +-
|
||||
server/src/storage/notificationcollector.cpp | 8 ++
|
||||
server/tests/unittest/fakedatastore.cpp | 8 +-
|
||||
server/tests/unittest/fakedatastore.h | 2 +
|
||||
14 files changed, 224 insertions(+), 130 deletions(-)
|
||||
create mode 100644 server/src/storage/collectionstatistics.cpp
|
||||
create mode 100644 server/src/storage/collectionstatistics.h
|
||||
|
||||
diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt
|
||||
index 275938d..f0e0093 100644
|
||||
--- a/server/CMakeLists.txt
|
||||
+++ b/server/CMakeLists.txt
|
||||
@@ -161,6 +161,7 @@ set(libakonadiprivate_SRCS
|
||||
src/search/searchmanager.cpp
|
||||
|
||||
src/storage/collectionqueryhelper.cpp
|
||||
+ src/storage/collectionstatistics.cpp
|
||||
src/storage/entity.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/entities.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/akonadischema.cpp
|
||||
diff --git a/server/src/handler/link.cpp b/server/src/handler/link.cpp
|
||||
index ce18e47..227de11 100644
|
||||
--- a/server/src/handler/link.cpp
|
||||
+++ b/server/src/handler/link.cpp
|
||||
@@ -25,10 +25,10 @@
|
||||
#include "storage/itemqueryhelper.h"
|
||||
#include "storage/transaction.h"
|
||||
#include "storage/selectquerybuilder.h"
|
||||
+#include "storage/collectionqueryhelper.h"
|
||||
#include "entities.h"
|
||||
|
||||
#include "imapstreamparser.h"
|
||||
-#include <storage/collectionqueryhelper.h>
|
||||
|
||||
using namespace Akonadi::Server;
|
||||
|
||||
diff --git a/server/src/handler/merge.cpp b/server/src/handler/merge.cpp
|
||||
index c26917d..5149916 100644
|
||||
--- a/server/src/handler/merge.cpp
|
||||
+++ b/server/src/handler/merge.cpp
|
||||
@@ -88,7 +88,7 @@ bool Merge::mergeItem( PimItem &newItem, PimItem ¤tItem,
|
||||
if ( !itemFlags.removed.isEmpty() ) {
|
||||
const Flag::List removedFlags = HandlerHelper::resolveFlags( itemFlags.removed );
|
||||
DataStore::self()->removeItemsFlags( PimItem::List() << currentItem, removedFlags,
|
||||
- &flagsRemoved, true );
|
||||
+ &flagsRemoved, col, true );
|
||||
}
|
||||
|
||||
if ( flagsAdded || flagsRemoved ) {
|
||||
@@ -98,7 +98,7 @@ bool Merge::mergeItem( PimItem &newItem, PimItem ¤tItem,
|
||||
bool flagsChanged = false;
|
||||
const Flag::List flags = HandlerHelper::resolveFlags( itemFlags.added );
|
||||
DataStore::self()->setItemsFlags( PimItem::List() << currentItem, flags,
|
||||
- &flagsChanged, true );
|
||||
+ &flagsChanged, col, true );
|
||||
if ( flagsChanged ) {
|
||||
mChangedParts << AKONADI_PARAM_FLAGS;
|
||||
}
|
||||
diff --git a/server/src/handler/select.cpp b/server/src/handler/select.cpp
|
||||
index 1c5dd8a..f1ecc44 100644
|
||||
--- a/server/src/handler/select.cpp
|
||||
+++ b/server/src/handler/select.cpp
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "handlerhelper.h"
|
||||
#include "imapstreamparser.h"
|
||||
#include "storage/selectquerybuilder.h"
|
||||
+#include "storage/collectionstatistics.h"
|
||||
#include "commandcontext.h"
|
||||
|
||||
#include "response.h"
|
||||
@@ -96,19 +97,14 @@ bool Select::parseStream()
|
||||
response.setString( "FLAGS (" + Flag::joinByName( Flag::retrieveAll(), QLatin1String( " " ) ).toLatin1() + ")" );
|
||||
Q_EMIT responseAvailable( response );
|
||||
|
||||
- const int itemCount = HandlerHelper::itemCount( col );
|
||||
- if ( itemCount < 0 ) {
|
||||
+ const CollectionStatistics::Statistics stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ if ( stats.count == -1 ) {
|
||||
return failureResponse( "Unable to determine item count" );
|
||||
}
|
||||
- response.setString( QByteArray::number( itemCount ) + " EXISTS" );
|
||||
+ response.setString( QByteArray::number( stats.count ) + " EXISTS" );
|
||||
Q_EMIT responseAvailable( response );
|
||||
|
||||
- int readCount = HandlerHelper::itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
|
||||
- << QLatin1String( AKONADI_FLAG_IGNORED ) );
|
||||
- if ( readCount < 0 || itemCount < readCount ) {
|
||||
- return failureResponse( "Unable to retrieve unseen count" );
|
||||
- }
|
||||
- response.setString( "OK [UNSEEN " + QByteArray::number( itemCount - readCount ) + "] Message 0 is first unseen" );
|
||||
+ response.setString( "OK [UNSEEN " + QByteArray::number( stats.count - stats.read ) + "] Message 0 is first unseen" );
|
||||
Q_EMIT responseAvailable( response );
|
||||
}
|
||||
|
||||
diff --git a/server/src/handler/status.cpp b/server/src/handler/status.cpp
|
||||
index 8c6823d..283532c 100644
|
||||
--- a/server/src/handler/status.cpp
|
||||
+++ b/server/src/handler/status.cpp
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "storage/datastore.h"
|
||||
#include "storage/entity.h"
|
||||
#include "storage/countquerybuilder.h"
|
||||
+#include "storage/collectionstatistics.h"
|
||||
|
||||
#include "response.h"
|
||||
#include "handlerhelper.h"
|
||||
@@ -62,9 +63,9 @@ bool Status::parseStream()
|
||||
// Responses:
|
||||
// REQUIRED untagged responses: STATUS
|
||||
|
||||
- qint64 itemCount, itemSize;
|
||||
- if ( !HandlerHelper::itemStatistics( col, itemCount, itemSize ) ) {
|
||||
- return failureResponse( "Failed to query statistics." );
|
||||
+ const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ if (stats.count == -1) {
|
||||
+ return failureResponse( "Failed to query statistics." );
|
||||
}
|
||||
|
||||
// build STATUS response
|
||||
@@ -72,7 +73,7 @@ bool Status::parseStream()
|
||||
// MESSAGES - The number of messages in the mailbox
|
||||
if ( attributeList.contains( AKONADI_ATTRIBUTE_MESSAGES ) ) {
|
||||
statusResponse += AKONADI_ATTRIBUTE_MESSAGES " ";
|
||||
- statusResponse += QByteArray::number( itemCount );
|
||||
+ statusResponse += QByteArray::number( stats.count );
|
||||
}
|
||||
|
||||
if ( attributeList.contains( AKONADI_ATTRIBUTE_UNSEEN ) ) {
|
||||
@@ -80,21 +81,14 @@ bool Status::parseStream()
|
||||
statusResponse += " ";
|
||||
}
|
||||
statusResponse += AKONADI_ATTRIBUTE_UNSEEN " ";
|
||||
-
|
||||
- // itemWithFlagCount is twice as fast as itemWithoutFlagCount...
|
||||
- const int count = HandlerHelper::itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
|
||||
- << QLatin1String( AKONADI_FLAG_IGNORED ) );
|
||||
- if ( count < 0 ) {
|
||||
- return failureResponse( "Unable to retrieve unread count" );
|
||||
- }
|
||||
- statusResponse += QByteArray::number( itemCount - count );
|
||||
+ statusResponse += QByteArray::number( stats.count - stats.read );
|
||||
}
|
||||
if ( attributeList.contains( AKONADI_PARAM_SIZE ) ) {
|
||||
if ( !statusResponse.isEmpty() ) {
|
||||
statusResponse += " ";
|
||||
}
|
||||
statusResponse += AKONADI_PARAM_SIZE " ";
|
||||
- statusResponse += QByteArray::number( itemSize );
|
||||
+ statusResponse += QByteArray::number( stats.size );
|
||||
}
|
||||
|
||||
Response response;
|
||||
diff --git a/server/src/handlerhelper.cpp b/server/src/handlerhelper.cpp
|
||||
index 82347b4..39583ce 100644
|
||||
--- a/server/src/handlerhelper.cpp
|
||||
+++ b/server/src/handlerhelper.cpp
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "storage/countquerybuilder.h"
|
||||
#include "storage/datastore.h"
|
||||
#include "storage/selectquerybuilder.h"
|
||||
+#include "storage/collectionstatistics.h"
|
||||
#include "storage/queryhelper.h"
|
||||
#include "libs/imapparser_p.h"
|
||||
#include "libs/protocol_p.h"
|
||||
@@ -78,74 +79,6 @@ QString HandlerHelper::pathForCollection( const Collection &col )
|
||||
return parts.join( QLatin1String( "/" ) );
|
||||
}
|
||||
|
||||
-bool HandlerHelper::itemStatistics( const Collection &col, qint64 &count, qint64 &size )
|
||||
-{
|
||||
- QueryBuilder qb( PimItem::tableName() );
|
||||
- qb.addAggregation( PimItem::idColumn(), QLatin1String( "count" ) );
|
||||
- qb.addAggregation( PimItem::sizeColumn(), QLatin1String( "sum" ) );
|
||||
-
|
||||
- if ( col.isVirtual() ) {
|
||||
- qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
|
||||
- CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
|
||||
- qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
|
||||
- } else {
|
||||
- qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
|
||||
- }
|
||||
-
|
||||
- if ( !qb.exec() ) {
|
||||
- return false;
|
||||
- }
|
||||
- if ( !qb.query().next() ) {
|
||||
- akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
|
||||
- return false;
|
||||
- }
|
||||
- count = qb.query().value( 0 ).toLongLong();
|
||||
- size = qb.query().value( 1 ).toLongLong();
|
||||
- return true;
|
||||
-}
|
||||
-
|
||||
-int HandlerHelper::itemWithFlagsCount( const Collection &col, const QStringList &flags )
|
||||
-{
|
||||
- CountQueryBuilder qb( PimItem::tableName(), PimItem::idFullColumnName(), CountQueryBuilder::Distinct );
|
||||
- qb.addJoin( QueryBuilder::InnerJoin, PimItemFlagRelation::tableName(),
|
||||
- PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName() );
|
||||
- if ( col.isVirtual() ) {
|
||||
- qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
|
||||
- CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName() );
|
||||
- qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id() );
|
||||
- } else {
|
||||
- qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, col.id() );
|
||||
- }
|
||||
- Query::Condition cond( Query::Or );
|
||||
- // We use the below instead of an inner join in the query above because postgres seems
|
||||
- // to struggle to optimize the two inner joins, despite having indices that should
|
||||
- // facilitate that. This exploits the fact that the Flag::retrieveByName is fast because
|
||||
- // it hits an in-memory cache.
|
||||
- Q_FOREACH ( const QString &flag, flags ) {
|
||||
- const Flag f = Flag::retrieveByName( flag );
|
||||
- if (!f.isValid()) {
|
||||
- // since we OR this condition, we can skip invalid flags to speed up the query
|
||||
- continue;
|
||||
- }
|
||||
- cond.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, f.id() );
|
||||
- }
|
||||
- qb.addCondition( cond );
|
||||
- if ( !qb.exec() ) {
|
||||
- return -1;
|
||||
- }
|
||||
- return qb.result();
|
||||
-}
|
||||
-
|
||||
-int HandlerHelper::itemCount( const Collection &col )
|
||||
-{
|
||||
- CountQueryBuilder qb( PimItem::tableName() );
|
||||
- qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() );
|
||||
- if ( !qb.exec() ) {
|
||||
- return -1;
|
||||
- }
|
||||
- return qb.result();
|
||||
-}
|
||||
-
|
||||
int HandlerHelper::parseCachePolicy( const QByteArray &data, Collection &col, int start, bool *changed )
|
||||
{
|
||||
bool inheritChanged = false;
|
||||
@@ -233,14 +166,12 @@ QByteArray HandlerHelper::collectionToByteArray( const Collection &col, bool hid
|
||||
b += " " AKONADI_PARAM_VIRTUAL " " + QByteArray::number( col.isVirtual() ) + ' ';
|
||||
|
||||
if ( includeStatistics ) {
|
||||
- qint64 itemCount, itemSize;
|
||||
- if ( itemStatistics( col, itemCount, itemSize ) ) {
|
||||
- b += AKONADI_ATTRIBUTE_MESSAGES " " + QByteArray::number( itemCount ) + ' ';
|
||||
- // itemWithFlagCount is twice as fast as itemWithoutFlagCount, so emulated that...
|
||||
+ const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ if (stats.count > -1) {
|
||||
+ b += AKONADI_ATTRIBUTE_MESSAGES " " + QByteArray::number( stats.count ) + ' ';
|
||||
b += AKONADI_ATTRIBUTE_UNSEEN " ";
|
||||
- b += QByteArray::number( itemCount - itemWithFlagsCount( col, QStringList() << QLatin1String( AKONADI_FLAG_SEEN )
|
||||
- << QLatin1String( AKONADI_FLAG_IGNORED ) ) );
|
||||
- b += " " AKONADI_PARAM_SIZE " " + QByteArray::number( itemSize ) + ' ';
|
||||
+ b += QByteArray::number( stats.count - stats.read) ;
|
||||
+ b += " " AKONADI_PARAM_SIZE " " + QByteArray::number( stats.size ) + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/server/src/handlerhelper.h b/server/src/handlerhelper.h
|
||||
index 22e6e1c..cf9ac22 100644
|
||||
--- a/server/src/handlerhelper.h
|
||||
+++ b/server/src/handlerhelper.h
|
||||
@@ -52,28 +52,6 @@ class HandlerHelper
|
||||
static QString pathForCollection( const Collection &col );
|
||||
|
||||
/**
|
||||
- Returns the amount of existing items in the given collection.
|
||||
- @return -1 on error
|
||||
- */
|
||||
- static int itemCount( const Collection &col );
|
||||
-
|
||||
- /**
|
||||
- * Queries for collection statistics.
|
||||
- * @param col The collection to query.
|
||||
- * @param count The total amount of items in this collection.
|
||||
- * @param size The size of all items in this collection.
|
||||
- * @return @c false on a query error, @c true otherwise
|
||||
- */
|
||||
- static bool itemStatistics( const Collection &col, qint64 &count, qint64 &size );
|
||||
-
|
||||
- /**
|
||||
- Returns the amount of existing items in the given collection
|
||||
- which have a given flag set.
|
||||
- @return -1 on error.
|
||||
- */
|
||||
- static int itemWithFlagsCount( const Collection &col, const QStringList &flags );
|
||||
-
|
||||
- /**
|
||||
Parse cache policy and update the given Collection object accoordingly.
|
||||
@param changed Indicates whether or not the cache policy already available in @p col
|
||||
has actually changed
|
||||
diff --git a/server/src/storage/collectionstatistics.cpp b/server/src/storage/collectionstatistics.cpp
|
||||
new file mode 100644
|
||||
index 0000000..85ee449
|
||||
--- /dev/null
|
||||
+++ b/server/src/storage/collectionstatistics.cpp
|
||||
@@ -0,0 +1,108 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
+ * License as published by the Free Software Foundation; either
|
||||
+ * version 2.1 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * Lesser General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU Lesser General Public
|
||||
+ * License along with this library; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include "collectionstatistics.h"
|
||||
+#include "querybuilder.h"
|
||||
+#include "countquerybuilder.h"
|
||||
+#include "akdebug.h"
|
||||
+#include "entities.h"
|
||||
+
|
||||
+#include <libs/protocol_p.h>
|
||||
+
|
||||
+#include <QDateTime>
|
||||
+
|
||||
+using namespace Akonadi::Server;
|
||||
+
|
||||
+CollectionStatistics *CollectionStatistics::sInstance = 0;
|
||||
+
|
||||
+CollectionStatistics* CollectionStatistics::instance()
|
||||
+{
|
||||
+ static QMutex lock;
|
||||
+ lock.lock();
|
||||
+ if (sInstance == 0) {
|
||||
+ sInstance = new CollectionStatistics();
|
||||
+ }
|
||||
+ lock.unlock();
|
||||
+ return sInstance;
|
||||
+}
|
||||
+
|
||||
+void CollectionStatistics::invalidateCollection(const Collection &col)
|
||||
+{
|
||||
+ QMutexLocker lock(&mCacheLock);
|
||||
+ mCache.remove(col.id());
|
||||
+}
|
||||
+
|
||||
+const CollectionStatistics::Statistics& CollectionStatistics::statistics(const Collection &col)
|
||||
+{
|
||||
+ QMutexLocker lock(&mCacheLock);
|
||||
+ auto it = mCache.find(col.id());
|
||||
+ if (it == mCache.constEnd()) {
|
||||
+ it = mCache.insert(col.id(), getCollectionStatistics(col));
|
||||
+ }
|
||||
+ return it.value();
|
||||
+}
|
||||
+
|
||||
+CollectionStatistics::Statistics CollectionStatistics::getCollectionStatistics(const Collection &col)
|
||||
+{
|
||||
+ QueryBuilder qb(PimItem::tableName());
|
||||
+ // COUNT(DISTINCT PimItemTable.id)
|
||||
+ qb.addAggregation(QString::fromLatin1("DISTINCT %1")
|
||||
+ .arg(PimItem::idFullColumnName()),
|
||||
+ QLatin1String("count"));
|
||||
+ // SUM(PimItemTable.size)
|
||||
+ qb.addAggregation(PimItem::sizeFullColumnName(), QLatin1String("sum"));
|
||||
+ // SUM(CASE WHEN FlagTable.name IN ('\SEEN', '$IGNORED') THEN 1 ELSE 0 END)
|
||||
+ // This allows us to get read messages count in a single query with the other
|
||||
+ // statistics. It is much than doing two queries, because the database
|
||||
+ // only has to calculate the JOINs once.
|
||||
+ //
|
||||
+ // Flag::retrieveByName() will hit the Entity cache, which allows us to avoid
|
||||
+ // a second JOIN with FlagTable, which PostgreSQL seems to struggle to optimize.
|
||||
+ Query::Condition cond(Query::Or);
|
||||
+ cond.addValueCondition(PimItemFlagRelation::rightFullColumnName(),
|
||||
+ Query::Equals,
|
||||
+ Flag::retrieveByName(QLatin1String(AKONADI_FLAG_SEEN)).id());
|
||||
+ cond.addValueCondition(PimItemFlagRelation::rightFullColumnName(),
|
||||
+ Query::Equals,
|
||||
+ Flag::retrieveByName(QLatin1String(AKONADI_FLAG_IGNORED)).id());
|
||||
+ Query::Case caseStmt(cond, QLatin1String("1"), QLatin1String("0"));
|
||||
+ qb.addAggregation(caseStmt, QLatin1String("sum"));
|
||||
+
|
||||
+ qb.addJoin(QueryBuilder::LeftJoin, PimItemFlagRelation::tableName(),
|
||||
+ PimItem::idFullColumnName(), PimItemFlagRelation::leftFullColumnName());
|
||||
+ if (col.isVirtual()) {
|
||||
+ qb.addJoin(QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
|
||||
+ CollectionPimItemRelation::rightFullColumnName(), PimItem::idFullColumnName());
|
||||
+ qb.addValueCondition(CollectionPimItemRelation::leftFullColumnName(), Query::Equals, col.id());
|
||||
+ } else {
|
||||
+ qb.addValueCondition(PimItem::collectionIdColumn(), Query::Equals, col.id());
|
||||
+ }
|
||||
+
|
||||
+ if (!qb.exec()) {
|
||||
+ return { -1, -1, -1 };
|
||||
+ }
|
||||
+ if (!qb.query().next()) {
|
||||
+ akError() << "Error during retrieving result of statistics query:" << qb.query().lastError().text();
|
||||
+ return { -1, -1, -1 };
|
||||
+ }
|
||||
+
|
||||
+ return { qb.query().value(0).toLongLong(),
|
||||
+ qb.query().value(1).toLongLong(),
|
||||
+ qb.query().value(2).toLongLong() };
|
||||
+}
|
||||
diff --git a/server/src/storage/collectionstatistics.h b/server/src/storage/collectionstatistics.h
|
||||
new file mode 100644
|
||||
index 0000000..2c0af6a
|
||||
--- /dev/null
|
||||
+++ b/server/src/storage/collectionstatistics.h
|
||||
@@ -0,0 +1,70 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
+ * License as published by the Free Software Foundation; either
|
||||
+ * version 2.1 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * Lesser General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU Lesser General Public
|
||||
+ * License along with this library; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef AKONADI_SERVER_COLLECTIONSTATISTICS_H
|
||||
+#define AKONADI_SERVER_COLLECTIONSTATISTICS_H
|
||||
+
|
||||
+class QMutex;
|
||||
+
|
||||
+#include <QHash>
|
||||
+#include <QMutex>
|
||||
+
|
||||
+namespace Akonadi {
|
||||
+namespace Server {
|
||||
+
|
||||
+class Collection;
|
||||
+
|
||||
+/**
|
||||
+ * Provides cache for collection statistics
|
||||
+ *
|
||||
+ * Collection statistics are requested very often, so to take some load from the
|
||||
+ * database we cache the results until the statistics are invalidated (see
|
||||
+ * NotificationCollector, which takes care for invalidating the statistics).
|
||||
+ *
|
||||
+ * The cache (together with optimization of the actual SQL query) seems to
|
||||
+ * massively improve initial folder listing on system start (when IO and CPU loads
|
||||
+ * are very high).
|
||||
+ */
|
||||
+class CollectionStatistics
|
||||
+{
|
||||
+public:
|
||||
+ struct Statistics {
|
||||
+ qint64 count;
|
||||
+ qint64 size;
|
||||
+ qint64 read;
|
||||
+ };
|
||||
+
|
||||
+ static CollectionStatistics* instance();
|
||||
+
|
||||
+ const Statistics& statistics(const Collection &col);
|
||||
+ void invalidateCollection(const Collection &col);
|
||||
+
|
||||
+private:
|
||||
+ Statistics getCollectionStatistics(const Collection &col);
|
||||
+
|
||||
+ QMutex mCacheLock;
|
||||
+ QHash<qint64, Statistics> mCache;
|
||||
+
|
||||
+ static CollectionStatistics *sInstance;
|
||||
+};
|
||||
+
|
||||
+} // namespace Server
|
||||
+} // namespace Akonadi
|
||||
+
|
||||
+#endif // AKONADI_SERVER_COLLECTIONSTATISTICS_H
|
||||
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
|
||||
index 304f0e8..0983d84 100644
|
||||
--- a/server/src/storage/datastore.cpp
|
||||
+++ b/server/src/storage/datastore.cpp
|
||||
@@ -209,7 +209,7 @@ DataStore *DataStore::self()
|
||||
/* --- ItemFlags ----------------------------------------------------- */
|
||||
|
||||
bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
|
||||
- bool *flagsChanged, bool silent )
|
||||
+ bool *flagsChanged, const Collection &col, bool silent )
|
||||
{
|
||||
QSet<QByteArray> removedFlags;
|
||||
QSet<QByteArray> addedFlags;
|
||||
@@ -258,7 +258,7 @@ bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &
|
||||
}
|
||||
|
||||
if ( !silent && ( !addedFlags.isEmpty() || !removedFlags.isEmpty() ) ) {
|
||||
- mNotificationCollector->itemsFlagsChanged( items, addedFlags, removedFlags );
|
||||
+ mNotificationCollector->itemsFlagsChanged( items, addedFlags, removedFlags, col );
|
||||
}
|
||||
|
||||
setBoolPtr( flagsChanged, ( addedFlags != removedFlags ) );
|
||||
@@ -361,7 +361,7 @@ bool DataStore::appendItemsFlags( const PimItem::List &items, const QVector<Flag
|
||||
}
|
||||
|
||||
bool DataStore::removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
|
||||
- bool *flagsChanged, bool silent )
|
||||
+ bool *flagsChanged, const Collection &col, bool silent )
|
||||
{
|
||||
QSet<QByteArray> removedFlags;
|
||||
QVariantList itemsIds;
|
||||
@@ -393,7 +393,7 @@ bool DataStore::removeItemsFlags( const PimItem::List &items, const QVector<Flag
|
||||
if ( qb.query().numRowsAffected() != 0 ) {
|
||||
setBoolPtr( flagsChanged, true );
|
||||
if ( !silent ) {
|
||||
- mNotificationCollector->itemsFlagsChanged( items, QSet<QByteArray>(), removedFlags );
|
||||
+ mNotificationCollector->itemsFlagsChanged( items, QSet<QByteArray>(), removedFlags, col );
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h
|
||||
index 395b227..a2d8a42 100644
|
||||
--- a/server/src/storage/datastore.h
|
||||
+++ b/server/src/storage/datastore.h
|
||||
@@ -119,10 +119,12 @@ class DataStore : public QObject
|
||||
static DataStore *self();
|
||||
|
||||
/* --- ItemFlags ----------------------------------------------------- */
|
||||
- virtual bool setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *flagsChanged = 0, bool silent = false );
|
||||
+ virtual bool setItemsFlags( const PimItem::List &items, const QVector<Flag> &flags,
|
||||
+ bool *flagsChanged = 0, const Collection &col = Collection(), bool silent = false );
|
||||
virtual bool appendItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *flagsChanged = 0,
|
||||
bool checkIfExists = true, const Collection &col = Collection(), bool silent = false );
|
||||
- virtual bool removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *tagsChanged = 0, bool silent = false );
|
||||
+ virtual bool removeItemsFlags( const PimItem::List &items, const QVector<Flag> &flags, bool *tagsChanged = 0,
|
||||
+ const Collection &collection = Collection(), bool silent = false );
|
||||
|
||||
/* --- ItemTags ----------------------------------------------------- */
|
||||
virtual bool setItemsTags( const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = 0, bool silent = false );
|
||||
diff --git a/server/src/storage/notificationcollector.cpp b/server/src/storage/notificationcollector.cpp
|
||||
index 67f57d1..dbc7883 100644
|
||||
--- a/server/src/storage/notificationcollector.cpp
|
||||
+++ b/server/src/storage/notificationcollector.cpp
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "notificationcollector.h"
|
||||
#include "storage/datastore.h"
|
||||
#include "storage/entity.h"
|
||||
+#include "storage/collectionstatistics.h"
|
||||
#include "handlerhelper.h"
|
||||
#include "cachecleaner.h"
|
||||
#include "intervalcheck.h"
|
||||
@@ -133,6 +134,7 @@ void NotificationCollector::collectionChanged( const Collection &collection,
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionAdded( collection.id() );
|
||||
}
|
||||
+ CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
collectionNotification( NotificationMessageV2::Modify, collection, collection.parentId(), -1, resource, changes.toSet() );
|
||||
}
|
||||
|
||||
@@ -159,6 +161,8 @@ void NotificationCollector::collectionRemoved( const Collection &collection,
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
|
||||
}
|
||||
+ CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
+
|
||||
collectionNotification( NotificationMessageV2::Remove, collection, collection.parentId(), -1, resource );
|
||||
}
|
||||
|
||||
@@ -183,6 +187,8 @@ void NotificationCollector::collectionUnsubscribed( const Collection &collection
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
|
||||
}
|
||||
+ CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
+
|
||||
collectionNotification( NotificationMessageV2::Unsubscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>() );
|
||||
}
|
||||
|
||||
@@ -282,6 +288,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
|
||||
copy.setParentCollection( iter.key() );
|
||||
copy.setResource( resource );
|
||||
|
||||
+ CollectionStatistics::instance()->invalidateCollection(Collection::retrieveById(iter.key()));
|
||||
dispatchNotification( copy );
|
||||
}
|
||||
|
||||
@@ -304,6 +311,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
|
||||
}
|
||||
msg.setResource( res );
|
||||
|
||||
+ CollectionStatistics::instance()->invalidateCollection(col);
|
||||
dispatchNotification( msg );
|
||||
}
|
||||
|
||||
diff --git a/server/tests/unittest/fakedatastore.cpp b/server/tests/unittest/fakedatastore.cpp
|
||||
index 12214fa..43ef7e6 100644
|
||||
--- a/server/tests/unittest/fakedatastore.cpp
|
||||
+++ b/server/tests/unittest/fakedatastore.cpp
|
||||
@@ -91,13 +91,15 @@ bool FakeDataStore::init()
|
||||
bool FakeDataStore::setItemsFlags( const PimItem::List &items,
|
||||
const QVector<Flag> &flags,
|
||||
bool *flagsChanged,
|
||||
+ const Collection &col,
|
||||
bool silent )
|
||||
{
|
||||
mChanges.insert( QLatin1String( "setItemsFlags" ),
|
||||
QVariantList() << QVariant::fromValue( items )
|
||||
<< QVariant::fromValue( flags )
|
||||
+ << QVariant::fromValue( col )
|
||||
<< silent );
|
||||
- return DataStore::setItemsFlags( items, flags, flagsChanged, silent );
|
||||
+ return DataStore::setItemsFlags( items, flags, flagsChanged, col, silent );
|
||||
}
|
||||
|
||||
bool FakeDataStore::appendItemsFlags( const PimItem::List &items,
|
||||
@@ -119,13 +121,15 @@ bool FakeDataStore::appendItemsFlags( const PimItem::List &items,
|
||||
bool FakeDataStore::removeItemsFlags( const PimItem::List &items,
|
||||
const QVector<Flag> &flags,
|
||||
bool *flagsChanged,
|
||||
+ const Collection &col,
|
||||
bool silent )
|
||||
{
|
||||
mChanges.insert( QLatin1String( "removeItemsFlags" ),
|
||||
QVariantList() << QVariant::fromValue( items )
|
||||
<< QVariant::fromValue( flags )
|
||||
+ << QVariant::fromValue( col )
|
||||
<< silent );
|
||||
- return DataStore::removeItemsFlags( items, flags, flagsChanged, silent );
|
||||
+ return DataStore::removeItemsFlags( items, flags, flagsChanged, col, silent );
|
||||
}
|
||||
|
||||
|
||||
diff --git a/server/tests/unittest/fakedatastore.h b/server/tests/unittest/fakedatastore.h
|
||||
index 62c5b75..cd9ab13 100644
|
||||
--- a/server/tests/unittest/fakedatastore.h
|
||||
+++ b/server/tests/unittest/fakedatastore.h
|
||||
@@ -41,6 +41,7 @@ class FakeDataStore: public DataStore
|
||||
virtual bool setItemsFlags( const PimItem::List &items,
|
||||
const QVector<Flag> &flags,
|
||||
bool *flagsChanged = 0,
|
||||
+ const Collection &col = Collection(),
|
||||
bool silent = false );
|
||||
virtual bool appendItemsFlags( const PimItem::List &items,
|
||||
const QVector<Flag> &flags,
|
||||
@@ -51,6 +52,7 @@ class FakeDataStore: public DataStore
|
||||
virtual bool removeItemsFlags( const PimItem::List &items,
|
||||
const QVector<Flag> &flags,
|
||||
bool *flagsChanged = 0,
|
||||
+ const Collection &col = Collection(),
|
||||
bool silent = false );
|
||||
|
||||
virtual bool setItemsTags( const PimItem::List &items,
|
||||
--
|
||||
2.1.0
|
||||
|
||||
105
0023-Always-create-a-new-PartType-when-it-does-not-exist.patch
Normal file
105
0023-Always-create-a-new-PartType-when-it-does-not-exist.patch
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
From 1ce732668b2b3e4d735665bd60e1a18f139b1de2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 5 Dec 2014 18:49:15 +0100
|
||||
Subject: [PATCH 23/30] Always create a new PartType when it does not exist
|
||||
|
||||
Fixes a regression introduced in previous commit that caused Part operations
|
||||
to fail when specified PartType did not exist in Akonadi storage yet.
|
||||
---
|
||||
server/src/handler/append.cpp | 4 ++--
|
||||
server/src/storage/datastore.cpp | 2 +-
|
||||
server/src/storage/parttypehelper.cpp | 16 +++++++++++++++-
|
||||
server/src/storage/parttypehelper.h | 8 ++++++++
|
||||
4 files changed, 26 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/server/src/handler/append.cpp b/server/src/handler/append.cpp
|
||||
index b594e27..15fb9ea 100644
|
||||
--- a/server/src/handler/append.cpp
|
||||
+++ b/server/src/handler/append.cpp
|
||||
@@ -134,7 +134,7 @@ bool Append::commit()
|
||||
|
||||
// wrap data into a part
|
||||
Part part;
|
||||
- part.setPartType( PartType::retrieveByFQName( QLatin1String("PLD"), QLatin1String("RFC822") ) );
|
||||
+ part.setPartType( PartTypeHelper::fromFqName( QLatin1String("PLD"), QLatin1String("RFC822") ) );
|
||||
part.setData( m_data );
|
||||
part.setPimItemId( item.id() );
|
||||
part.setDatasize( dataSize );
|
||||
@@ -148,7 +148,7 @@ bool Append::commit()
|
||||
//akDebug() << "Append handler: doPreprocessing is" << doPreprocessing;
|
||||
if ( doPreprocessing ) {
|
||||
Part hiddenAttribute;
|
||||
- hiddenAttribute.setPartType( PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ) );
|
||||
+ hiddenAttribute.setPartType( PartTypeHelper::fromFqName( QLatin1String("ATR"), QLatin1String("HIDDEN") ) );
|
||||
hiddenAttribute.setData( QByteArray() );
|
||||
hiddenAttribute.setPimItemId( item.id() );
|
||||
hiddenAttribute.setDatasize( 0 );
|
||||
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
|
||||
index 0983d84..c9fa0c3 100644
|
||||
--- a/server/src/storage/datastore.cpp
|
||||
+++ b/server/src/storage/datastore.cpp
|
||||
@@ -1027,7 +1027,7 @@ bool DataStore::unhideAllPimItems()
|
||||
|
||||
try {
|
||||
return PartHelper::remove( Part::partTypeIdFullColumnName(),
|
||||
- PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ).id() );
|
||||
+ PartTypeHelper::fromFqName( QLatin1String("ATR"), QLatin1String("HIDDEN") ).id() );
|
||||
} catch ( ... ) {} // we can live with this failing
|
||||
|
||||
return false;
|
||||
diff --git a/server/src/storage/parttypehelper.cpp b/server/src/storage/parttypehelper.cpp
|
||||
index 7654108..bcff9c6 100644
|
||||
--- a/server/src/storage/parttypehelper.cpp
|
||||
+++ b/server/src/storage/parttypehelper.cpp
|
||||
@@ -37,7 +37,8 @@ QPair< QString, QString > PartTypeHelper::parseFqName(const QString& fqName)
|
||||
PartType PartTypeHelper::fromFqName(const QString& fqName)
|
||||
{
|
||||
const QPair<QString, QString> p = parseFqName( fqName );
|
||||
- return PartType::retrieveByFQName(p.first, p.second);
|
||||
+ return fromFqName(p.first, p.second);
|
||||
+
|
||||
}
|
||||
|
||||
PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
|
||||
@@ -45,6 +46,19 @@ PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
|
||||
return fromFqName( QLatin1String(fqName) );
|
||||
}
|
||||
|
||||
+PartType PartTypeHelper::fromFqName(const QString& ns, const QString& name)
|
||||
+{
|
||||
+ PartType partType = PartType::retrieveByFQName(ns, name);
|
||||
+ if (!partType.isValid()) {
|
||||
+ PartType pt(name, ns);
|
||||
+ if (!pt.insert()) {
|
||||
+ throw PartTypeException( "Failed to append part type" );
|
||||
+ }
|
||||
+ partType = pt;
|
||||
+ }
|
||||
+ return partType;
|
||||
+}
|
||||
+
|
||||
Query::Condition PartTypeHelper::conditionFromFqName(const QString& fqName)
|
||||
{
|
||||
const QPair<QString, QString> p = parseFqName( fqName );
|
||||
diff --git a/server/src/storage/parttypehelper.h b/server/src/storage/parttypehelper.h
|
||||
index 4c4f42f..6d3cf74 100644
|
||||
--- a/server/src/storage/parttypehelper.h
|
||||
+++ b/server/src/storage/parttypehelper.h
|
||||
@@ -48,6 +48,14 @@ namespace PartTypeHelper
|
||||
PartType fromFqName( const QByteArray &fqName );
|
||||
|
||||
/**
|
||||
+ * Retrieve (or create) PartType for the given namespace and name
|
||||
+ * @param ns Namespace
|
||||
+ * @param name Name
|
||||
+ * @throws PartTypeException
|
||||
+ */
|
||||
+ PartType fromFqName( const QString &ns, const QString &name );
|
||||
+
|
||||
+ /**
|
||||
* Returns a query condition that matches the given part.
|
||||
* @param fqName fully-qualified part type name
|
||||
* @throws PartTypeException
|
||||
--
|
||||
2.1.0
|
||||
|
||||
25
0024-Fix-compilation-with-strict-iterators.patch
Normal file
25
0024-Fix-compilation-with-strict-iterators.patch
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
From fcae659e9be22b00b0efe52f19a89b8fce26a925 Mon Sep 17 00:00:00 2001
|
||||
From: David Faure <faure@kde.org>
|
||||
Date: Sat, 6 Dec 2014 11:35:16 +0100
|
||||
Subject: [PATCH 24/30] Fix compilation with strict iterators
|
||||
|
||||
---
|
||||
server/src/storage/collectionstatistics.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/src/storage/collectionstatistics.cpp b/server/src/storage/collectionstatistics.cpp
|
||||
index 85ee449..b2c6915 100644
|
||||
--- a/server/src/storage/collectionstatistics.cpp
|
||||
+++ b/server/src/storage/collectionstatistics.cpp
|
||||
@@ -52,7 +52,7 @@ const CollectionStatistics::Statistics& CollectionStatistics::statistics(const C
|
||||
{
|
||||
QMutexLocker lock(&mCacheLock);
|
||||
auto it = mCache.find(col.id());
|
||||
- if (it == mCache.constEnd()) {
|
||||
+ if (it == mCache.end()) {
|
||||
it = mCache.insert(col.id(), getCollectionStatistics(col));
|
||||
}
|
||||
return it.value();
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
From 55dc6d141a20e2438308214ab60c18e282dd7b43 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 8 Dec 2014 10:33:51 +0100
|
||||
Subject: [PATCH 25/30] Avoid repeated calls to PimItem::flags() and
|
||||
PimItem::tags()
|
||||
|
||||
The queries results are not cached, so each call to those methods runs an SQL
|
||||
query. At least in case of flags, this reduced the number of queries to one
|
||||
query per changed item.
|
||||
---
|
||||
server/src/storage/datastore.cpp | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
|
||||
index c9fa0c3..035395e 100644
|
||||
--- a/server/src/storage/datastore.cpp
|
||||
+++ b/server/src/storage/datastore.cpp
|
||||
@@ -220,7 +220,8 @@ bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &
|
||||
setBoolPtr( flagsChanged, false );
|
||||
|
||||
Q_FOREACH ( const PimItem &item, items ) {
|
||||
- Q_FOREACH ( const Flag &flag, item.flags() ) {
|
||||
+ const Flag::List itemFlags = item.flags();
|
||||
+ Q_FOREACH ( const Flag &flag, itemFlags ) {
|
||||
if ( !flags.contains( flag ) ) {
|
||||
removedFlags << flag.name().toLatin1();
|
||||
Query::Condition cond;
|
||||
@@ -231,7 +232,7 @@ bool DataStore::setItemsFlags( const PimItem::List &items, const QVector<Flag> &
|
||||
}
|
||||
|
||||
Q_FOREACH ( const Flag &flag, flags ) {
|
||||
- if ( !item.flags().contains( flag ) ) {
|
||||
+ if ( !itemFlags.contains( flag ) ) {
|
||||
addedFlags << flag.name().toLatin1();
|
||||
insIds << item.id();
|
||||
insFlags << flag.id();
|
||||
@@ -414,7 +415,8 @@ bool DataStore::setItemsTags( const PimItem::List &items, const Tag::List &tags,
|
||||
setBoolPtr( tagsChanged, false );
|
||||
|
||||
Q_FOREACH ( const PimItem &item, items ) {
|
||||
- Q_FOREACH ( const Tag &tag, item.tags() ) {
|
||||
+ const Tag::List itemTags = item.tags();
|
||||
+ Q_FOREACH ( const Tag &tag, itemTags ) {
|
||||
if ( !tags.contains( tag ) ) {
|
||||
// Remove tags from items that had it set
|
||||
removedTags << tag.id();
|
||||
@@ -426,7 +428,7 @@ bool DataStore::setItemsTags( const PimItem::List &items, const Tag::List &tags,
|
||||
}
|
||||
|
||||
Q_FOREACH ( const Tag &tag, tags ) {
|
||||
- if ( !item.tags().contains( tag ) ) {
|
||||
+ if ( !itemTags.contains( tag ) ) {
|
||||
// Add tags to items that did not have the tag
|
||||
addedTags << tag.id();
|
||||
insIds << item.id();
|
||||
--
|
||||
2.1.0
|
||||
|
||||
374
0026-Avoid-recursive-collection-listing-in-SearchHelper.patch
Normal file
374
0026-Avoid-recursive-collection-listing-in-SearchHelper.patch
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
From 059d52845cbbc10e882764f64245c5995af4e741 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 8 Dec 2014 13:49:27 +0100
|
||||
Subject: [PATCH 26/30] Avoid recursive collection listing in SearchHelper
|
||||
|
||||
The recursive listing generates one SQL query per collection, and since search
|
||||
is invoked rather often (basically whenever you open an email in KMail), we get
|
||||
lots of unnecessary queries. This new algorithm does one query to fetch all
|
||||
folders with matching mime types, and only falls back to query the parent chain
|
||||
when the requested ancestor is not 0 (root), or when the result collection is
|
||||
not a direct descendant of the requested ancestor.
|
||||
---
|
||||
server/src/handler/search.cpp | 4 +-
|
||||
server/src/handler/searchhelper.cpp | 111 ++++++++++++++----------
|
||||
server/src/handler/searchhelper.h | 2 +-
|
||||
server/src/search/searchmanager.cpp | 2 +-
|
||||
server/tests/unittest/CMakeLists.txt | 2 +
|
||||
server/tests/unittest/searchtest.cpp | 158 +++++++++++++++++++++++++++++++++++
|
||||
6 files changed, 230 insertions(+), 49 deletions(-)
|
||||
create mode 100644 server/tests/unittest/searchtest.cpp
|
||||
|
||||
diff --git a/server/src/handler/search.cpp b/server/src/handler/search.cpp
|
||||
index 06d172f..00484ff 100644
|
||||
--- a/server/src/handler/search.cpp
|
||||
+++ b/server/src/handler/search.cpp
|
||||
@@ -95,9 +95,7 @@ bool Search::parseStream()
|
||||
}
|
||||
|
||||
if ( recursive ) {
|
||||
- Q_FOREACH ( qint64 collection, collectionIds ) {
|
||||
- collections << SearchHelper::listCollectionsRecursive( QVector<qint64>() << collection, mimeTypes );
|
||||
- }
|
||||
+ collections << SearchHelper::matchSubcollectionsByMimeType( collectionIds, mimeTypes );
|
||||
} else {
|
||||
collections = collectionIds;
|
||||
}
|
||||
diff --git a/server/src/handler/searchhelper.cpp b/server/src/handler/searchhelper.cpp
|
||||
index aa6694d..1a06c0e 100644
|
||||
--- a/server/src/handler/searchhelper.cpp
|
||||
+++ b/server/src/handler/searchhelper.cpp
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "searchhelper.h"
|
||||
#include "storage/countquerybuilder.h"
|
||||
+#include <storage/queryhelper.h>
|
||||
#include "entities.h"
|
||||
|
||||
#include <libs/protocol_p.h>
|
||||
@@ -89,55 +90,77 @@ QString SearchHelper::extractMimetype( const QList<QByteArray> &junks, int start
|
||||
}
|
||||
|
||||
|
||||
-QVector<qint64> SearchHelper::listCollectionsRecursive( const QVector<qint64> &ancestors, const QStringList &mimeTypes )
|
||||
+static qint64 parentCollectionId(qint64 collectionId)
|
||||
{
|
||||
- QVector<qint64> recursiveChildren;
|
||||
- Q_FOREACH ( qint64 ancestor, ancestors ) {
|
||||
- QVector<qint64> searchChildren;
|
||||
-
|
||||
- { // Free the query before entering recursion to prevent too many opened connections
|
||||
-
|
||||
- Query::Condition mimeTypeCondition;
|
||||
- mimeTypeCondition.addColumnCondition( CollectionMimeTypeRelation::rightFullColumnName(), Query::Equals, MimeType::idFullColumnName() );
|
||||
- // Exclude top-level collections and collections that cannot have items!
|
||||
- mimeTypeCondition.addValueCondition( MimeType::nameFullColumnName(), Query::NotEquals, QLatin1String( "inode/directory" ) );
|
||||
- if ( !mimeTypes.isEmpty() ) {
|
||||
- mimeTypeCondition.addValueCondition( MimeType::nameFullColumnName(), Query::In, mimeTypes );
|
||||
- }
|
||||
+ QueryBuilder qb(Collection::tableName(), QueryBuilder::Select);
|
||||
+ qb.addColumn(Collection::parentIdColumn());
|
||||
+ qb.addValueCondition(Collection::idColumn(), Query::Equals, collectionId);
|
||||
+ if (!qb.exec()) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if (!qb.query().next()) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return qb.query().value(0).toLongLong();
|
||||
+}
|
||||
|
||||
- CountQueryBuilder qb( Collection::tableName(), MimeType::nameFullColumnName(), CountQueryBuilder::All );
|
||||
- qb.addColumn( Collection::idFullColumnName() );
|
||||
- qb.addJoin( QueryBuilder::LeftJoin, CollectionMimeTypeRelation::tableName(), CollectionMimeTypeRelation::leftFullColumnName(), Collection::idFullColumnName() );
|
||||
- qb.addJoin( QueryBuilder::LeftJoin, MimeType::tableName(), mimeTypeCondition );
|
||||
- if ( ancestor == 0 ) {
|
||||
- qb.addValueCondition( Collection::parentIdFullColumnName(), Query::Is, QVariant() );
|
||||
- } else {
|
||||
- // Also include current ancestor's result, so that we know whether we should search in the ancestor too
|
||||
- Query::Condition idCond( Query::Or );
|
||||
- idCond.addValueCondition( Collection::parentIdFullColumnName(), Query::Equals, ancestor );
|
||||
- idCond.addValueCondition( Collection::idFullColumnName(), Query::Equals, ancestor );
|
||||
- qb.addCondition( idCond );
|
||||
- }
|
||||
- qb.addValueCondition( Collection::isVirtualFullColumnName(), Query::Equals, false );
|
||||
- qb.addGroupColumn( Collection::idFullColumnName() );
|
||||
- qb.exec();
|
||||
-
|
||||
- QSqlQuery query = qb.query();
|
||||
- while ( query.next() ) {
|
||||
- const qint64 id = query.value( 1 ).toLongLong();
|
||||
- // Don't add ancestor into search children, we are resolving it right now
|
||||
- if ( id != ancestor ) {
|
||||
- searchChildren << id;
|
||||
+
|
||||
+QVector<qint64> SearchHelper::matchSubcollectionsByMimeType(const QVector<qint64> &ancestors, const QStringList &mimeTypes)
|
||||
+{
|
||||
+ // Get all collections with given mime types
|
||||
+ QueryBuilder qb(Collection::tableName(), QueryBuilder::Select);
|
||||
+ qb.setDistinct(true);
|
||||
+ qb.addColumn(Collection::idFullColumnName());
|
||||
+ qb.addColumn(Collection::parentIdFullColumnName());
|
||||
+ qb.addJoin(QueryBuilder::LeftJoin, CollectionMimeTypeRelation::tableName(),
|
||||
+ CollectionMimeTypeRelation::leftFullColumnName(), Collection::idFullColumnName());
|
||||
+ qb.addJoin(QueryBuilder::LeftJoin, MimeType::tableName(),
|
||||
+ CollectionMimeTypeRelation::rightFullColumnName(), MimeType::idFullColumnName());
|
||||
+ Query::Condition cond(Query::Or);
|
||||
+ Q_FOREACH (const QString &mt, mimeTypes) {
|
||||
+ cond.addValueCondition(MimeType::nameFullColumnName(), Query::Equals, mt);
|
||||
+ }
|
||||
+ qb.addCondition(cond);
|
||||
+
|
||||
+ if (!qb.exec()) {
|
||||
+ qWarning() << "Failed to query search collections";
|
||||
+ return QVector<qint64>();
|
||||
+ }
|
||||
+
|
||||
+ QMap<qint64 /* parentId */, QVector<qint64> /* collectionIds */> candidateCollections;
|
||||
+ while (qb.query().next()) {
|
||||
+ candidateCollections[qb.query().value(1).toLongLong()].append(qb.query().value(0).toLongLong());
|
||||
+ }
|
||||
+
|
||||
+ // If the ancestors list contains root, then return what we got, since everything
|
||||
+ // is sub collection of root
|
||||
+ QVector<qint64> results;
|
||||
+ if (ancestors.contains(0)) {
|
||||
+ Q_FOREACH (const QVector<qint64> &res, candidateCollections.values()) {
|
||||
+ results += res;
|
||||
}
|
||||
- if ( query.value( 0 ).toInt() > 0 ) { // count( mimeTypeTable.name ) > 0
|
||||
- recursiveChildren << id;
|
||||
+ return results;
|
||||
+ }
|
||||
+
|
||||
+ // Try to resolve direct descendants
|
||||
+ Q_FOREACH (qint64 ancestor, ancestors) {
|
||||
+ const QVector<qint64> cols = candidateCollections.take(ancestor);
|
||||
+ if (!cols.isEmpty()) {
|
||||
+ results += cols;
|
||||
}
|
||||
- }
|
||||
}
|
||||
- if ( !searchChildren.isEmpty() ) {
|
||||
- recursiveChildren << listCollectionsRecursive( searchChildren, mimeTypes );
|
||||
+
|
||||
+ for (auto iter = candidateCollections.begin(); iter != candidateCollections.end(); ++iter) {
|
||||
+ // Traverse the collection chain up to root
|
||||
+ qint64 parentId = iter.key();
|
||||
+ while (!ancestors.contains(parentId) && parentId > 0) {
|
||||
+ parentId = parentCollectionId(parentId);
|
||||
+ }
|
||||
+ // Ok, we found a requested ancestor in the parent chain
|
||||
+ if (parentId > 0) {
|
||||
+ results += iter.value();
|
||||
+ }
|
||||
}
|
||||
- }
|
||||
|
||||
- return recursiveChildren;
|
||||
+ return results;
|
||||
}
|
||||
diff --git a/server/src/handler/searchhelper.h b/server/src/handler/searchhelper.h
|
||||
index a64bb61..1595501 100644
|
||||
--- a/server/src/handler/searchhelper.h
|
||||
+++ b/server/src/handler/searchhelper.h
|
||||
@@ -33,7 +33,7 @@ class SearchHelper
|
||||
public:
|
||||
static QList<QByteArray> splitLine( const QByteArray &line );
|
||||
static QString extractMimetype( const QList<QByteArray> &junks, int start );
|
||||
- static QVector<qint64> listCollectionsRecursive( const QVector<qint64> &ancestors, const QStringList &mimeTypes );
|
||||
+ static QVector<qint64> matchSubcollectionsByMimeType( const QVector<qint64> &ancestors, const QStringList &mimeTypes );
|
||||
};
|
||||
|
||||
} // namespace Server
|
||||
diff --git a/server/src/search/searchmanager.cpp b/server/src/search/searchmanager.cpp
|
||||
index c821aa3..b940fcc 100644
|
||||
--- a/server/src/search/searchmanager.cpp
|
||||
+++ b/server/src/search/searchmanager.cpp
|
||||
@@ -296,7 +296,7 @@ void SearchManager::updateSearchImpl( const Collection &collection, QWaitConditi
|
||||
}
|
||||
|
||||
if ( recursive ) {
|
||||
- queryCollections = SearchHelper::listCollectionsRecursive( queryAncestors, queryMimeTypes );
|
||||
+ queryCollections = SearchHelper::matchSubcollectionsByMimeType( queryAncestors, queryMimeTypes );
|
||||
} else {
|
||||
queryCollections = queryAncestors;
|
||||
}
|
||||
diff --git a/server/tests/unittest/CMakeLists.txt b/server/tests/unittest/CMakeLists.txt
|
||||
index b9744d9..acdc180 100644
|
||||
--- a/server/tests/unittest/CMakeLists.txt
|
||||
+++ b/server/tests/unittest/CMakeLists.txt
|
||||
@@ -77,3 +77,5 @@ add_server_test(listhandlertest.cpp akonadiprivate)
|
||||
add_server_test(modifyhandlertest.cpp akonadiprivate)
|
||||
add_server_test(createhandlertest.cpp akonadiprivate)
|
||||
add_server_test(collectionreferencetest.cpp akonadiprivate)
|
||||
+
|
||||
+add_server_test(searchtest.cpp akonadiprivate)
|
||||
\ No newline at end of file
|
||||
diff --git a/server/tests/unittest/searchtest.cpp b/server/tests/unittest/searchtest.cpp
|
||||
new file mode 100644
|
||||
index 0000000..f523b09
|
||||
--- /dev/null
|
||||
+++ b/server/tests/unittest/searchtest.cpp
|
||||
@@ -0,0 +1,158 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
+ * License as published by the Free Software Foundation; either
|
||||
+ * version 2.1 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * Lesser General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU Lesser General Public
|
||||
+ * License along with this library; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include "fakeakonadiserver.h"
|
||||
+#include "searchhelper.h"
|
||||
+#include "akdebug.h"
|
||||
+#include "aktest.h"
|
||||
+
|
||||
+#include <entities.h>
|
||||
+
|
||||
+#include <QTest>
|
||||
+
|
||||
+using namespace Akonadi::Server;
|
||||
+
|
||||
+Q_DECLARE_METATYPE(QList<qint64>)
|
||||
+Q_DECLARE_METATYPE(QList<QString>)
|
||||
+
|
||||
+
|
||||
+class SearchTest : public QObject
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+
|
||||
+public:
|
||||
+ SearchTest()
|
||||
+ : QObject()
|
||||
+ {
|
||||
+ try {
|
||||
+ FakeAkonadiServer::instance()->setPopulateDb(false);
|
||||
+ FakeAkonadiServer::instance()->init();
|
||||
+ } catch (const FakeAkonadiServerException &e) {
|
||||
+ akError() << "Server exception: " << e.what();
|
||||
+ akFatal() << "Fake Akonadi Server failed to start up, aborting test";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ~SearchTest()
|
||||
+ {
|
||||
+ FakeAkonadiServer::instance()->quit();
|
||||
+ }
|
||||
+
|
||||
+ Collection createCollection(const Resource &res, const QString &name, const Collection &parent, const QStringList &mimetypes)
|
||||
+ {
|
||||
+ Collection col;
|
||||
+ col.setName(name);
|
||||
+ col.setResource(res);
|
||||
+ col.setParentId(parent.isValid() ? parent.id() : 0);
|
||||
+ col.insert();
|
||||
+ Q_FOREACH (const QString &mimeType, mimetypes) {
|
||||
+ MimeType mt = MimeType::retrieveByName(mimeType);
|
||||
+ if (!mt.isValid()) {
|
||||
+ mt = MimeType(mimeType);
|
||||
+ mt.insert();
|
||||
+ }
|
||||
+ col.addMimeType(mt);
|
||||
+ }
|
||||
+ return col;
|
||||
+ }
|
||||
+
|
||||
+private Q_SLOTS:
|
||||
+ void testSearchHelperCollectionListing_data()
|
||||
+ {
|
||||
+ /*
|
||||
+ Fake Resource
|
||||
+ |- Col 1 (inode/directory)
|
||||
+ | |- Col 2 (inode/direcotry, application/octet-stream)
|
||||
+ | | |- Col 3(application/octet-stream)
|
||||
+ | |- Col 4 (text/plain)
|
||||
+ |- Col 5 (inode/directory, text/plain)
|
||||
+ |- Col 6 (inode/directory, application/octet-stream)
|
||||
+ |- Col 7 (inode/directory, text/plain)
|
||||
+ |- Col 8 (inode/directory, application/octet-stream)
|
||||
+ |- Col 9 (unique/mime-type)
|
||||
+ */
|
||||
+
|
||||
+ Resource res(QLatin1String("Test Resource"), false);
|
||||
+ res.insert();
|
||||
+
|
||||
+ Collection col1 = createCollection(res, QLatin1String("Col 1"), Collection(),
|
||||
+ QStringList() << QLatin1String("inode/directory"));
|
||||
+ Collection col2 = createCollection(res, QLatin1String("Col 2"), col1,
|
||||
+ QStringList() << QLatin1String("inode/directory")
|
||||
+ << QLatin1String("application/octet-stream"));
|
||||
+ Collection col3 = createCollection(res, QLatin1String("Col 3"), col2,
|
||||
+ QStringList() << QLatin1String("application/octet-stream"));
|
||||
+ Collection col4 = createCollection(res, QLatin1String("Col 4"), col2,
|
||||
+ QStringList() << QLatin1String("text/plain"));
|
||||
+ Collection col5 = createCollection(res, QLatin1String("Col 5"), Collection(),
|
||||
+ QStringList() << QLatin1String("inode/directory")
|
||||
+ << QLatin1String("text/plain"));
|
||||
+ Collection col6 = createCollection(res, QLatin1String("Col 6"), col5,
|
||||
+ QStringList() << QLatin1String("inode/directory")
|
||||
+ << QLatin1String("application/octet-stream"));
|
||||
+ Collection col7 = createCollection(res, QLatin1String("Col 7"), col5,
|
||||
+ QStringList() << QLatin1String("inode/directory")
|
||||
+ << QLatin1String("text/plain"));
|
||||
+ Collection col8 = createCollection(res, QLatin1String("Col 8"), col7,
|
||||
+ QStringList() << QLatin1String("text/directory")
|
||||
+ << QLatin1String("application/octet-stream"));
|
||||
+ Collection col9 = createCollection(res, QLatin1String("Col 9"), col8,
|
||||
+ QStringList() << QLatin1String("unique/mime-type"));
|
||||
+
|
||||
+ QTest::addColumn<QVector<qint64>>("ancestors");
|
||||
+ QTest::addColumn<QStringList>("mimetypes");
|
||||
+ QTest::addColumn<QVector<qint64>>("expectedResults");
|
||||
+
|
||||
+ QTest::newRow("") << QVector<qint64>({ 0 })
|
||||
+ << QStringList({ QLatin1String("text/plain") })
|
||||
+ << QVector<qint64>({ col4.id(), col5.id(), col7.id() });
|
||||
+ QTest::newRow("") << QVector<qint64>({ 0 })
|
||||
+ << QStringList({ QLatin1String("application/octet-stream") })
|
||||
+ << QVector<qint64>({ col2.id(), col3.id(), col6.id(), col8.id() });
|
||||
+ QTest::newRow("") << QVector<qint64>({ col1.id() })
|
||||
+ << QStringList({ QLatin1String("text/plain") })
|
||||
+ << QVector<qint64>({ col4.id() });
|
||||
+ QTest::newRow("") << QVector<qint64>({ col1.id() })
|
||||
+ << QStringList({ QLatin1String("unique/mime-type") })
|
||||
+ << QVector<qint64>();
|
||||
+ QTest::newRow("") << QVector<qint64>({ col2.id(), col7.id() })
|
||||
+ << QStringList({ QLatin1String("application/octet-stream") })
|
||||
+ << QVector<qint64>({ col3.id(), col8.id() });
|
||||
+ }
|
||||
+
|
||||
+ void testSearchHelperCollectionListing()
|
||||
+ {
|
||||
+ QFETCH(QVector<qint64>, ancestors);
|
||||
+ QFETCH(QStringList, mimetypes);
|
||||
+ QFETCH(QVector<qint64>, expectedResults);
|
||||
+
|
||||
+ QVector<qint64> results = SearchHelper::matchSubcollectionsByMimeType(ancestors, mimetypes);
|
||||
+
|
||||
+ qSort(expectedResults);
|
||||
+ qSort(results);
|
||||
+
|
||||
+ QCOMPARE(results.size(), expectedResults.size());
|
||||
+ QCOMPARE(results, expectedResults);
|
||||
+ }
|
||||
+
|
||||
+};
|
||||
+
|
||||
+AKTEST_FAKESERVER_MAIN(SearchTest)
|
||||
+
|
||||
+#include "searchtest.moc"
|
||||
--
|
||||
2.1.0
|
||||
|
||||
185
0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch
Normal file
185
0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
From ac118e12fca25826340b6c8561939be19c4b7170 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 8 Dec 2014 13:55:58 +0100
|
||||
Subject: [PATCH 27/30] Minor improvements in StatisticsCache as suggested by
|
||||
Millian
|
||||
|
||||
* rename instance() to self()
|
||||
* first call self() from main thread to avoid having mutex
|
||||
* use CountQueryBuilder
|
||||
---
|
||||
server/src/akonadi.cpp | 4 +++-
|
||||
server/src/handler/select.cpp | 2 +-
|
||||
server/src/handler/status.cpp | 2 +-
|
||||
server/src/handlerhelper.cpp | 2 +-
|
||||
server/src/storage/collectionstatistics.cpp | 10 ++--------
|
||||
server/src/storage/collectionstatistics.h | 2 +-
|
||||
server/src/storage/notificationcollector.cpp | 10 +++++-----
|
||||
7 files changed, 14 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/server/src/akonadi.cpp b/server/src/akonadi.cpp
|
||||
index 5369320..faef3a5 100644
|
||||
--- a/server/src/akonadi.cpp
|
||||
+++ b/server/src/akonadi.cpp
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "utils.h"
|
||||
#include "debuginterface.h"
|
||||
#include "storage/itemretrievalthread.h"
|
||||
+#include "storage/collectionstatistics.h"
|
||||
#include "preprocessormanager.h"
|
||||
#include "search/searchmanager.h"
|
||||
#include "search/searchtaskmanagerthread.h"
|
||||
@@ -169,6 +170,8 @@ bool AkonadiServer::init()
|
||||
new DebugInterface( this );
|
||||
ResourceManager::self();
|
||||
|
||||
+ CollectionStatistics::self();
|
||||
+
|
||||
// Initialize the preprocessor manager
|
||||
PreprocessorManager::init();
|
||||
|
||||
@@ -194,7 +197,6 @@ bool AkonadiServer::init()
|
||||
mAgentSearchManagerThread = new SearchTaskManagerThread( this );
|
||||
mAgentSearchManagerThread->start();
|
||||
|
||||
-
|
||||
const QStringList searchManagers = settings.value( QLatin1String( "Search/Manager" ),
|
||||
QStringList() << QLatin1String( "Nepomuk" )
|
||||
<< QLatin1String( "Agent" ) ).toStringList();
|
||||
diff --git a/server/src/handler/select.cpp b/server/src/handler/select.cpp
|
||||
index f1ecc44..a94d971 100644
|
||||
--- a/server/src/handler/select.cpp
|
||||
+++ b/server/src/handler/select.cpp
|
||||
@@ -97,7 +97,7 @@ bool Select::parseStream()
|
||||
response.setString( "FLAGS (" + Flag::joinByName( Flag::retrieveAll(), QLatin1String( " " ) ).toLatin1() + ")" );
|
||||
Q_EMIT responseAvailable( response );
|
||||
|
||||
- const CollectionStatistics::Statistics stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ const CollectionStatistics::Statistics stats = CollectionStatistics::self()->statistics(col);
|
||||
if ( stats.count == -1 ) {
|
||||
return failureResponse( "Unable to determine item count" );
|
||||
}
|
||||
diff --git a/server/src/handler/status.cpp b/server/src/handler/status.cpp
|
||||
index 283532c..5fc9bb1 100644
|
||||
--- a/server/src/handler/status.cpp
|
||||
+++ b/server/src/handler/status.cpp
|
||||
@@ -63,7 +63,7 @@ bool Status::parseStream()
|
||||
// Responses:
|
||||
// REQUIRED untagged responses: STATUS
|
||||
|
||||
- const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ const CollectionStatistics::Statistics &stats = CollectionStatistics::self()->statistics(col);
|
||||
if (stats.count == -1) {
|
||||
return failureResponse( "Failed to query statistics." );
|
||||
}
|
||||
diff --git a/server/src/handlerhelper.cpp b/server/src/handlerhelper.cpp
|
||||
index 39583ce..a88bc6e 100644
|
||||
--- a/server/src/handlerhelper.cpp
|
||||
+++ b/server/src/handlerhelper.cpp
|
||||
@@ -166,7 +166,7 @@ QByteArray HandlerHelper::collectionToByteArray( const Collection &col, bool hid
|
||||
b += " " AKONADI_PARAM_VIRTUAL " " + QByteArray::number( col.isVirtual() ) + ' ';
|
||||
|
||||
if ( includeStatistics ) {
|
||||
- const CollectionStatistics::Statistics &stats = CollectionStatistics::instance()->statistics(col);
|
||||
+ const CollectionStatistics::Statistics &stats = CollectionStatistics::self()->statistics(col);
|
||||
if (stats.count > -1) {
|
||||
b += AKONADI_ATTRIBUTE_MESSAGES " " + QByteArray::number( stats.count ) + ' ';
|
||||
b += AKONADI_ATTRIBUTE_UNSEEN " ";
|
||||
diff --git a/server/src/storage/collectionstatistics.cpp b/server/src/storage/collectionstatistics.cpp
|
||||
index b2c6915..7307956 100644
|
||||
--- a/server/src/storage/collectionstatistics.cpp
|
||||
+++ b/server/src/storage/collectionstatistics.cpp
|
||||
@@ -31,14 +31,11 @@ using namespace Akonadi::Server;
|
||||
|
||||
CollectionStatistics *CollectionStatistics::sInstance = 0;
|
||||
|
||||
-CollectionStatistics* CollectionStatistics::instance()
|
||||
+CollectionStatistics* CollectionStatistics::self()
|
||||
{
|
||||
- static QMutex lock;
|
||||
- lock.lock();
|
||||
if (sInstance == 0) {
|
||||
sInstance = new CollectionStatistics();
|
||||
}
|
||||
- lock.unlock();
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
@@ -60,11 +57,8 @@ const CollectionStatistics::Statistics& CollectionStatistics::statistics(const C
|
||||
|
||||
CollectionStatistics::Statistics CollectionStatistics::getCollectionStatistics(const Collection &col)
|
||||
{
|
||||
- QueryBuilder qb(PimItem::tableName());
|
||||
// COUNT(DISTINCT PimItemTable.id)
|
||||
- qb.addAggregation(QString::fromLatin1("DISTINCT %1")
|
||||
- .arg(PimItem::idFullColumnName()),
|
||||
- QLatin1String("count"));
|
||||
+ CountQueryBuilder qb(PimItem::tableName(), PimItem::idFullColumnName(), CountQueryBuilder::Distinct);
|
||||
// SUM(PimItemTable.size)
|
||||
qb.addAggregation(PimItem::sizeFullColumnName(), QLatin1String("sum"));
|
||||
// SUM(CASE WHEN FlagTable.name IN ('\SEEN', '$IGNORED') THEN 1 ELSE 0 END)
|
||||
diff --git a/server/src/storage/collectionstatistics.h b/server/src/storage/collectionstatistics.h
|
||||
index 2c0af6a..a0b9f24 100644
|
||||
--- a/server/src/storage/collectionstatistics.h
|
||||
+++ b/server/src/storage/collectionstatistics.h
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
qint64 read;
|
||||
};
|
||||
|
||||
- static CollectionStatistics* instance();
|
||||
+ static CollectionStatistics* self();
|
||||
|
||||
const Statistics& statistics(const Collection &col);
|
||||
void invalidateCollection(const Collection &col);
|
||||
diff --git a/server/src/storage/notificationcollector.cpp b/server/src/storage/notificationcollector.cpp
|
||||
index dbc7883..7ed255c 100644
|
||||
--- a/server/src/storage/notificationcollector.cpp
|
||||
+++ b/server/src/storage/notificationcollector.cpp
|
||||
@@ -134,7 +134,7 @@ void NotificationCollector::collectionChanged( const Collection &collection,
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionAdded( collection.id() );
|
||||
}
|
||||
- CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
+ CollectionStatistics::self()->invalidateCollection(collection);
|
||||
collectionNotification( NotificationMessageV2::Modify, collection, collection.parentId(), -1, resource, changes.toSet() );
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ void NotificationCollector::collectionRemoved( const Collection &collection,
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
|
||||
}
|
||||
- CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
+ CollectionStatistics::self()->invalidateCollection(collection);
|
||||
|
||||
collectionNotification( NotificationMessageV2::Remove, collection, collection.parentId(), -1, resource );
|
||||
}
|
||||
@@ -187,7 +187,7 @@ void NotificationCollector::collectionUnsubscribed( const Collection &collection
|
||||
if ( AkonadiServer::instance()->intervalChecker() ) {
|
||||
AkonadiServer::instance()->intervalChecker()->collectionRemoved( collection.id() );
|
||||
}
|
||||
- CollectionStatistics::instance()->invalidateCollection(collection);
|
||||
+ CollectionStatistics::self()->invalidateCollection(collection);
|
||||
|
||||
collectionNotification( NotificationMessageV2::Unsubscribe, collection, collection.parentId(), -1, resource, QSet<QByteArray>() );
|
||||
}
|
||||
@@ -288,7 +288,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
|
||||
copy.setParentCollection( iter.key() );
|
||||
copy.setResource( resource );
|
||||
|
||||
- CollectionStatistics::instance()->invalidateCollection(Collection::retrieveById(iter.key()));
|
||||
+ CollectionStatistics::self()->invalidateCollection(Collection::retrieveById(iter.key()));
|
||||
dispatchNotification( copy );
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ void NotificationCollector::itemNotification( NotificationMessageV2::Operation o
|
||||
}
|
||||
msg.setResource( res );
|
||||
|
||||
- CollectionStatistics::instance()->invalidateCollection(col);
|
||||
+ CollectionStatistics::self()->invalidateCollection(col);
|
||||
dispatchNotification( msg );
|
||||
}
|
||||
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
From da5751c7b1589d2ea5800a3cf96dfc93b23b9783 Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Tue, 9 Dec 2014 14:35:04 +0100
|
||||
Subject: [PATCH 28/30] Extend imapparser benchmark and keep static data
|
||||
around.
|
||||
|
||||
This gets rid of some temporary allocations and thus speeds up
|
||||
the whole process a bit.
|
||||
|
||||
REVIEW: 121406
|
||||
---
|
||||
libs/imapparser.cpp | 5 +++--
|
||||
libs/tests/imapparserbenchmark.cpp | 22 ++++++++++++++++++++++
|
||||
2 files changed, 25 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libs/imapparser.cpp b/libs/imapparser.cpp
|
||||
index 9b437e2..6f9f592 100644
|
||||
--- a/libs/imapparser.cpp
|
||||
+++ b/libs/imapparser.cpp
|
||||
@@ -364,7 +364,8 @@ int ImapParser::parseNumber( const QByteArray &data, qint64 &result, bool *ok, i
|
||||
QByteArray ImapParser::quote( const QByteArray &data )
|
||||
{
|
||||
if ( data.isEmpty() ) {
|
||||
- return QByteArray( "\"\"" );
|
||||
+ static const QByteArray empty( "\"\"" );
|
||||
+ return empty;
|
||||
}
|
||||
|
||||
const int inputLength = data.length();
|
||||
@@ -499,7 +500,7 @@ int ImapParser::parseDateTime( const QByteArray &data, QDateTime &dateTime, int
|
||||
}
|
||||
|
||||
pos += 3;
|
||||
- const QByteArray shortMonthNames( "janfebmaraprmayjunjulaugsepoctnovdec" );
|
||||
+ static const QByteArray shortMonthNames( "janfebmaraprmayjunjulaugsepoctnovdec" );
|
||||
int month = shortMonthNames.indexOf( data.mid( pos, 3 ).toLower() );
|
||||
if ( month == -1 ) {
|
||||
return start;
|
||||
diff --git a/libs/tests/imapparserbenchmark.cpp b/libs/tests/imapparserbenchmark.cpp
|
||||
index 17dac66..fd4335c 100644
|
||||
--- a/libs/tests/imapparserbenchmark.cpp
|
||||
+++ b/libs/tests/imapparserbenchmark.cpp
|
||||
@@ -94,6 +94,28 @@ class ImapParserBenchmark : public QObject
|
||||
ImapParser::parseParenthesizedList( data, result, 0 );
|
||||
}
|
||||
}
|
||||
+
|
||||
+ void parseNumber()
|
||||
+ {
|
||||
+ QByteArray data( "123456" );
|
||||
+ qint64 result;
|
||||
+ bool ok = false;
|
||||
+ QBENCHMARK {
|
||||
+ ImapParser::parseNumber( data, result, &ok );
|
||||
+ }
|
||||
+ QVERIFY(ok);
|
||||
+ QCOMPARE(result, qint64(123456));
|
||||
+ }
|
||||
+
|
||||
+ void parseDateTime()
|
||||
+ {
|
||||
+ QByteArray data( "28-May-2006 01:03:35 +0000" );
|
||||
+ QDateTime result;
|
||||
+ QBENCHMARK {
|
||||
+ ImapParser::parseDateTime( data, result );
|
||||
+ }
|
||||
+ QCOMPARE(result.toString( QString::fromUtf8( "dd-MMM-yyyy hh:mm:ss +0000" ) ), QString::fromUtf8( data ));
|
||||
+ }
|
||||
};
|
||||
|
||||
#include "imapparserbenchmark.moc"
|
||||
--
|
||||
2.1.0
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
From 89357c7b0fc5e76091510af504058c036fa1b2f9 Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Wed, 10 Dec 2014 21:04:24 +0100
|
||||
Subject: [PATCH 29/30] Reduce the amount of allocations by preallocating a
|
||||
buffer.
|
||||
|
||||
Sadly, QByteArray cannot be cleared without destroying the buffer.
|
||||
But we can guesstimate the target size of the buffer and thus
|
||||
reduce the amount of allocations.
|
||||
|
||||
This also adds a benchmark for ImapParser::parseString.
|
||||
---
|
||||
libs/imapparser.cpp | 1 +
|
||||
libs/tests/imapparserbenchmark.cpp | 19 +++++++++++++++++++
|
||||
2 files changed, 20 insertions(+)
|
||||
|
||||
diff --git a/libs/imapparser.cpp b/libs/imapparser.cpp
|
||||
index 6f9f592..f3301e7 100644
|
||||
--- a/libs/imapparser.cpp
|
||||
+++ b/libs/imapparser.cpp
|
||||
@@ -186,6 +186,7 @@ int ImapParser::parseQuotedString( const QByteArray &data, QByteArray &result, i
|
||||
// quoted string
|
||||
if ( data[begin] == '"' ) {
|
||||
++begin;
|
||||
+ result.reserve(qMin(32, data.size() - begin));
|
||||
for ( int i = begin; i < data.length(); ++i ) {
|
||||
const char ch = data.at( i );
|
||||
if ( foundSlash ) {
|
||||
diff --git a/libs/tests/imapparserbenchmark.cpp b/libs/tests/imapparserbenchmark.cpp
|
||||
index fd4335c..ee861a0 100644
|
||||
--- a/libs/tests/imapparserbenchmark.cpp
|
||||
+++ b/libs/tests/imapparserbenchmark.cpp
|
||||
@@ -95,6 +95,25 @@ class ImapParserBenchmark : public QObject
|
||||
}
|
||||
}
|
||||
|
||||
+ void parseString_data()
|
||||
+ {
|
||||
+ QTest::addColumn<QByteArray>( "data" );
|
||||
+ QTest::newRow("plain") << QByteArray("fooobarasdf something more lalala");
|
||||
+ QTest::newRow("quoted") << QByteArray("\"fooobarasdf\" something more lalala");
|
||||
+ }
|
||||
+
|
||||
+ void parseString()
|
||||
+ {
|
||||
+ QFETCH(QByteArray, data);
|
||||
+ QByteArray result;
|
||||
+ qint64 sum = 0;
|
||||
+ QBENCHMARK {
|
||||
+ sum += ImapParser::parseString( data, result );
|
||||
+ }
|
||||
+ QVERIFY(!result.isEmpty());
|
||||
+ QVERIFY(sum > 0);
|
||||
+ }
|
||||
+
|
||||
void parseNumber()
|
||||
{
|
||||
QByteArray data( "123456" );
|
||||
--
|
||||
2.1.0
|
||||
|
||||
119
0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch
Normal file
119
0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
From c733429f4fa9696fb027ddc946e54f6bbb68deaf Mon Sep 17 00:00:00 2001
|
||||
From: Milian Wolff <mail@milianw.de>
|
||||
Date: Wed, 10 Dec 2014 21:16:45 +0100
|
||||
Subject: [PATCH 30/30] Preallocate a capacity of 16 for the returned list.
|
||||
|
||||
See also 159abaf2f372eaa633db8f69ff6b1edd459998cc in kdepimlibs on
|
||||
why. I'll quote it here again:
|
||||
|
||||
In my data, most often the final size of fetchResponse is 16.
|
||||
By reserving that data upfront, we can get rid of 3/4 of the
|
||||
list allocations, according to the growth-strategy outlined here:
|
||||
http://qt-project.org/doc/qt-4.8/containers.html#growth-strategies
|
||||
I.e. before, we'd allocate 4, 8, 12, 16. Now we directly allocate
|
||||
room for 16.
|
||||
|
||||
Thing is, doing this outside of akonadi has no effect, as QList::clear
|
||||
destroys the internal buffer. With the added benchmark, I now verified
|
||||
that this patch here does have a positive effect.
|
||||
---
|
||||
libs/imapparser.cpp | 2 ++
|
||||
libs/tests/imapparserbenchmark.cpp | 52 ++++++++++++++++++++++++++------------
|
||||
2 files changed, 38 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/libs/imapparser.cpp b/libs/imapparser.cpp
|
||||
index f3301e7..f5a7457 100644
|
||||
--- a/libs/imapparser.cpp
|
||||
+++ b/libs/imapparser.cpp
|
||||
@@ -79,6 +79,8 @@ int parseParenthesizedListHelper( const QByteArray &data, T &result, int start )
|
||||
return start;
|
||||
}
|
||||
|
||||
+ result.reserve(16);
|
||||
+
|
||||
int count = 0;
|
||||
int sublistBegin = start;
|
||||
bool insideQuote = false;
|
||||
diff --git a/libs/tests/imapparserbenchmark.cpp b/libs/tests/imapparserbenchmark.cpp
|
||||
index ee861a0..7545238 100644
|
||||
--- a/libs/tests/imapparserbenchmark.cpp
|
||||
+++ b/libs/tests/imapparserbenchmark.cpp
|
||||
@@ -27,6 +27,25 @@ Q_DECLARE_METATYPE( QList<QByteArray> )
|
||||
class ImapParserBenchmark : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
+ private:
|
||||
+ void geneateParseParenthesizedListData()
|
||||
+ {
|
||||
+ QTest::addColumn<QByteArray>( "data" );
|
||||
+ QTest::newRow( "empty" ) << QByteArray();
|
||||
+ QTest::newRow( "unnested" ) << QByteArray("(\"Foo Bar\" NIL \"foobar\" \"test.com\")");
|
||||
+ QTest::newRow( "nested" ) << QByteArray("((\"Foo Bar\" NIL \"foobar\" \"test.com\"))");
|
||||
+ QTest::newRow( "nested-long" ) << QByteArray("(UID 86 REV 0 MIMETYPE \"message/rfc822\" COLLECTIONID 13 SIZE 6114 FLAGS (\\SEEN)"
|
||||
+ " ANCESTORS ((13 \"/INBOX\") (12 \"imap://mail@mail.test.com/\") (0 \"\")) PLD:ENVELOPE[1] {396}"
|
||||
+ " (\"Fri, 04 Jun 2010 09:07:54 +0200\" \"Re: [ADMIN] foobar available again!\""
|
||||
+ " ((\"Foo Bar\" NIL \"foobar\" \"test.com\"))"
|
||||
+ " NIL NIL"
|
||||
+ " ((\"Asdf Bla Blub\" NIL \"asdf.bla.blub\" \"123test.org\"))"
|
||||
+ " ((NIL NIL \"muh.kuh\" \"lalala.com\") (\"Konqi KDE\" NIL \"konqi\" \"kde.org\") (NIL NIL \"all\" \"test.com\"))"
|
||||
+ " NIL \"<201006040905.33367.foo.bar@test.com>\" \"<4C08A64A.9020205@123test.org>\""
|
||||
+ " \"<201006040142.56540.muh.kuh@lalala.com> <201006040704.39648.konqi@kde.org> <201006040905.33367.foo.bar@test.com>\""
|
||||
+ "))");
|
||||
+ }
|
||||
+
|
||||
private Q_SLOTS:
|
||||
void quote_data()
|
||||
{
|
||||
@@ -68,25 +87,12 @@ class ImapParserBenchmark : public QObject
|
||||
}
|
||||
}
|
||||
|
||||
- void parseParenthesizedList_data()
|
||||
+ void parseParenthesizedQVarLengthArray_data()
|
||||
{
|
||||
- QTest::addColumn<QByteArray>( "data" );
|
||||
- QTest::newRow( "empty" ) << QByteArray();
|
||||
- QTest::newRow( "unnested" ) << QByteArray("(\"Foo Bar\" NIL \"foobar\" \"test.com\")");
|
||||
- QTest::newRow( "nested" ) << QByteArray("((\"Foo Bar\" NIL \"foobar\" \"test.com\"))");
|
||||
- QTest::newRow( "nested-long" ) << QByteArray("(UID 86 REV 0 MIMETYPE \"message/rfc822\" COLLECTIONID 13 SIZE 6114 FLAGS (\\SEEN)"
|
||||
- " ANCESTORS ((13 \"/INBOX\") (12 \"imap://mail@mail.test.com/\") (0 \"\")) PLD:ENVELOPE[1] {396}"
|
||||
- " (\"Fri, 04 Jun 2010 09:07:54 +0200\" \"Re: [ADMIN] foobar available again!\""
|
||||
- " ((\"Foo Bar\" NIL \"foobar\" \"test.com\"))"
|
||||
- " NIL NIL"
|
||||
- " ((\"Asdf Bla Blub\" NIL \"asdf.bla.blub\" \"123test.org\"))"
|
||||
- " ((NIL NIL \"muh.kuh\" \"lalala.com\") (\"Konqi KDE\" NIL \"konqi\" \"kde.org\") (NIL NIL \"all\" \"test.com\"))"
|
||||
- " NIL \"<201006040905.33367.foo.bar@test.com>\" \"<4C08A64A.9020205@123test.org>\""
|
||||
- " \"<201006040142.56540.muh.kuh@lalala.com> <201006040704.39648.konqi@kde.org> <201006040905.33367.foo.bar@test.com>\""
|
||||
- "))");
|
||||
+ geneateParseParenthesizedListData();
|
||||
}
|
||||
|
||||
- void parseParenthesizedList()
|
||||
+ void parseParenthesizedQVarLengthArray()
|
||||
{
|
||||
QFETCH( QByteArray, data );
|
||||
QVarLengthArray<QByteArray, 16> result;
|
||||
@@ -95,6 +101,20 @@ class ImapParserBenchmark : public QObject
|
||||
}
|
||||
}
|
||||
|
||||
+ void parseParenthesizedQList_data()
|
||||
+ {
|
||||
+ geneateParseParenthesizedListData();
|
||||
+ }
|
||||
+
|
||||
+ void parseParenthesizedQList()
|
||||
+ {
|
||||
+ QFETCH( QByteArray, data );
|
||||
+ QList<QByteArray> result;
|
||||
+ QBENCHMARK {
|
||||
+ ImapParser::parseParenthesizedList( data, result, 0 );
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
void parseString_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>( "data" );
|
||||
--
|
||||
2.1.0
|
||||
|
||||
52
0031-Less-C-11-fixes-build-with-clang.patch
Normal file
52
0031-Less-C-11-fixes-build-with-clang.patch
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
From c23607679fa1451f0c6890bd4a5656c07d519853 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Fri, 6 Feb 2015 13:33:50 +0100
|
||||
Subject: [PATCH 31/34] Less C++11, fixes build with clang
|
||||
|
||||
---
|
||||
server/tests/unittest/searchtest.cpp | 28 ++++++++++++++--------------
|
||||
1 file changed, 14 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/server/tests/unittest/searchtest.cpp b/server/tests/unittest/searchtest.cpp
|
||||
index f523b09..969d296 100644
|
||||
--- a/server/tests/unittest/searchtest.cpp
|
||||
+++ b/server/tests/unittest/searchtest.cpp
|
||||
@@ -119,21 +119,21 @@ private Q_SLOTS:
|
||||
QTest::addColumn<QStringList>("mimetypes");
|
||||
QTest::addColumn<QVector<qint64>>("expectedResults");
|
||||
|
||||
- QTest::newRow("") << QVector<qint64>({ 0 })
|
||||
- << QStringList({ QLatin1String("text/plain") })
|
||||
- << QVector<qint64>({ col4.id(), col5.id(), col7.id() });
|
||||
- QTest::newRow("") << QVector<qint64>({ 0 })
|
||||
- << QStringList({ QLatin1String("application/octet-stream") })
|
||||
- << QVector<qint64>({ col2.id(), col3.id(), col6.id(), col8.id() });
|
||||
- QTest::newRow("") << QVector<qint64>({ col1.id() })
|
||||
- << QStringList({ QLatin1String("text/plain") })
|
||||
- << QVector<qint64>({ col4.id() });
|
||||
- QTest::newRow("") << QVector<qint64>({ col1.id() })
|
||||
- << QStringList({ QLatin1String("unique/mime-type") })
|
||||
+ QTest::newRow("") << (QVector<qint64>() << 0)
|
||||
+ << (QStringList() << QLatin1String("text/plain"))
|
||||
+ << (QVector<qint64>() << col4.id() << col5.id() << col7.id());
|
||||
+ QTest::newRow("") << (QVector<qint64>() << 0)
|
||||
+ << (QStringList() << QLatin1String("application/octet-stream"))
|
||||
+ << (QVector<qint64>() << col2.id() << col3.id() << col6.id() << col8.id());
|
||||
+ QTest::newRow("") << (QVector<qint64>() << col1.id())
|
||||
+ << (QStringList() << QLatin1String("text/plain"))
|
||||
+ << (QVector<qint64>() << col4.id());
|
||||
+ QTest::newRow("") << (QVector<qint64>() << col1.id())
|
||||
+ << (QStringList() << QLatin1String("unique/mime-type"))
|
||||
<< QVector<qint64>();
|
||||
- QTest::newRow("") << QVector<qint64>({ col2.id(), col7.id() })
|
||||
- << QStringList({ QLatin1String("application/octet-stream") })
|
||||
- << QVector<qint64>({ col3.id(), col8.id() });
|
||||
+ QTest::newRow("") << (QVector<qint64>() << col2.id() << col7.id())
|
||||
+ << (QStringList() << QLatin1String("application/octet-stream"))
|
||||
+ << (QVector<qint64>() << col3.id() << col8.id());
|
||||
}
|
||||
|
||||
void testSearchHelperCollectionListing()
|
||||
--
|
||||
2.4.3
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
From abe71f46c3b2e657db25ac16c43a4c76b2212a9f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Wed, 17 Jun 2015 13:04:13 +0200
|
||||
Subject: [PATCH 32/34] Don't throw exception when MOVE handler finds no items
|
||||
to move
|
||||
|
||||
Instead return "OK MOVE complete" right away. The reason for this is that
|
||||
when client tries to move an Item from a folder into the same folder (it's
|
||||
possible in KMail, also mailfilter agent might trigger this situation) the
|
||||
subsequent command gets eaten by ImapStreamParser and the client's Job gets
|
||||
stuck waiting for response forever. According to Laurent this could also fix
|
||||
the Mail Filter Agent getting stuck occasionally.
|
||||
|
||||
The problem is in ImapStreamParser::atCommandEnd() method, which is called
|
||||
by the Move handler at some point. atCommandEnd() checks whether we reached
|
||||
command end in the stream by looking if the next characters in the stream
|
||||
are "\r\n" and if so it will consume the command end ("\r\n"), effectively
|
||||
moving the streaming position BEYOND the command. In case of MOVE the
|
||||
command has already been completely parsed so we are actually at the end of
|
||||
the command and so ImapStreamParser will consume the "\r\n" and position the
|
||||
stream beyond the command end.
|
||||
|
||||
After that the Move handler tries to get the items from DB and throws the
|
||||
exception (the second part of the condition in the SQL query causes that
|
||||
the query yields no results in this situation) which gets us back to
|
||||
Connection where we then call ImapStreamParser::skipCommand(). At this point
|
||||
however there are no more data in the stream (because atCommandEnd() moved
|
||||
us beyond the end of the MOVE command) and so ImapStreamParser will block
|
||||
and wait for more data (with 30 seconds timeout). If client sends another
|
||||
command within this time the ImapStreamParser will think that this is the
|
||||
command to be skipped and will consume it. This means that the command never
|
||||
really reaches the Connection as it's consumed as soon as it's captured by
|
||||
ImapStreamParser. And because Akonadi never receives the command it cannot
|
||||
send a response and thus the Job in client will wait forever and ever...
|
||||
|
||||
Proper fix would be to make ImapStreamParser::atCommandEnd() to only peek
|
||||
instead of actually altering the position in the stream however I'm really
|
||||
afraid that it could break some other stuff that relies on this (broken?)
|
||||
behaviour and our test coverage is not sufficient at this point to be
|
||||
reliable enough.
|
||||
---
|
||||
server/src/handler/move.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/src/handler/move.cpp b/server/src/handler/move.cpp
|
||||
index 0a6c3bf..4cf9d4e 100644
|
||||
--- a/server/src/handler/move.cpp
|
||||
+++ b/server/src/handler/move.cpp
|
||||
@@ -85,7 +85,7 @@ bool Move::parseStream()
|
||||
if ( qb.exec() ) {
|
||||
const QVector<PimItem> items = qb.result();
|
||||
if ( items.isEmpty() ) {
|
||||
- throw HandlerException( "No items found" );
|
||||
+ return successResponse( "MOVE complete" );
|
||||
}
|
||||
|
||||
// Split the list by source collection
|
||||
--
|
||||
2.4.3
|
||||
|
||||
140
0033-Don-t-leak-old-external-payload-files.patch
Normal file
140
0033-Don-t-leak-old-external-payload-files.patch
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
From 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
||||
Date: Mon, 29 Jun 2015 22:45:11 +0200
|
||||
Subject: [PATCH 33/34] Don't leak old external payload files
|
||||
|
||||
Actually delete old payload files after we increase the payload revision or
|
||||
switch from external to internal payload. This caused ~/.local/share/akonadi/file_db_data
|
||||
to grow insanely for all users, leaving them with many duplicated files (just with
|
||||
different revisions).
|
||||
|
||||
It is recommended that users run akonadictl fsck to clean up the leaked payload
|
||||
files.
|
||||
|
||||
Note that there won't be any more releases of Akonadi 1.13 (and this has been
|
||||
fixed in master already), so I strongly recommend distributions to pick this
|
||||
patch into their packaging.
|
||||
|
||||
BUG: 341884
|
||||
CCBUG: 338402
|
||||
---
|
||||
server/src/storage/partstreamer.cpp | 14 ++++++++++++++
|
||||
server/tests/unittest/partstreamertest.cpp | 24 +++++++++++++-----------
|
||||
2 files changed, 27 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/server/src/storage/partstreamer.cpp b/server/src/storage/partstreamer.cpp
|
||||
index 2ec41fa..71bdca8 100644
|
||||
--- a/server/src/storage/partstreamer.cpp
|
||||
+++ b/server/src/storage/partstreamer.cpp
|
||||
@@ -290,6 +290,12 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists,
|
||||
mDataChanged = true;
|
||||
}
|
||||
|
||||
+ // If the part is external, remember it's current file name
|
||||
+ QString originalFile;
|
||||
+ if (part.isValid() && part.external()) {
|
||||
+ originalFile = PartHelper::resolveAbsolutePath(part.data());
|
||||
+ }
|
||||
+
|
||||
part.setPartType(partType);
|
||||
part.setVersion(partVersion);
|
||||
part.setPimItemId(mItem.id());
|
||||
@@ -306,6 +312,14 @@ bool PartStreamer::stream(const QByteArray &command, bool checkExists,
|
||||
*changed = mDataChanged;
|
||||
}
|
||||
|
||||
+ if (!originalFile.isEmpty()) {
|
||||
+ // If the part was external but is not anymore, or if it's still external
|
||||
+ // but the filename has changed (revision update), remove the original file
|
||||
+ if (!part.external() || (part.external() && originalFile != PartHelper::resolveAbsolutePath(part.data()))) {
|
||||
+ PartHelper::removeFile(originalFile);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return ok;
|
||||
}
|
||||
|
||||
diff --git a/server/tests/unittest/partstreamertest.cpp b/server/tests/unittest/partstreamertest.cpp
|
||||
index 05e3a8a..669bbbc 100644
|
||||
--- a/server/tests/unittest/partstreamertest.cpp
|
||||
+++ b/server/tests/unittest/partstreamertest.cpp
|
||||
@@ -91,6 +91,7 @@ private Q_SLOTS:
|
||||
QTest::addColumn<qint64>("expectedPartSize");
|
||||
QTest::addColumn<bool>("expectedChanged");
|
||||
QTest::addColumn<bool>("isExternal");
|
||||
+ QTest::addColumn<int>("version");
|
||||
QTest::addColumn<PimItem>("pimItem");
|
||||
|
||||
PimItem item;
|
||||
@@ -101,22 +102,22 @@ private Q_SLOTS:
|
||||
QVERIFY(item.insert());
|
||||
|
||||
// Order of these tests matters!
|
||||
- QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << item;
|
||||
- QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << item;
|
||||
- QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << item;
|
||||
- QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << item;
|
||||
- QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << item;
|
||||
- QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << item;
|
||||
+ QTest::newRow("item 1, internal") << QByteArray("PLD:DATA") << QByteArray("123") << 3ll << true << false << -1 << item;
|
||||
+ QTest::newRow("item 1, change to external") << QByteArray("PLD:DATA") << QByteArray("123456789") << 9ll << true << true << 0 << item;
|
||||
+ QTest::newRow("item 1, update external") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << true << true << 1 << item;
|
||||
+ QTest::newRow("item 1, external, no change") << QByteArray("PLD:DATA") << QByteArray("987654321") << 9ll << false << true << 2 << item;
|
||||
+ QTest::newRow("item 1, change to internal") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << true << false << 2 << item;
|
||||
+ QTest::newRow("item 1, internal, no change") << QByteArray("PLD:DATA") << QByteArray("1234") << 4ll << false << false << 2 << item;
|
||||
}
|
||||
|
||||
void testStreamer()
|
||||
{
|
||||
- return;
|
||||
QFETCH(QByteArray, expectedPartName);
|
||||
QFETCH(QByteArray, expectedData);
|
||||
QFETCH(qint64, expectedPartSize);
|
||||
QFETCH(bool, expectedChanged);
|
||||
QFETCH(bool, isExternal);
|
||||
+ QFETCH(int, version);
|
||||
QFETCH(PimItem, pimItem);
|
||||
|
||||
FakeConnection connection;
|
||||
@@ -160,17 +161,18 @@ private Q_SLOTS:
|
||||
|
||||
PimItem item = PimItem::retrieveById(pimItem.id());
|
||||
const QVector<Part> parts = item.parts();
|
||||
- QVERIFY(parts.count() == 1);
|
||||
+ QCOMPARE(parts.count(), 1);
|
||||
const Part part = parts[0];
|
||||
QCOMPARE(part.datasize(), expectedPartSize);
|
||||
QCOMPARE(part.external(), isExternal);
|
||||
+ qDebug() << part.version() << part.data();
|
||||
const QByteArray data = part.data();
|
||||
if (isExternal) {
|
||||
QVERIFY(streamerSpy.count() == 1);
|
||||
QVERIFY(streamerSpy.first().count() == 1);
|
||||
const Response response = streamerSpy.first().first().value<Akonadi::Server::Response>();
|
||||
const QByteArray str = response.asString();
|
||||
- const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version()) + "]";
|
||||
+ const QByteArray expectedResponse = "+ STREAM [FILE " + QByteArray::number(part.id()) + "_r" + QByteArray::number(version) + "]";
|
||||
QCOMPARE(QString::fromUtf8(str), QString::fromUtf8(expectedResponse));
|
||||
|
||||
QFile file(PartHelper::resolveAbsolutePath(data));
|
||||
@@ -182,7 +184,7 @@ private Q_SLOTS:
|
||||
QCOMPARE(fileData, expectedData);
|
||||
|
||||
// Make sure no previous versions are left behind in file_db_data
|
||||
- for (int i = 0; i < part.version(); ++i) {
|
||||
+ for (int i = 0; i < version; ++i) {
|
||||
const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version());
|
||||
const QString filePath = PartHelper::resolveAbsolutePath(fileName);
|
||||
QVERIFY(!QFile::exists(filePath));
|
||||
@@ -194,7 +196,7 @@ private Q_SLOTS:
|
||||
QCOMPARE(data, expectedData);
|
||||
|
||||
// Make sure nothing is left behind in file_db_data
|
||||
- for (int i = 0; i <= part.version(); ++i) {
|
||||
+ for (int i = 0; i <= version; ++i) {
|
||||
const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(part.version());
|
||||
const QString filePath = PartHelper::resolveAbsolutePath(fileName);
|
||||
QVERIFY(!QFile::exists(filePath));
|
||||
--
|
||||
2.4.3
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
From 18ed37d89b8185ac15a8bfe245de8a88d17f2c64 Mon Sep 17 00:00:00 2001
|
||||
From: David Faure <faure@kde.org>
|
||||
Date: Tue, 28 Jul 2015 12:47:44 +0200
|
||||
Subject: [PATCH 34/34] set cmake_min_req to match kdelibs4 and enable newer
|
||||
policies
|
||||
|
||||
---
|
||||
CMakeLists.txt | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 2d790c9..a64e724 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1,6 +1,6 @@
|
||||
+cmake_minimum_required(VERSION 2.8.9)
|
||||
project(Akonadi)
|
||||
|
||||
-cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
|
||||
|
||||
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
|
||||
set(CMAKE_MODULE_PATH "${Akonadi_SOURCE_DIR}/cmake/modules")
|
||||
--
|
||||
2.4.3
|
||||
|
||||
33
akonadi-1.13.0-libs_only.patch
Normal file
33
akonadi-1.13.0-libs_only.patch
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
diff -up akonadi-1.13.0/CMakeLists.txt.opt akonadi-1.13.0/CMakeLists.txt
|
||||
--- akonadi-1.13.0/CMakeLists.txt.opt 2015-12-11 07:44:57.653216984 -0600
|
||||
+++ akonadi-1.13.0/CMakeLists.txt 2015-12-11 07:52:14.749205933 -0600
|
||||
@@ -339,22 +339,22 @@ endif()
|
||||
|
||||
include_directories(${Akonadi_SOURCE_DIR} ${Akonadi_BINARY_DIR} ${QT_INCLUDES} ${Boost_INCLUDE_DIR})
|
||||
|
||||
-add_subdirectory(interfaces)
|
||||
+add_subdirectory(interfaces)
|
||||
add_subdirectory(libs)
|
||||
set(AKONADI_PROTOCOLINTERNALS_LIBS ${akonadiprotocolinternals_LIB_DEPENDS} akonadiprotocolinternals)
|
||||
|
||||
-add_subdirectory(shared)
|
||||
-add_subdirectory(agentserver)
|
||||
-add_subdirectory(server)
|
||||
+#add_subdirectory(shared)
|
||||
+#add_subdirectory(agentserver)
|
||||
+#add_subdirectory(server)
|
||||
|
||||
-add_subdirectory(rds)
|
||||
+#add_subdirectory(rds)
|
||||
if(NOT WIN32)
|
||||
- add_subdirectory(asapcat)
|
||||
+ #add_subdirectory(asapcat)
|
||||
endif()
|
||||
if (NOT QT5_BUILD)
|
||||
if(SQLITE_FOUND)
|
||||
option(SQLITE_LINK_STATIC "link libsqlite3 statically" FALSE)
|
||||
- add_subdirectory(qsqlite)
|
||||
+ #add_subdirectory(qsqlite)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
414
akonadi.spec
414
akonadi.spec
|
|
@ -1,3 +1,5 @@
|
|||
# Force out of source build
|
||||
%undefine __cmake_in_source_build
|
||||
|
||||
# base pkg default to SQLITE now, install -mysql if you want that instead
|
||||
%global database_backend SQLITE
|
||||
|
|
@ -5,206 +7,322 @@
|
|||
# trim changelog included in binary rpms
|
||||
%global _changelog_trimtime %(date +%s -d "1 year ago")
|
||||
|
||||
%if 0%{?rhel} == 6
|
||||
%define cmake_pkg cmake28
|
||||
%else
|
||||
%define cmake_pkg cmake
|
||||
%endif
|
||||
|
||||
Summary: PIM Storage Service
|
||||
Summary: PIM Storage Service Libraries
|
||||
Name: akonadi
|
||||
Version: 1.10.3
|
||||
Release: 1%{?dist}
|
||||
Version: 1.13.0
|
||||
Release: 130%{?dist}
|
||||
|
||||
License: LGPLv2+
|
||||
License: LGPL-2.0-or-later
|
||||
URL: http://community.kde.org/KDE_PIM/Akonadi
|
||||
%if 0%{?snap}
|
||||
# git clone git://git.kde.org/akonadi
|
||||
# git archive --prefix=akonadi-%{version}/ master | bzip2 > akonadi-%{version}-%{snap}.tar.bz2
|
||||
Source0: akonadi-%{version}-%{snap}.tar.bz2
|
||||
%else
|
||||
# Official release
|
||||
Source0: http://download.kde.org/stable/akonadi/src/akonadi-%{version}-1.tar.bz2
|
||||
%endif
|
||||
Source0: http://download.kde.org/stable/akonadi/src/akonadi-%{version}.tar.bz2
|
||||
|
||||
## mysql config
|
||||
Source10: akonadiserverrc.mysql
|
||||
## downstream patches
|
||||
Patch100: akonadi-1.13.0-libs_only.patch
|
||||
|
||||
## upstreamable patches
|
||||
|
||||
## upstream patches
|
||||
|
||||
%define mysql_conf_timestamp 20130607
|
||||
## upstream patches (1.13 branch)
|
||||
Patch1: 0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch
|
||||
Patch2: 0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch
|
||||
Patch3: 0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch
|
||||
Patch4: 0004-Fix-typo-in-if-condition.patch
|
||||
Patch5: 0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch
|
||||
Patch6: 0006-Don-t-crash-when-setmntent-returns-NULL.patch
|
||||
Patch7: 0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch
|
||||
Patch8: 0008-Suppress-unused-variable-warnings-in-release-mode.patch
|
||||
Patch9: 0009-Test-whether-compiler-supports-all-required-C-11-fea.patch
|
||||
Patch10: 0010-prevent-starting-a-QTimer-with-a-negative-interval.patch
|
||||
Patch11: 0011-Convert-some-qDebugs-to-akDebugs.patch
|
||||
Patch12: 0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch
|
||||
Patch13: 0013-Intern-entity-strings-for-table-and-column-names.patch
|
||||
Patch14: 0014-No-semicolon-after-Q_DECLARE_METATYPE.patch
|
||||
Patch15: 0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch
|
||||
Patch16: 0016-Use-an-QAtomicInt-instead-of-a-plain-bool-for-Entity.patch
|
||||
Patch17: 0017-Optimize-Only-do-one-hash-lookup-to-retrieve-value-f.patch
|
||||
Patch18: 0018-Optimize-Skip-value-condition-on-invalid-flags.patch
|
||||
Patch19: 0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch
|
||||
Patch20: 0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch
|
||||
Patch21: 0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch
|
||||
Patch22: 0022-Implement-cache-for-CollectionStatistics-to-signific.patch
|
||||
Patch23: 0023-Always-create-a-new-PartType-when-it-does-not-exist.patch
|
||||
Patch24: 0024-Fix-compilation-with-strict-iterators.patch
|
||||
Patch25: 0025-Avoid-repeated-calls-to-PimItem-flags-and-PimItem-ta.patch
|
||||
Patch26: 0026-Avoid-recursive-collection-listing-in-SearchHelper.patch
|
||||
Patch27: 0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch
|
||||
Patch28: 0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch
|
||||
Patch29: 0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch
|
||||
Patch30: 0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch
|
||||
|
||||
BuildRequires: automoc4
|
||||
BuildRequires: boost-devel
|
||||
BuildRequires: %{cmake_pkg} >= 2.8.8
|
||||
BuildRequires: cmake >= 2.8.8
|
||||
BuildRequires: gcc-c++
|
||||
# for xsltproc
|
||||
BuildRequires: libxslt
|
||||
BuildRequires: pkgconfig(QtDBus) pkgconfig(QtSql) pkgconfig(QtXml)
|
||||
BuildRequires: pkgconfig(shared-mime-info)
|
||||
BuildRequires: pkgconfig(soprano)
|
||||
BuildRequires: pkgconfig(sqlite3) >= 3.6.23
|
||||
# %%check
|
||||
BuildRequires: dbus-x11 xorg-x11-server-Xvfb
|
||||
# backends, used at buildtime to query known locations of server binaries
|
||||
# FIXME/TODO: set these via cmake directives, avoids needless buildroot items
|
||||
BuildRequires: mysql-server
|
||||
BuildRequires: postgresql-server
|
||||
|
||||
#%{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}}
|
||||
Requires: qt4%{?_isa} >= 4.8.5-10
|
||||
|
||||
Requires(postun): /sbin/ldconfig
|
||||
|
||||
%description
|
||||
%{summary}.
|
||||
|
||||
%package devel
|
||||
Summary: Developer files for %{name}
|
||||
Conflicts: kf5-akonadi-server-devel
|
||||
Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
%description devel
|
||||
%{summary}.
|
||||
|
||||
%package mysql
|
||||
Summary: Akonadi MySQL backend support
|
||||
# upgrade path
|
||||
Obsoletes: akonadi < 1.7.90-2
|
||||
Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
Requires: mysql-server
|
||||
Requires: qt4-mysql%{?_isa}
|
||||
Requires(post): %{_sbindir}/update-alternatives
|
||||
Requires(postun): %{_sbindir}/update-alternatives
|
||||
%description mysql
|
||||
Configures akonadi to use mysql backend by default.
|
||||
|
||||
Requires an available instance of mysql server at runtime.
|
||||
Akonadi can spawn a per-user one automatically if the mysql-server
|
||||
package is installed on the machine.
|
||||
See also: %{_sysconfdir}/akonadi/mysql-global.conf
|
||||
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -n akonadi-%{version}
|
||||
%autosetup -p1 -n akonadi-%{version}
|
||||
|
||||
|
||||
%build
|
||||
mkdir -p %{_target_platform}
|
||||
pushd %{_target_platform}
|
||||
%{?cmake28}%{!?cmake28:%{?cmake}} \
|
||||
-DCONFIG_INSTALL_DIR=%{_sysconfdir} \
|
||||
%{?database_backend:-DDATABASE_BACKEND=%{database_backend}} \
|
||||
-DINSTALL_QSQLITE_IN_QT_PREFIX:BOOL=ON \
|
||||
..
|
||||
popd
|
||||
|
||||
make %{?_smp_mflags} -C %{_target_platform}
|
||||
%cmake -DCMAKE_BUILD_TYPE:STRING="Release"
|
||||
%cmake_build
|
||||
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
make install/fast DESTDIR=$RPM_BUILD_ROOT -C %{_target_platform}
|
||||
%cmake_install
|
||||
|
||||
install -p -m644 -D %{SOURCE10} %{buildroot}%{_sysconfdir}/xdg/akonadi/akonadiserverrc.mysql
|
||||
|
||||
mkdir -p %{buildroot}%{_datadir}/akonadi/agents
|
||||
|
||||
# create "big" config (analog to -mobile.conf)
|
||||
install -p \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-global.conf \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-global-big.conf
|
||||
|
||||
# default to small/mobile config
|
||||
install -p \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-global-mobile.conf \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-global.conf
|
||||
|
||||
touch -d %{mysql_conf_timestamp} \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-global*.conf \
|
||||
%{buildroot}%{_sysconfdir}/akonadi/mysql-local.conf
|
||||
|
||||
# create/own %{_libdir}/akondi
|
||||
mkdir -p %{buildroot}%{_libdir}/akonadi
|
||||
|
||||
# %%ghost'd global akonadiserverrc
|
||||
touch akonadiserverrc
|
||||
install -p -m644 -D akonadiserverrc %{buildroot}%{_sysconfdir}/xdg/akonadi/akonadiserverrc
|
||||
## unpackaged files
|
||||
rm -fv %{buildroot}%{_datadir}/mime/packages/akonadi-mime.xml
|
||||
|
||||
|
||||
%check
|
||||
export PKG_CONFIG_PATH=%{buildroot}%{_datadir}/pkgconfig:%{buildroot}%{_libdir}/pkgconfig
|
||||
test "$(pkg-config --modversion akonadi)" = "%{version}"
|
||||
# this one (still) fails in mock (local build ok):
|
||||
# 14/14 Test #14: akonadi-dbconfigtest
|
||||
xvfb-run -a dbus-launch --exit-with-session make test -C %{_target_platform} ||:
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
|
||||
%post -p /sbin/ldconfig
|
||||
|
||||
%posttrans
|
||||
update-mime-database %{_datadir}/mime &> /dev/null || :
|
||||
|
||||
%postun
|
||||
/sbin/ldconfig ||:
|
||||
if [ $1 -eq 0 ] ; then
|
||||
update-mime-database %{_datadir}/mime &> /dev/null ||:
|
||||
fi
|
||||
%ldconfig_scriptlets
|
||||
|
||||
%files
|
||||
%doc AUTHORS lgpl-license
|
||||
%dir %{_sysconfdir}/xdg/akonadi/
|
||||
%ghost %config(missingok,noreplace) %{_sysconfdir}/xdg/akonadi/akonadiserverrc
|
||||
%dir %{_sysconfdir}/akonadi/
|
||||
%{_bindir}/akonadi_agent_launcher
|
||||
%{_bindir}/akonadi_agent_server
|
||||
%{_bindir}/akonadi_control
|
||||
%{_bindir}/akonadi_rds
|
||||
%{_bindir}/akonadictl
|
||||
%{_bindir}/akonadiserver
|
||||
%{_libdir}/akonadi/
|
||||
%doc AUTHORS
|
||||
%license lgpl-license
|
||||
%{_libdir}/libakonadiprotocolinternals.so.1*
|
||||
%{_datadir}/dbus-1/interfaces/org.freedesktop.Akonadi.*.xml
|
||||
%{_datadir}/dbus-1/services/org.freedesktop.Akonadi.*.service
|
||||
%{_datadir}/mime/packages/akonadi-mime.xml
|
||||
%{_datadir}/akonadi/
|
||||
%{_qt4_plugindir}/sqldrivers/libqsqlite3.so
|
||||
|
||||
%files devel
|
||||
%{_bindir}/asapcat
|
||||
%{_includedir}/akonadi/
|
||||
%{_libdir}/pkgconfig/akonadi.pc
|
||||
%{_libdir}/libakonadiprotocolinternals.so
|
||||
%{_libdir}/cmake/Akonadi/
|
||||
|
||||
%post mysql
|
||||
%{_sbindir}/update-alternatives \
|
||||
--install %{_sysconfdir}/xdg/akonadi/akonadiserverrc \
|
||||
akonadiserverrc \
|
||||
%{_sysconfdir}/xdg/akonadi/akonadiserverrc.mysql \
|
||||
10
|
||||
|
||||
%postun mysql
|
||||
if [ $1 -eq 0 ]; then
|
||||
%{_sbindir}/update-alternatives \
|
||||
--remove akonadiserverrc \
|
||||
%{_sysconfdir}/xdg/akonadi/akonadiserverrc.mysql
|
||||
fi
|
||||
|
||||
%files mysql
|
||||
%config(noreplace) %{_sysconfdir}/xdg/akonadi/akonadiserverrc.mysql
|
||||
%config(noreplace) %{_sysconfdir}/akonadi/mysql-global.conf
|
||||
%config(noreplace) %{_sysconfdir}/akonadi/mysql-local.conf
|
||||
# example conf's
|
||||
%{_sysconfdir}/akonadi/mysql-global-big.conf
|
||||
%{_sysconfdir}/akonadi/mysql-global-mobile.conf
|
||||
%{_datadir}/dbus-1/interfaces/org.freedesktop.Akonadi.*.xml
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Jan 16 2026 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-130
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild
|
||||
|
||||
* Wed Jul 23 2025 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-129
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild
|
||||
|
||||
* Thu Jan 16 2025 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-128
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild
|
||||
|
||||
* Wed Jul 17 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-127
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
|
||||
|
||||
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-126
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Fri Jan 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-125
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Wed Jul 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-124
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||
|
||||
* Mon Jun 12 2023 Than Ngo <than@redhat.com> - 1.13.0-123
|
||||
- migrated to SPDX license
|
||||
|
||||
* Wed Jan 18 2023 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-122
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||
|
||||
* Wed Jul 20 2022 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-121
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
|
||||
|
||||
* Wed Jan 19 2022 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-120
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
|
||||
|
||||
* Wed Jul 21 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-119
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
|
||||
|
||||
* Mon Jan 25 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-118
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
|
||||
|
||||
* Fri Jul 31 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-117
|
||||
- Second attempt - Rebuilt for
|
||||
https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
|
||||
|
||||
* Mon Jul 27 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-116
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
|
||||
|
||||
* Tue Jan 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-115
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
|
||||
|
||||
* Wed Jul 24 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-114
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
|
||||
|
||||
* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-113
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||
|
||||
* Fri Jan 25 2019 Jonathan Wakely <jwakely@redhat.com> - 1.13.0-112
|
||||
- Rebuilt for Boost 1.69
|
||||
|
||||
* Thu Jul 12 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-111
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
|
||||
|
||||
* Tue Feb 20 2018 Rex Dieter <rdieter@fedoraproject.org> - 1.13.0-110
|
||||
- BR: gcc-c++, use %%ldconfig_scriptlets
|
||||
|
||||
* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-109
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
|
||||
|
||||
* Tue Jan 23 2018 Jonathan Wakely <jwakely@redhat.com> - 1.13.0-108
|
||||
- Rebuilt for Boost 1.66
|
||||
|
||||
* Sun Aug 06 2017 Björn Esser <besser82@fedoraproject.org> - 1.13.0-107
|
||||
- Rebuilt for AutoReq cmake-filesystem
|
||||
|
||||
* Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-106
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
|
||||
|
||||
* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-105
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
|
||||
|
||||
* Tue Jul 18 2017 Jonathan Wakely <jwakely@redhat.com> - 1.13.0-104
|
||||
- Rebuilt for Boost 1.64
|
||||
|
||||
* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-103
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
|
||||
|
||||
* Wed Feb 03 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.0-102
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
|
||||
|
||||
* Thu Dec 17 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-101
|
||||
- -devel: re-enable dbus-1/interfaces, Conflicts: kf5-akonadi-server-devel
|
||||
|
||||
* Fri Dec 11 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-100
|
||||
- for kf5 kdepim world, build libakonadi bits only (omit server and related files)
|
||||
|
||||
* Thu Nov 12 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-22
|
||||
- Recommends: akonadi-mysql
|
||||
|
||||
* Sun Aug 30 2015 Jonathan Wakely <jwakely@redhat.com> 1.13.0-21
|
||||
- Rebuilt for Boost 1.59
|
||||
|
||||
* Wed Aug 05 2015 Jonathan Wakely <jwakely@redhat.com> 1.13.0-20
|
||||
- Rebuilt for Boost 1.58
|
||||
|
||||
* Fri Jul 31 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-19
|
||||
- pull in latest 1.13 branch fixes
|
||||
|
||||
* Wed Jul 29 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.13.0-18
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159
|
||||
|
||||
* Wed Jul 22 2015 David Tardon <dtardon@redhat.com> - 1.13.0-17
|
||||
- rebuild for Boost 1.58
|
||||
|
||||
* Mon Jun 29 2015 Daniel Vrátil <dvratil@redhat.com> - 1.13.0-16
|
||||
- pull upstream fix for KDE#341884
|
||||
|
||||
* Tue Jun 16 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.13.0-15
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
|
||||
|
||||
* Thu May 07 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-14
|
||||
- %%build: explicitly set -DCMAKE_BUILD_TYPE="Release" (-DQT_NO_DEBUG was used already)
|
||||
|
||||
* Mon Apr 27 2015 Daniel Vrátil <dvratil@redhat.com> - 1.13.0-13
|
||||
- Rebuild for boost
|
||||
|
||||
* Sun Mar 08 2015 Rex Dieter <rdieter@fedoraproject.org> - 1.13.0-12
|
||||
- explicit BuildRequires: mariadb-server
|
||||
- -mysql: Recommends: mariadb-server (f21+, #1199797)
|
||||
|
||||
* Sun Mar 08 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-11
|
||||
- explicitly Requires: mariadb(-sesrver) only as needed (#1199797)
|
||||
|
||||
* Wed Feb 18 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-10
|
||||
- rebuild (gcc5)
|
||||
|
||||
* Wed Feb 04 2015 Petr Machata <pmachata@redhat.com> - 1.13.0-9
|
||||
- Bump for rebuild.
|
||||
|
||||
* Sat Jan 31 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-8
|
||||
- latest 1.13 branch fixes
|
||||
|
||||
* Tue Jan 27 2015 Petr Machata <pmachata@redhat.com> - 1.13.0-7
|
||||
- Rebuild for boost 1.57.0
|
||||
|
||||
* Thu Jan 08 2015 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-6
|
||||
- drop el6/cmake hacks
|
||||
|
||||
* Fri Oct 31 2014 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-5
|
||||
- latest 1.13 branch fixes
|
||||
|
||||
* Sat Sep 27 2014 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-4
|
||||
- explicitly Requires: mariadb-server/mysql-server as appropriate
|
||||
|
||||
* Mon Sep 15 2014 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-3
|
||||
- pull in some upstream fixes
|
||||
|
||||
* Fri Aug 15 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.13.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
|
||||
|
||||
* Thu Aug 14 2014 Rex Dieter <rdieter@fedoraproject.org> 1.13.0-1
|
||||
- 1.13.0
|
||||
|
||||
* Mon Aug 04 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.91-1
|
||||
- 1.12.91
|
||||
|
||||
* Tue Jul 08 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-10
|
||||
- scriptlet polish
|
||||
|
||||
* Thu Jul 03 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-9
|
||||
- optimized mimeinfo scriptlet
|
||||
|
||||
* Mon Jun 09 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-8
|
||||
- pull in latest 1.12 branch commits
|
||||
|
||||
* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.12.1-7
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
|
||||
|
||||
* Thu May 22 2014 Petr Machata <pmachata@redhat.com> - 1.12.1-6
|
||||
- Rebuild for boost 1.55.0
|
||||
|
||||
* Tue Apr 22 2014 Daniel Vrátil <dvratil@redhat.com> 1.12.1-5
|
||||
- backport 1.12.2 patch to fix upgrade from Akonadi < 1.12 for users with invalid entries in DB
|
||||
|
||||
* Wed Apr 16 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-4
|
||||
- backport master/ branch commits to test sqlite backend concurrency support
|
||||
|
||||
* Wed Apr 16 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-3
|
||||
- WITH_SOPRANO=OFF (kde-4.13,fc21+)
|
||||
|
||||
* Tue Apr 15 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-2
|
||||
- drop mysql-global-mobile.conf, it's too minimalistic
|
||||
- drop Requires: qt4 >= 4.8.5-10 (workaround for psql driver bug had only a small window a long time ago)
|
||||
|
||||
* Tue Apr 08 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.1-1
|
||||
- 1.12.1
|
||||
|
||||
* Wed Mar 26 2014 Rex Dieter <rdieter@fedoraproject.org> 1.12.0-1
|
||||
- 1.12.0
|
||||
|
||||
* Wed Mar 19 2014 Daniel Vrátil <dvratil@redhat.com> 1.11.90-1
|
||||
- 1.11.90
|
||||
|
||||
* Mon Mar 17 2014 Rex Dieter <rdieter@fedoraproject.org> 1.11.80-1
|
||||
- 1.11.80
|
||||
|
||||
* Mon Feb 03 2014 Rex Dieter <rdieter@fedoraproject.org> 1.11.0-2
|
||||
- rebuild
|
||||
|
||||
* Sat Nov 30 2013 Rex Dieter <rdieter@fedoraproject.org> 1.11.0-1
|
||||
- 1.11.0
|
||||
|
||||
* Fri Nov 15 2013 Rex Dieter <rdieter@fedoraproject.org> 1.10.80-1
|
||||
- 1.10.80
|
||||
|
||||
* Mon Oct 07 2013 Daniel Vrátil <dvratil@redhat.com> - 1.10.3-1
|
||||
- 1.10.3
|
||||
|
||||
|
|
|
|||
2
sources
2
sources
|
|
@ -1 +1 @@
|
|||
3929b765baa3dc0d548a26893c64abcf akonadi-1.10.3-1.tar.bz2
|
||||
84eb2e471bd6bdfe54a2a2f1d858c07d akonadi-1.13.0.tar.bz2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue