From 5e14a94748e5a1d775d15adcc7020c0a6467489a Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Fri, 15 Nov 2013 10:57:10 -0600 Subject: [PATCH 01/78] 1.10.80 --- .gitignore | 4 +--- akonadi.spec | 11 +++++------ sources | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index e9cc806..c2b810a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -/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.10.80.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index 4278cfb..a6650d7 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,7 +13,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.10.3 +Version: 1.10.80 Release: 1%{?dist} License: LGPLv2+ @@ -24,7 +24,7 @@ URL: http://community.kde.org/KDE_PIM/Akonadi Source0: akonadi-%{version}-%{snap}.tar.bz2 %else # Official release -Source0: http://download.kde.org/stable/akonadi/src/akonadi-%{version}-1.tar.bz2 +Source0: http://download.kde.org/stable/akonadi/src/akonadi-%{version}.tar.bz2 %endif ## mysql config @@ -140,10 +140,6 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" xvfb-run -a dbus-launch --exit-with-session make test -C %{_target_platform} ||: -%clean -rm -rf %{buildroot} - - %post -p /sbin/ldconfig %posttrans @@ -205,6 +201,9 @@ fi %changelog +* Fri Nov 15 2013 Rex Dieter 1.10.80-1 +- 1.10.80 + * Mon Oct 07 2013 Daniel Vrátil - 1.10.3-1 - 1.10.3 diff --git a/sources b/sources index 18ca667..7aae25b 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3929b765baa3dc0d548a26893c64abcf akonadi-1.10.3-1.tar.bz2 +a1e580f740b5d238eb82df72ae25ebbd akonadi-1.10.80.tar.bz2 From 6fef9757f6decc3b1381c121e20e35275c288d10 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sat, 30 Nov 2013 21:17:45 -0600 Subject: [PATCH 02/78] 1.11.0 --- .gitignore | 2 +- akonadi.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index c2b810a..07e1588 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /akonadi-1.10.3-1.tar.bz2 -/akonadi-1.10.80.tar.bz2 +/akonadi-1.11.0.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index a6650d7..2b6a745 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,7 +13,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.10.80 +Version: 1.11.0 Release: 1%{?dist} License: LGPLv2+ @@ -201,6 +201,9 @@ fi %changelog +* Sat Nov 30 2013 Rex Dieter 1.11.0-1 +- 1.11.0 + * Fri Nov 15 2013 Rex Dieter 1.10.80-1 - 1.10.80 diff --git a/sources b/sources index 7aae25b..c7ffb05 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -a1e580f740b5d238eb82df72ae25ebbd akonadi-1.10.80.tar.bz2 +580361613d04b260f807b2a4df099eca akonadi-1.11.0.tar.bz2 From 8a132797b8ec5adbbd92eeab7740bc0b70c484ad Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Mon, 3 Feb 2014 17:23:20 -0600 Subject: [PATCH 03/78] rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 2b6a745..c935133 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -14,7 +14,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.11.0 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -201,6 +201,9 @@ fi %changelog +* Mon Feb 03 2014 Rex Dieter 1.11.0-2 +- rebuild + * Sat Nov 30 2013 Rex Dieter 1.11.0-1 - 1.11.0 From 00571966d421b2b725c4e0ad776ef9d2c0ca1680 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Mon, 17 Mar 2014 13:44:59 -0500 Subject: [PATCH 04/78] 1.11.80 --- .gitignore | 2 +- akonadi.spec | 7 +++++-- sources | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 07e1588..3bdea4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/akonadi-1.10.3-1.tar.bz2 /akonadi-1.11.0.tar.bz2 +/akonadi-1.11.80.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index c935133..04a1e82 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,8 +13,8 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.11.0 -Release: 2%{?dist} +Version: 1.11.80 +Release: 1%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -201,6 +201,9 @@ fi %changelog +* Mon Mar 17 2014 Rex Dieter 1.11.80-1 +- 1.11.80 + * Mon Feb 03 2014 Rex Dieter 1.11.0-2 - rebuild diff --git a/sources b/sources index c7ffb05..b9d9a57 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -580361613d04b260f807b2a4df099eca akonadi-1.11.0.tar.bz2 +5f32da9e6370db56980d888f888cc3de akonadi-1.11.80.tar.bz2 From ddb5148507dab442da9fba884a8a7ddb84886883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Wed, 19 Mar 2014 18:21:01 +0100 Subject: [PATCH 05/78] 1.11.90 --- .gitignore | 1 + akonadi.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3bdea4b..9a0eabc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /akonadi-1.11.0.tar.bz2 /akonadi-1.11.80.tar.bz2 +/akonadi-1.11.90.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index 04a1e82..d59fe79 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,7 +13,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.11.80 +Version: 1.11.90 Release: 1%{?dist} License: LGPLv2+ @@ -201,6 +201,9 @@ fi %changelog +* Wed Mar 19 2014 Daniel Vrátil 1.11.90-1 +- 1.11.90 + * Mon Mar 17 2014 Rex Dieter 1.11.80-1 - 1.11.80 diff --git a/sources b/sources index b9d9a57..f8d4b41 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -5f32da9e6370db56980d888f888cc3de akonadi-1.11.80.tar.bz2 +8210c086a3c554a2aada8335f29ff730 akonadi-1.11.90.tar.bz2 From 998c8cf8611c72996097a8948dac1114f4d1756f Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Wed, 26 Mar 2014 21:20:26 -0500 Subject: [PATCH 06/78] 1.12.0 --- .gitignore | 4 +--- akonadi.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 9a0eabc..3905e7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -/akonadi-1.11.0.tar.bz2 -/akonadi-1.11.80.tar.bz2 -/akonadi-1.11.90.tar.bz2 +/akonadi-1.12.0.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index d59fe79..3d7fa2f 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,7 +13,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.11.90 +Version: 1.12.0 Release: 1%{?dist} License: LGPLv2+ @@ -201,6 +201,9 @@ fi %changelog +* Wed Mar 26 2014 Rex Dieter 1.12.0-1 +- 1.12.0 + * Wed Mar 19 2014 Daniel Vrátil 1.11.90-1 - 1.11.90 diff --git a/sources b/sources index f8d4b41..3690e40 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -8210c086a3c554a2aada8335f29ff730 akonadi-1.11.90.tar.bz2 +3fb6703072410534bce51bbc3e6aa6e4 akonadi-1.12.0.tar.bz2 From 9fdf2bb2a70464a7b032ae771fac54176a05f84d Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Tue, 8 Apr 2014 20:59:14 -0500 Subject: [PATCH 07/78] 1.12.1 --- .gitignore | 2 +- akonadi.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3905e7f..c30bf02 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/akonadi-1.12.0.tar.bz2 +/akonadi-1.12.1.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index 3d7fa2f..c61c5ed 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -13,7 +13,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.12.0 +Version: 1.12.1 Release: 1%{?dist} License: LGPLv2+ @@ -201,6 +201,9 @@ fi %changelog +* Tue Apr 08 2014 Rex Dieter 1.12.1-1 +- 1.12.1 + * Wed Mar 26 2014 Rex Dieter 1.12.0-1 - 1.12.0 diff --git a/sources b/sources index 3690e40..7eb8642 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3fb6703072410534bce51bbc3e6aa6e4 akonadi-1.12.0.tar.bz2 +9a4a99d10e003a267a515fc60de4f817 akonadi-1.12.1.tar.bz2 From 215b266c1a150600c32144e5e84e21cda84b2834 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Tue, 15 Apr 2014 08:44:48 -0500 Subject: [PATCH 08/78] 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) --- akonadi.spec | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index c61c5ed..95bcf26 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -14,7 +14,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -34,7 +34,7 @@ Source10: akonadiserverrc.mysql ## upstream patches -%define mysql_conf_timestamp 20130607 +%define mysql_conf_timestamp 20140415 BuildRequires: automoc4 BuildRequires: boost-devel @@ -52,8 +52,7 @@ BuildRequires: dbus-x11 xorg-x11-server-Xvfb BuildRequires: mysql-server BuildRequires: postgresql-server -#%{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} -Requires: qt4%{?_isa} >= 4.8.5-10 +%{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} Requires(postun): /sbin/ldconfig @@ -103,23 +102,12 @@ make %{?_smp_mflags} -C %{_target_platform} %install -rm -rf %{buildroot} make install/fast DESTDIR=$RPM_BUILD_ROOT -C %{_target_platform} 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 @@ -131,6 +119,10 @@ mkdir -p %{buildroot}%{_libdir}/akonadi touch akonadiserverrc install -p -m644 -D akonadiserverrc %{buildroot}%{_sysconfdir}/xdg/akonadi/akonadiserverrc +## unpackaged files +# omit mysql-global-mobile.conf +rm -fv %{buildroot}%{_sysconfdir}/akonadi/mysql-global-mobile.conf + %check export PKG_CONFIG_PATH=%{buildroot}%{_datadir}/pkgconfig:%{buildroot}%{_libdir}/pkgconfig @@ -195,12 +187,13 @@ fi %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 %changelog +* Tue Apr 15 2014 Rex Dieter 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 1.12.1-1 - 1.12.1 From fdc262e626173bca787f2298c95dacd25fe1e74c Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Wed, 16 Apr 2014 06:55:05 -0500 Subject: [PATCH 09/78] WITH_SOPRANO=OFF (kde-4.13,fc21+) --- akonadi.spec | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 95bcf26..3f2aa3f 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -11,10 +11,15 @@ %define cmake_pkg cmake %endif +# legacy nepomuk/soprano support (ie, kde < 4.13) +%if 0%{?fedora} < 21 +%define soprano 1 +%endif + Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 2%{?dist} +Release: 3%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -43,7 +48,9 @@ BuildRequires: %{cmake_pkg} >= 2.8.8 BuildRequires: libxslt BuildRequires: pkgconfig(QtDBus) pkgconfig(QtSql) pkgconfig(QtXml) BuildRequires: pkgconfig(shared-mime-info) +%if 0%{?soprano} BuildRequires: pkgconfig(soprano) +%endif BuildRequires: pkgconfig(sqlite3) >= 3.6.23 # %%check BuildRequires: dbus-x11 xorg-x11-server-Xvfb @@ -95,6 +102,7 @@ pushd %{_target_platform} -DCONFIG_INSTALL_DIR=%{_sysconfdir} \ %{?database_backend:-DDATABASE_BACKEND=%{database_backend}} \ -DINSTALL_QSQLITE_IN_QT_PREFIX:BOOL=ON \ + -DWITH_SOPRANO:BOOL=%{?soprano:ON}%{!?soprano:OFF} \ .. popd @@ -190,6 +198,9 @@ fi %changelog +* Wed Apr 16 2014 Rex Dieter 1.12.1-3 +- WITH_SOPRANO=OFF (kde-4.13,fc21+) + * Tue Apr 15 2014 Rex Dieter 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) From fcd69dcca69f9d9419a5831dedebba8324213ef1 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Wed, 16 Apr 2014 09:23:26 -0500 Subject: [PATCH 10/78] backport master/ branch commits to test sqlite backend concurrency support --- ...rrency-in-our-copy-of-QSQLITE-driver.patch | 190 ++++++++++++++++++ ...ransaction-mutex-for-QSQLITE3-and-en.patch | 167 +++++++++++++++ akonadi.spec | 11 +- 3 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch create mode 100644 0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch diff --git a/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch b/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch new file mode 100644 index 0000000..5717639 --- /dev/null +++ b/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch @@ -0,0 +1,190 @@ +diff --git a/qsqlite/src/qsql_sqlite.cpp b/qsqlite/src/qsql_sqlite.cpp +index c1e9508..5da232f 100644 +--- a/qsqlite/src/qsql_sqlite.cpp ++++ b/qsqlite/src/qsql_sqlite.cpp +@@ -528,7 +528,7 @@ static int qGetSqliteOpenMode(QString opts) + return SQLITE_OPEN_READONLY; + } + // The SQLITE_OPEN_NOMUTEX flag causes the database connection to be in the multi-thread mode +- return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; ++ return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_SHAREDCACHE; + } + + /* +@@ -543,8 +543,10 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c + if (db.isEmpty()) + return false; + ++ sqlite3_enable_shared_cache(1); + if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, qGetSqliteOpenMode(conOpts), NULL) == SQLITE_OK) { + sqlite3_busy_timeout(d->access, qGetSqliteTimeout(conOpts)); ++ sqlite3_extended_result_codes(d->access, 1); + setOpen(true); + setOpenError(false); + return true; +diff --git a/qsqlite/src/sqlite_blocking.cpp b/qsqlite/src/sqlite_blocking.cpp +index c0fe3f2..180685c 100644 +--- a/qsqlite/src/sqlite_blocking.cpp ++++ b/qsqlite/src/sqlite_blocking.cpp +@@ -1,63 +1,94 @@ ++/* ++ Copyright (c) 2009 Bertjan Broeksema ++ Copyright (c) 2014 Daniel Vrátil ++ ++ This library is free software; you can redistribute it and/or modify it ++ under the terms of the GNU Library General Public License as published by ++ the Free Software Foundation; either version 2 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 Library General Public ++ License for more details. ++ ++ You should have received a copy of the GNU Library General Public License ++ along with this library; see the file COPYING.LIB. If not, write to the ++ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ 02110-1301, USA. ++*/ ++ + #include "sqlite_blocking.h" + + #include +-#ifndef _WIN32 +-#include +-#else +-#include +-#define usleep(x) Sleep(x/1000) +-#endif +- +-#include "qdebug.h" ++ ++#include ++#include + #include "qstringbuilder.h" + #include "qthread.h" ++#include ++ ++/* Based on example in http://www.sqlite.org/unlock_notify.html */ + +-QString debugString() ++struct UnlockNotification { ++ bool fired; ++ QWaitCondition cond; ++ QMutex mutex; ++}; ++ ++static void qSqlite3UnlockNotifyCb(void **apArg, int nArg) + { +- return QString( QLatin1Literal("[QSQLITE3: ") + QString::number( quint64( QThread::currentThreadId() ) ) + QLatin1Literal("] ") ); ++ for (int i = 0; i < nArg; ++i) { ++ UnlockNotification *ntf = static_cast(apArg[i]); ++ ntf->mutex.lock(); ++ ntf->fired = true; ++ ntf->cond.wakeOne(); ++ ntf->mutex.unlock(); ++ } + } + +-int sqlite3_blocking_step( sqlite3_stmt *pStmt ) ++static int qSqlite3WaitForUnlockNotify(sqlite3 *db) + { +- // NOTE: The example at http://www.sqlite.org/unlock_notify.html says to wait +- // for SQLITE_LOCK but for some reason I don't understand I get +- // SQLITE_BUSY. +- int rc = sqlite3_step( pStmt ); +- +- QThread::currentThreadId(); +- if ( rc == SQLITE_BUSY ) +- qDebug() << debugString() << "sqlite3_blocking_step: Entering while loop"; +- +- while( rc == SQLITE_BUSY ) { +- usleep(5000); +- sqlite3_reset( pStmt ); +- rc = sqlite3_step( pStmt ); +- +- if ( rc != SQLITE_BUSY ) { +- qDebug() << debugString() << "sqlite3_blocking_step: Leaving while loop"; ++ int rc; ++ UnlockNotification un; ++ un.fired = false; ++ ++ rc = sqlite3_unlock_notify(db, qSqlite3UnlockNotifyCb, (void *)&un); ++ Q_ASSERT(rc == SQLITE_LOCKED || rc == SQLITE_OK); ++ ++ if (rc == SQLITE_OK) { ++ un.mutex.lock(); ++ if (!un.fired) { ++ un.cond.wait(&un.mutex); + } ++ un.mutex.unlock(); + } + + return rc; + } + +-int sqlite3_blocking_prepare16_v2( sqlite3 *db, /* Database handle. */ +- const void *zSql, /* SQL statement, UTF-16 encoded */ +- int nSql, /* Length of zSql in bytes. */ +- sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ +- const void **pzTail /* OUT: Pointer to unused portion of zSql */ ) ++int sqlite3_blocking_step(sqlite3_stmt *pStmt) + { +- int rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); +- +- if ( rc == SQLITE_BUSY ) +- qDebug() << debugString() << "sqlite3_blocking_prepare16_v2: Entering while loop"; ++ int rc; ++ while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_step(pStmt))) { ++ rc = qSqlite3WaitForUnlockNotify(sqlite3_db_handle(pStmt)); ++ if (rc != SQLITE_OK) { ++ break; ++ } ++ sqlite3_reset(pStmt); ++ } + +- while( rc == SQLITE_BUSY ) { +- usleep(500000); +- rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); ++ return rc; ++} + +- if ( rc != SQLITE_BUSY ) { +- qDebug() << debugString() << "sqlite3_prepare16_v2: Leaving while loop"; ++int sqlite3_blocking_prepare16_v2(sqlite3 *db, const void *zSql, int nSql, ++ sqlite3_stmt **ppStmt, const void **pzTail) ++{ ++ int rc; ++ while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_prepare16_v2(db, zSql, nSql, ppStmt, pzTail))) { ++ rc = qSqlite3WaitForUnlockNotify(db); ++ if (rc != SQLITE_OK) { ++ break; + } + } + +diff --git a/qsqlite/src/sqlite_blocking.h b/qsqlite/src/sqlite_blocking.h +index 0d6f6a0..9f13946 100644 +--- a/qsqlite/src/sqlite_blocking.h ++++ b/qsqlite/src/sqlite_blocking.h +@@ -1,3 +1,22 @@ ++/* ++ Copyright (c) 2009 Bertjan Broeksema ++ ++ This library is free software; you can redistribute it and/or modify it ++ under the terms of the GNU Library General Public License as published by ++ the Free Software Foundation; either version 2 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 Library General Public ++ License for more details. ++ ++ You should have received a copy of the GNU Library General Public License ++ along with this library; see the file COPYING.LIB. If not, write to the ++ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ 02110-1301, USA. ++*/ ++ + #ifndef SQLITE_BLOCKING_H + #define SQLITE_BLOCKING_H diff --git a/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch b/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch new file mode 100644 index 0000000..34de4e1 --- /dev/null +++ b/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch @@ -0,0 +1,167 @@ +From 24413dc44b0637d6c64e6b2105c2bcf1b99849a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Sun, 6 Apr 2014 19:50:38 +0200 +Subject: [PATCH 13/16] Disable global transaction mutex for QSQLITE3 and + enable transaction recording + +Our QSQLITE3 driver now supports concurrency, so we don't need to serialize +transactions in DataStore anymore. It is however still needed for the +QSQLITE driver shipped with Qt. + +Secondary, concurrency support also means possible transactions deadlocks and +timeouts, so we also need to enable transaction recording and replaying for +the QSQLITE3 backend. +--- + server/src/storage/datastore.cpp | 18 +++++++++++------- + server/src/storage/datastore.h | 2 +- + server/src/storage/dbtype.cpp | 5 +++++ + server/src/storage/dbtype.h | 3 +++ + server/src/storage/querybuilder.cpp | 20 ++++++++++++++++---- + server/src/storage/querybuilder.h | 2 +- + 6 files changed, 37 insertions(+), 13 deletions(-) + +diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp +index 57d1e4e..0f04fa5 100644 +--- a/server/src/storage/datastore.cpp ++++ b/server/src/storage/datastore.cpp +@@ -61,8 +61,8 @@ using namespace Akonadi::Server; + static QMutex sTransactionMutex; + bool DataStore::s_hasForeignKeyConstraints = false; + +-#define TRANSACTION_MUTEX_LOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.lock() +-#define TRANSACTION_MUTEX_UNLOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.unlock() ++#define TRANSACTION_MUTEX_LOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.lock() ++#define TRANSACTION_MUTEX_UNLOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.unlock() + + /*************************************************************************** + * DataStore * +@@ -1083,23 +1083,27 @@ QDateTime DataStore::dateTimeToQDateTime( const QByteArray &dateTime ) + + void DataStore::addQueryToTransaction( const QSqlQuery &query, bool isBatch ) + { +- DbType::Type dbType = DbType::type( m_database ); + // This is used for replaying deadlocked transactions, so only record queries + // for backends that support concurrent transactions. +- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) { ++ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) { + return; + } + + m_transactionQueries.append( qMakePair( query, isBatch ) ); + } + +-QSqlQuery DataStore::retryLastTransaction() ++QSqlQuery DataStore::retryLastTransaction( bool rollbackFirst ) + { +- DbType::Type dbType = DbType::type( m_database ); +- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) { ++ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) { + return QSqlQuery(); + } + ++ if ( rollbackFirst ) { ++ // In some cases the SQL database won't rollback the failed transaction, so ++ // we need to do it manually ++ m_database.driver()->rollbackTransaction(); ++ } ++ + // The database has rolled back the actual transaction, so reset the counter + // to 0 and start a new one in beginTransaction(). Then restore the level + // because this has to be completely transparent to the original caller +diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h +index 8b4a2b7..8a0fe01 100644 +--- a/server/src/storage/datastore.h ++++ b/server/src/storage/datastore.h +@@ -317,7 +317,7 @@ protected: + * @return Returns an invalid query when error occurs, or the last replayed + * query on success. + */ +- QSqlQuery retryLastTransaction(); ++ QSqlQuery retryLastTransaction( bool rollbackFirst ); + + private Q_SLOTS: + void sendKeepAliveQuery(); +diff --git a/server/src/storage/dbtype.cpp b/server/src/storage/dbtype.cpp +index 495f532..7df2fb1 100644 +--- a/server/src/storage/dbtype.cpp ++++ b/server/src/storage/dbtype.cpp +@@ -39,3 +39,8 @@ DbType::Type DbType::typeForDriverName( const QString &driverName ) + } + return Unknown; + } ++ ++bool DbType::isSystemSQLite( const QSqlDatabase &db ) ++{ ++ return db.driverName() == QLatin1String( "QSQLITE" ); ++} +diff --git a/server/src/storage/dbtype.h b/server/src/storage/dbtype.h +index a95a833..3595604 100644 +--- a/server/src/storage/dbtype.h ++++ b/server/src/storage/dbtype.h +@@ -42,6 +42,9 @@ namespace DbType + /** Returns the type for the given driver name. */ + Type typeForDriverName( const QString &driverName ); + ++ /** Returns true when using QSQLITE driver shipped with Qt, FALSE otherwise */ ++ bool isSystemSQLite( const QSqlDatabase &db ); ++ + } // namespace DbType + } // namespace Server + } // namespace Akonadi +diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp +index 0abad4a..0530b11 100644 +--- a/server/src/storage/querybuilder.cpp ++++ b/server/src/storage/querybuilder.cpp +@@ -320,10 +320,10 @@ QString QueryBuilder::buildQuery() + return statement; + } + +-bool QueryBuilder::retryLastTransaction() ++bool QueryBuilder::retryLastTransaction( bool rollback ) + { + #ifndef QUERYBUILDER_UNITTEST +- mQuery = DataStore::self()->retryLastTransaction(); ++ mQuery = DataStore::self()->retryLastTransaction( rollback ); + return !mQuery.lastError().isValid(); + #else + return true; +@@ -400,9 +400,21 @@ bool QueryBuilder::exec() + akDebug() << mQuery.lastError().text(); + return retryLastTransaction(); + } ++ } else if ( mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite( DataStore::self()->database() ) ) { ++ const int error = mQuery.lastError().number(); ++ if ( error == 6 /* SQLITE_LOCKED */ ) { ++ akDebug() << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; ++ akDebug() << mQuery.lastError().text(); ++ return retryLastTransaction(); ++ } else if ( error == 5 /* SQLITE_BUSY */ ) { ++ akDebug() << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; ++ akDebug() << mQuery.lastError().text(); ++ return retryLastTransaction( true ); ++ } + } else if ( mDatabaseType == DbType::Sqlite ) { +- // We can't have a transaction deadlock in SQLite, because it does not support +- // concurrent transactions and DataStore serializes them through a global lock. ++ // We can't have a transaction deadlock in SQLite when using driver shipped ++ // with Qt, because it does not support concurrent transactions and DataStore ++ // serializes them through a global lock. + } + + akError() << "DATABASE ERROR:"; +diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h +index 235a099..b380f93 100644 +--- a/server/src/storage/querybuilder.h ++++ b/server/src/storage/querybuilder.h +@@ -244,7 +244,7 @@ class QueryBuilder + */ + void sqliteAdaptUpdateJoin( Query::Condition &cond ); + +- bool retryLastTransaction(); ++ bool retryLastTransaction( bool rollback = false); + + private: + QString mTable; +-- +1.9.0 + diff --git a/akonadi.spec b/akonadi.spec index 3f2aa3f..936333e 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 3%{?dist} +Release: 4%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -38,6 +38,9 @@ Source10: akonadiserverrc.mysql ## upstreamable patches ## upstream patches +# master branch +Patch212: 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch +Patch213: 0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch %define mysql_conf_timestamp 20140415 @@ -94,6 +97,9 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %prep %setup -q -n akonadi-%{version} +%patch212 -p1 -b .0012 +%patch213 -p1 -b .0013 + %build mkdir -p %{_target_platform} @@ -198,6 +204,9 @@ fi %changelog +* Wed Apr 16 2014 Rex Dieter 1.12.1-4 +- backport master/ branch commits to test sqlite backend concurrency support + * Wed Apr 16 2014 Rex Dieter 1.12.1-3 - WITH_SOPRANO=OFF (kde-4.13,fc21+) From 660e892d222ff65e0c5f89a62222c90aa8a76b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Tue, 22 Apr 2014 11:40:30 +0200 Subject: [PATCH 11/78] Backport 1.12 commit to fix upgrade from Akonadi<1.12 for users with invalid DB entries --- ...alid-part-before-parttable-migration.patch | 41 +++++++++++++++++++ akonadi.spec | 9 +++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 0001-remove-invalid-part-before-parttable-migration.patch diff --git a/0001-remove-invalid-part-before-parttable-migration.patch b/0001-remove-invalid-part-before-parttable-migration.patch new file mode 100644 index 0000000..a367067 --- /dev/null +++ b/0001-remove-invalid-part-before-parttable-migration.patch @@ -0,0 +1,41 @@ +commit 4ca8b846baaad48ebbd723f6411f9571a3b0f5ad +Author: Dan Vrátil +Date: Tue Apr 22 11:28:07 2014 +0200 + + Remove the invalid GID part from PartTable before starting PartTable migration + + More people than we expected have invalid 'GID' part in their PartTable, + which breaks migration to schema 25, because it expects all part types + to have a valid name. + + To work around this fact, we DELETE all parts with name 'GID' from PartTable + before starting the actual migration. This will not fix the migration for + people with other invalid parts, but I haven't heard of any such. To make + this completely bullet-proof, we would need to iterate through all entries, + which would be massively slower than current INSERT INTO ... SELECT FROM approach. + + Distributions, this is a good choice for backporting into 1.12.1 ;-) + + BUG: 331867 + FIXED-IN: 1.12.2 + +diff --git a/server/src/storage/dbupdater.cpp b/server/src/storage/dbupdater.cpp +index ccaf584..d6368b5 100644 +--- a/server/src/storage/dbupdater.cpp ++++ b/server/src/storage/dbupdater.cpp +@@ -261,6 +261,15 @@ bool DbUpdater::complexUpdate_25() + } + } + ++ { ++ // It appears that more users than expected have the invalid "GID" part in their ++ // PartTable, which breaks the migration below (see BKO#331867), so we apply this ++ // wanna-be fix to remove the invalid part before we start the actual migration. ++ QueryBuilder qb( QLatin1String( "PartTable" ), QueryBuilder::Delete ); ++ qb.addValueCondition( QLatin1String( "PartTable.name" ), Query::Equals, QLatin1String( "GID" ) ); ++ qb.exec(); ++ } ++ + akDebug() << "Creating a PartTable_new"; + { + TableDescription description; diff --git a/akonadi.spec b/akonadi.spec index 936333e..d4d9d21 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 4%{?dist} +Release: 5%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -38,6 +38,9 @@ Source10: akonadiserverrc.mysql ## upstreamable patches ## upstream patches +# 1.12 branch +Patch101: 0001-remove-invalid-part-before-parttable-migration.patch + # master branch Patch212: 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch Patch213: 0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch @@ -97,6 +100,7 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %prep %setup -q -n akonadi-%{version} +%patch101 -p1 -b .0001 %patch212 -p1 -b .0012 %patch213 -p1 -b .0013 @@ -204,6 +208,9 @@ fi %changelog +* Tue Apr 22 2014 Daniel Vrátil 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 1.12.1-4 - backport master/ branch commits to test sqlite backend concurrency support From 7170ac89c4be65713139408b4350c621bd724674 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 22 May 2014 20:36:13 +0200 Subject: [PATCH 12/78] Rebuild for boost 1.55.0 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index d4d9d21..3868649 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 5%{?dist} +Release: 6%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -208,6 +208,9 @@ fi %changelog +* Thu May 22 2014 Petr Machata - 1.12.1-6 +- Rebuild for boost 1.55.0 + * Tue Apr 22 2014 Daniel Vrátil 1.12.1-5 - backport 1.12.2 patch to fix upgrade from Akonadi < 1.12 for users with invalid entries in DB From 6b7be2b3abe6e9684feef6397a163df591eaaffc Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Fri, 6 Jun 2014 19:21:48 -0500 Subject: [PATCH 13/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 3868649..cd1a53b 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 6%{?dist} +Release: 7%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -208,6 +208,9 @@ fi %changelog +* Sat Jun 07 2014 Fedora Release Engineering - 1.12.1-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + * Thu May 22 2014 Petr Machata - 1.12.1-6 - Rebuild for boost 1.55.0 From 78fb1bf7ea9886eca5a85b2cdbde7d23b8615765 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Mon, 9 Jun 2014 20:46:44 -0500 Subject: [PATCH 14/78] pull in latest 1.12 branch commits --- 0001-Use-per-thread-QDBusConnections.patch | 485 ++++++++++++++++++ ...alid-part-before-parttable-migration.patch | 41 -- ...id-GID-part-from-PartTable-before-st.patch | 47 ++ ...f-GID-from-SQL-query-result-in-Fetch.patch | 32 ++ ...tartup-when-akonadiserverrc-contains.patch | 85 +++ ...tart-when-postmaster.pid-is-not-remo.patch | 71 +++ ...m-an-argument-passed-to-postgres-ser.patch | 34 ++ ...-Don-t-listen-for-TCP-IP-connections.patch | 33 ++ akonadi.spec | 19 +- 9 files changed, 804 insertions(+), 43 deletions(-) create mode 100644 0001-Use-per-thread-QDBusConnections.patch delete mode 100644 0001-remove-invalid-part-before-parttable-migration.patch create mode 100644 0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch create mode 100644 0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch create mode 100644 0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch create mode 100644 0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch create mode 100644 0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch create mode 100644 0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch diff --git a/0001-Use-per-thread-QDBusConnections.patch b/0001-Use-per-thread-QDBusConnections.patch new file mode 100644 index 0000000..ccb0e62 --- /dev/null +++ b/0001-Use-per-thread-QDBusConnections.patch @@ -0,0 +1,485 @@ +From a5542751bc147c7f74131006339b55e6339b3e9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Thu, 17 Apr 2014 15:11:57 +0200 +Subject: [PATCH 1/7] Use per-thread QDBusConnections + +This moves DBusConnectionPool from Nepomuk search code to Akonadi and makes +use of it in search infrastructure and couple other classes that interact +with DBus from non-main thread. + +QDBusConnection is not thread-safe in Qt 4, so we need to workaround it by +having a connection for each thread. Qt 5 should be OK, so we can remove this +in Frameworks. + +This should fix random crashes I've been seeing when SearchTaskManager::addTask() +was called from multiple threads simultaneously. +--- + server/CMakeLists.txt | 2 +- + server/src/dbusconnectionpool.cpp | 62 +++++++++++++++++++++++++++++ + server/src/dbusconnectionpool.h | 41 +++++++++++++++++++ + server/src/handler/fetchhelper.cpp | 9 ++--- + server/src/nepomuk/dbusconnectionpool.cpp | 59 --------------------------- + server/src/nepomuk/dbusconnectionpool.h | 38 ------------------ + server/src/nepomuk/queryserviceclient.cpp | 4 +- + server/src/search/agentsearchinstance.cpp | 5 ++- + server/src/search/searchmanager.cpp | 3 +- + server/src/search/searchtaskmanager.cpp | 11 ++--- + server/src/search/searchtaskmanager.h | 2 - + server/src/storage/itemretrievalmanager.cpp | 6 +-- + server/src/storagejanitor.cpp | 3 +- + 13 files changed, 124 insertions(+), 121 deletions(-) + create mode 100644 server/src/dbusconnectionpool.cpp + create mode 100644 server/src/dbusconnectionpool.h + delete mode 100644 server/src/nepomuk/dbusconnectionpool.cpp + delete mode 100644 server/src/nepomuk/dbusconnectionpool.h + +diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt +index 571bead..1709348 100644 +--- a/server/CMakeLists.txt ++++ b/server/CMakeLists.txt +@@ -106,6 +106,7 @@ set(libakonadiprivate_SRCS + src/collectionscheduler.cpp + src/clientcapabilities.cpp + src/clientcapabilityaggregator.cpp ++ src/dbusconnectionpool.cpp + src/handler.cpp + src/handlerhelper.cpp + src/intervalcheck.cpp +@@ -206,7 +207,6 @@ if (Soprano_FOUND) + src/search/nepomuksearchengine.cpp + src/nepomuk/dbusoperators.cpp + src/nepomuk/queryserviceclient.cpp +- src/nepomuk/dbusconnectionpool.cpp + src/nepomuk/result.cpp + ) + +diff --git a/server/src/dbusconnectionpool.cpp b/server/src/dbusconnectionpool.cpp +new file mode 100644 +index 0000000..9aede4f +--- /dev/null ++++ b/server/src/dbusconnectionpool.cpp +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (C) 2010 Sebastian Trueg ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 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 ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public License ++ * along with this library; see the file COPYING.LIB. If not, write to ++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include "dbusconnectionpool.h" ++#include ++#include ++#include ++ ++namespace { ++QAtomicInt s_connectionCounter; ++ ++class DBusConnectionPoolPrivate ++{ ++public: ++ DBusConnectionPoolPrivate() ++ : m_connection( QDBusConnection::connectToBus( ++ QDBusConnection::SessionBus, ++ QString::fromLatin1("AkonadiServer-%1").arg(newNumber()) ) ) ++ { ++ } ++ ~DBusConnectionPoolPrivate() { ++ QDBusConnection::disconnectFromBus( m_connection.name() ); ++ } ++ ++ QDBusConnection connection() const { return m_connection; } ++ ++private: ++ static int newNumber() { ++ return s_connectionCounter.fetchAndAddAcquire( 1 ); ++ } ++ QDBusConnection m_connection; ++}; ++} ++ ++QThreadStorage s_perThreadConnection; ++ ++QDBusConnection Akonadi::Server::DBusConnectionPool::threadConnection() ++{ ++ if ( !QCoreApplication::instance() || QCoreApplication::instance()->thread() == QThread::currentThread() ) { ++ return QDBusConnection::sessionBus(); // main thread, use the default session bus ++ } ++ if ( !s_perThreadConnection.hasLocalData() ) { ++ s_perThreadConnection.setLocalData( new DBusConnectionPoolPrivate ); ++ } ++ return s_perThreadConnection.localData()->connection(); ++} +diff --git a/server/src/dbusconnectionpool.h b/server/src/dbusconnectionpool.h +new file mode 100644 +index 0000000..4f8a93e +--- /dev/null ++++ b/server/src/dbusconnectionpool.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (C) 2010 Sebastian Trueg ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 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 ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public License ++ * along with this library; see the file COPYING.LIB. If not, write to ++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef DBUSCONNECTIONPOOL_H ++#define DBUSCONNECTIONPOOL_H ++ ++#include ++ ++namespace Akonadi { ++namespace Server { ++namespace DBusConnectionPool { ++ ++/** ++ * Returns a new QDBusConnection for each thread, because QDBusConnection is ++ * not thread-safe in Qt 4. ++ * ++ * FIXME: Remove in KF5 ++ */ ++QDBusConnection threadConnection(); ++ ++} ++} ++} ++ ++#endif +diff --git a/server/src/handler/fetchhelper.cpp b/server/src/handler/fetchhelper.cpp +index 6284cc9..a6888a3 100644 +--- a/server/src/handler/fetchhelper.cpp ++++ b/server/src/handler/fetchhelper.cpp +@@ -38,6 +38,7 @@ + #include "utils.h" + #include "intervalcheck.h" + #include "agentmanagerinterface.h" ++#include "dbusconnectionpool.h" + + #include + #include +@@ -252,16 +253,12 @@ bool FetchHelper::isScopeLocal( const Scope &scope ) + + query.next(); + const QString resourceName = query.value( 0 ).toString(); +- // Workaround for QDBusConnectionPrivate not being thread-safe in Qt 4, fixed in Qt 5.2 +- // TODO: Remove in KF5 +- const QDBusConnection connection = QDBusConnection::connectToBus( QDBusConnection::SessionBus, +- QString::fromLatin1( mConnection->sessionId() ) ); ++ + org::freedesktop::Akonadi::AgentManager manager( AkDBus::serviceName( AkDBus::Control ), + QLatin1String( "/AgentManager" ), +- connection ); ++ DBusConnectionPool::threadConnection() ); + const QString typeIdentifier = manager.agentInstanceType( resourceName ); + const QVariantMap properties = manager.agentCustomProperties( typeIdentifier ); +- QDBusConnection::disconnectFromBus( QString::fromLatin1( mConnection->sessionId() ) ); + return properties.value( QLatin1String( "HasLocalStorage" ), false ).toBool(); + } + +diff --git a/server/src/nepomuk/dbusconnectionpool.cpp b/server/src/nepomuk/dbusconnectionpool.cpp +deleted file mode 100644 +index 598d16f..0000000 +--- a/server/src/nepomuk/dbusconnectionpool.cpp ++++ /dev/null +@@ -1,59 +0,0 @@ +-/* +- * This file is part of the Nepomuk KDE project. +- * Copyright (C) 2010 Sebastian Trueg +- * Copyright (C) 2010 David Faure +- * +- * This library is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Library General Public +- * License as published by the Free Software Foundation; either +- * version 2 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 +- * Library General Public License for more details. +- * +- * You should have received a copy of the GNU Library General Public License +- * along with this library; see the file COPYING.LIB. If not, write to +- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +- * Boston, MA 02110-1301, USA. +- */ +- +-#include "dbusconnectionpool.h" +-#include +- +-namespace { +-QAtomicInt s_connectionCounter; +- +-class DBusConnectionPoolPrivate +-{ +-public: +- DBusConnectionPoolPrivate() +- : m_connection( QDBusConnection::connectToBus( +- QDBusConnection::SessionBus, +- QString::fromLatin1( "NepomukQueryServiceConnection%1" ).arg( newNumber() ) ) ) +- { +- } +- ~DBusConnectionPoolPrivate() { +- QDBusConnection::disconnectFromBus( m_connection.name() ); +- } +- +- QDBusConnection connection() const { return m_connection; } +- +-private: +- static int newNumber() { +- return s_connectionCounter.fetchAndAddAcquire( 1 ); +- } +- QDBusConnection m_connection; +-}; +-} +- +-QThreadStorage s_perThreadConnection; +- +-QDBusConnection DBusConnectionPool::threadConnection() +-{ +- if ( !s_perThreadConnection.hasLocalData() ) { +- s_perThreadConnection.setLocalData( new DBusConnectionPoolPrivate ); +- } +- return s_perThreadConnection.localData()->connection(); +-} +diff --git a/server/src/nepomuk/dbusconnectionpool.h b/server/src/nepomuk/dbusconnectionpool.h +deleted file mode 100644 +index c5ac746..0000000 +--- a/server/src/nepomuk/dbusconnectionpool.h ++++ /dev/null +@@ -1,38 +0,0 @@ +-/* +- * This file is part of the Nepomuk KDE project. +- * Copyright (C) 2010 Sebastian Trueg +- * +- * This library is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Library General Public +- * License as published by the Free Software Foundation; either +- * version 2 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 +- * Library General Public License for more details. +- * +- * You should have received a copy of the GNU Library General Public License +- * along with this library; see the file COPYING.LIB. If not, write to +- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +- * Boston, MA 02110-1301, USA. +- */ +- +-#ifndef _DBUS_CONNECTION_POOL_H_ +-#define _DBUS_CONNECTION_POOL_H_ +- +-#include +- +-namespace DBusConnectionPool +-{ +-/** +- * The DBusConnectionPool works around the problem +- * of QDBusConnection not being thread-safe. As soon as that +- * has been fixed (either directly in libdbus or with a work- +- * around in Qt) this method can be dropped in favor of +- * QDBusConnection::sessionBus(). +- */ +-QDBusConnection threadConnection(); +-} +- +-#endif +diff --git a/server/src/nepomuk/queryserviceclient.cpp b/server/src/nepomuk/queryserviceclient.cpp +index 089a80b..b27a751 100644 +--- a/server/src/nepomuk/queryserviceclient.cpp ++++ b/server/src/nepomuk/queryserviceclient.cpp +@@ -24,7 +24,7 @@ + #include "result.h" + #include "queryserviceinterface.h" + #include "queryinterface.h" +-#include ++#include "dbusconnectionpool.h" + + #include + #include +@@ -40,7 +40,7 @@ public: + Private() + : queryServiceInterface( 0 ), + queryInterface( 0 ), +- dbusConnection( DBusConnectionPool::threadConnection() ), ++ dbusConnection( Akonadi::Server::DBusConnectionPool::threadConnection() ), + m_queryActive( false ), + loop( 0 ) { + } +diff --git a/server/src/search/agentsearchinstance.cpp b/server/src/search/agentsearchinstance.cpp +index 54ffab0..ca6ef87 100644 +--- a/server/src/search/agentsearchinstance.cpp ++++ b/server/src/search/agentsearchinstance.cpp +@@ -21,6 +21,7 @@ + #include "agentsearchinterface.h" + #include "searchtaskmanager.h" + #include "akdbus.h" ++#include "dbusconnectionpool.h" + + using namespace Akonadi::Server; + +@@ -43,7 +44,7 @@ bool AgentSearchInstance::init() + mInterface = new OrgFreedesktopAkonadiAgentSearchInterface( + AkDBus::agentServiceName( mId, AkDBus::Agent ), + QLatin1String( "/Search" ), +- QDBusConnection::sessionBus() ); ++ DBusConnectionPool::threadConnection() ); + + if ( !mInterface || !mInterface->isValid() ) { + delete mInterface; +@@ -52,7 +53,7 @@ bool AgentSearchInstance::init() + } + + mServiceWatcher = new QDBusServiceWatcher( AkDBus::agentServiceName( mId, AkDBus::Agent ), +- QDBusConnection::sessionBus(), ++ DBusConnectionPool::threadConnection(), + QDBusServiceWatcher::WatchForOwnerChange, + this ); + connect( mServiceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), +diff --git a/server/src/search/searchmanager.cpp b/server/src/search/searchmanager.cpp +index 139d058..1eafb27 100644 +--- a/server/src/search/searchmanager.cpp ++++ b/server/src/search/searchmanager.cpp +@@ -26,6 +26,7 @@ + #include "agentsearchengine.h" + #include "nepomuksearchengine.h" + #include "notificationmanager.h" ++#include "dbusconnectionpool.h" + #include "searchrequest.h" + #include "searchtaskmanager.h" + #include "storage/datastore.h" +@@ -98,7 +99,7 @@ SearchManager::SearchManager( const QStringList &searchEngines, QObject *parent + + new SearchManagerAdaptor( this ); + +- QDBusConnection::sessionBus().registerObject( ++ DBusConnectionPool::threadConnection().registerObject( + QLatin1String( "/SearchManager" ), + this, + QDBusConnection::ExportAdaptors ); +diff --git a/server/src/search/searchtaskmanager.cpp b/server/src/search/searchtaskmanager.cpp +index 51bb516..e7980a4 100644 +--- a/server/src/search/searchtaskmanager.cpp ++++ b/server/src/search/searchtaskmanager.cpp +@@ -23,6 +23,7 @@ + #include "akdbus.h" + #include "connection.h" + #include "storage/selectquerybuilder.h" ++#include "dbusconnectionpool.h" + #include + + #include +@@ -36,9 +37,6 @@ SearchTaskManager *SearchTaskManager::sInstance = 0; + SearchTaskManager::SearchTaskManager() + : QObject() + , mShouldStop( false ) +- , mAgentManager( AkDBus::serviceName( AkDBus::Control ), QLatin1String( "/AgentManager" ), +- QDBusConnection::sessionBus() ) +- + { + sInstance = this; + +@@ -125,13 +123,16 @@ void SearchTaskManager::addTask( SearchTask *task ) + } + + mInstancesLock.lock(); ++ ++ org::freedesktop::Akonadi::AgentManager agentManager( AkDBus::serviceName( AkDBus::Control ), QLatin1String( "/AgentManager" ), ++ DBusConnectionPool::threadConnection() ); + do { + const QString resourceId = query.value( 1 ).toString(); + if ( !mInstances.contains( resourceId ) ) { + akDebug() << "Resource" << resourceId << "does not implement Search interface, skipping"; +- } else if ( !mAgentManager.agentInstanceOnline( resourceId ) ) { ++ } else if ( !agentManager.agentInstanceOnline( resourceId ) ) { + akDebug() << "Agent" << resourceId << "is offline, skipping"; +- } else if ( mAgentManager.agentInstanceStatus( resourceId ) > 2 ) { // 2 == Broken, 3 == Not Configured ++ } else if ( agentManager.agentInstanceStatus( resourceId ) > 2 ) { // 2 == Broken, 3 == Not Configured + akDebug() << "Agent" << resourceId << "is broken or not configured"; + } else { + const qint64 collectionId = query.value( 0 ).toLongLong(); +diff --git a/server/src/search/searchtaskmanager.h b/server/src/search/searchtaskmanager.h +index 06e1b52..9b7972b 100644 +--- a/server/src/search/searchtaskmanager.h ++++ b/server/src/search/searchtaskmanager.h +@@ -97,8 +97,6 @@ class SearchTaskManager : public QObject + TasksMap::Iterator cancelRunningTask( TasksMap::Iterator &iter ); + bool allResourceTasksCompleted( SearchTask* ) const; + +- org::freedesktop::Akonadi::AgentManager mAgentManager; +- + QMap mInstances; + QMutex mInstancesLock; + +diff --git a/server/src/storage/itemretrievalmanager.cpp b/server/src/storage/itemretrievalmanager.cpp +index d49ba66..ce88410 100644 +--- a/server/src/storage/itemretrievalmanager.cpp ++++ b/server/src/storage/itemretrievalmanager.cpp +@@ -20,6 +20,7 @@ + #include "itemretrievalmanager.h" + #include "itemretrievalrequest.h" + #include "itemretrievaljob.h" ++#include "dbusconnectionpool.h" + + #include "resourceinterface.h" + +@@ -40,10 +41,7 @@ ItemRetrievalManager *ItemRetrievalManager::sInstance = 0; + + ItemRetrievalManager::ItemRetrievalManager( QObject *parent ) + : QObject( parent ), +- mDBusConnection( +- QDBusConnection::connectToBus( +- QDBusConnection::SessionBus, +- QString::fromLatin1( "AkonadiServerItemRetrievalManager" ) ) ) ++ mDBusConnection( DBusConnectionPool::threadConnection() ) + { + // make sure we are created from the retrieval thread and only once + Q_ASSERT( QThread::currentThread() != QCoreApplication::instance()->thread() ); +diff --git a/server/src/storagejanitor.cpp b/server/src/storagejanitor.cpp +index ebe7a24..af1a407 100644 +--- a/server/src/storagejanitor.cpp ++++ b/server/src/storagejanitor.cpp +@@ -26,6 +26,7 @@ + #include "storage/parthelper.h" + #include "resourcemanager.h" + #include "entities.h" ++#include "dbusconnectionpool.h" + + #include + #include +@@ -63,7 +64,7 @@ void StorageJanitorThread::run() + + StorageJanitor::StorageJanitor( QObject *parent ) + : QObject( parent ) +- , m_connection( QDBusConnection::connectToBus( QDBusConnection::SessionBus, QLatin1String( staticMetaObject.className() ) ) ) ++ , m_connection( DBusConnectionPool::threadConnection() ) + , m_lostFoundCollectionId( -1 ) + { + DataStore::self(); +-- +1.9.3 + diff --git a/0001-remove-invalid-part-before-parttable-migration.patch b/0001-remove-invalid-part-before-parttable-migration.patch deleted file mode 100644 index a367067..0000000 --- a/0001-remove-invalid-part-before-parttable-migration.patch +++ /dev/null @@ -1,41 +0,0 @@ -commit 4ca8b846baaad48ebbd723f6411f9571a3b0f5ad -Author: Dan Vrátil -Date: Tue Apr 22 11:28:07 2014 +0200 - - Remove the invalid GID part from PartTable before starting PartTable migration - - More people than we expected have invalid 'GID' part in their PartTable, - which breaks migration to schema 25, because it expects all part types - to have a valid name. - - To work around this fact, we DELETE all parts with name 'GID' from PartTable - before starting the actual migration. This will not fix the migration for - people with other invalid parts, but I haven't heard of any such. To make - this completely bullet-proof, we would need to iterate through all entries, - which would be massively slower than current INSERT INTO ... SELECT FROM approach. - - Distributions, this is a good choice for backporting into 1.12.1 ;-) - - BUG: 331867 - FIXED-IN: 1.12.2 - -diff --git a/server/src/storage/dbupdater.cpp b/server/src/storage/dbupdater.cpp -index ccaf584..d6368b5 100644 ---- a/server/src/storage/dbupdater.cpp -+++ b/server/src/storage/dbupdater.cpp -@@ -261,6 +261,15 @@ bool DbUpdater::complexUpdate_25() - } - } - -+ { -+ // It appears that more users than expected have the invalid "GID" part in their -+ // PartTable, which breaks the migration below (see BKO#331867), so we apply this -+ // wanna-be fix to remove the invalid part before we start the actual migration. -+ QueryBuilder qb( QLatin1String( "PartTable" ), QueryBuilder::Delete ); -+ qb.addValueCondition( QLatin1String( "PartTable.name" ), Query::Equals, QLatin1String( "GID" ) ); -+ qb.exec(); -+ } -+ - akDebug() << "Creating a PartTable_new"; - { - TableDescription description; diff --git a/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch b/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch new file mode 100644 index 0000000..dac2e31 --- /dev/null +++ b/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch @@ -0,0 +1,47 @@ +From 4ca8b846baaad48ebbd723f6411f9571a3b0f5ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Tue, 22 Apr 2014 11:28:07 +0200 +Subject: [PATCH 2/7] Remove the invalid GID part from PartTable before + starting PartTable migration + +More people than we expected have invalid 'GID' part in their PartTable, +which breaks migration to schema 25, because it expects all part types +to have a valid name. + +To work around this fact, we DELETE all parts with name 'GID' from PartTable +before starting the actual migration. This will not fix the migration for +people with other invalid parts, but I haven't heard of any such. To make +this completely bullet-proof, we would need to iterate through all entries, +which would be massively slower than current INSERT INTO ... SELECT FROM approach. + +Distributions, this is a good choice for backporting into 1.12.1 ;-) + +BUG: 331867 +FIXED-IN: 1.12.2 +--- + server/src/storage/dbupdater.cpp | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/server/src/storage/dbupdater.cpp b/server/src/storage/dbupdater.cpp +index ccaf584..d6368b5 100644 +--- a/server/src/storage/dbupdater.cpp ++++ b/server/src/storage/dbupdater.cpp +@@ -261,6 +261,15 @@ bool DbUpdater::complexUpdate_25() + } + } + ++ { ++ // It appears that more users than expected have the invalid "GID" part in their ++ // PartTable, which breaks the migration below (see BKO#331867), so we apply this ++ // wanna-be fix to remove the invalid part before we start the actual migration. ++ QueryBuilder qb( QLatin1String( "PartTable" ), QueryBuilder::Delete ); ++ qb.addValueCondition( QLatin1String( "PartTable.name" ), Query::Equals, QLatin1String( "GID" ) ); ++ qb.exec(); ++ } ++ + akDebug() << "Creating a PartTable_new"; + { + TableDescription description; +-- +1.9.3 + diff --git a/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch b/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch new file mode 100644 index 0000000..4bb61f7 --- /dev/null +++ b/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch @@ -0,0 +1,32 @@ +From 1d47dd274dd29bec42170d0cdf69cb9ac0b6686e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Thu, 24 Apr 2014 15:37:32 +0200 +Subject: [PATCH 3/7] Fix retrieving of GID from SQL query result in + FetchHelper + +This has been broken since the day one, but nobody noticed. I guess +we were lucky enough to always query other arguments, so that +ItemQueryPimItemGidColumn actually matched indexed of the GID column +in query. + +Another reason why we need proper unit-tests on the server... +--- + server/src/handler/fetchhelper.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/src/handler/fetchhelper.cpp b/server/src/handler/fetchhelper.cpp +index a6888a3..4d9f778 100644 +--- a/server/src/handler/fetchhelper.cpp ++++ b/server/src/handler/fetchhelper.cpp +@@ -367,7 +367,7 @@ bool FetchHelper::fetchItems( const QByteArray &responseIdentifier ) + } + } + if ( mFetchScope.gidRequested() ) { +- const QByteArray gid = Utils::variantToByteArray( itemQuery.value( ItemQueryPimItemGidColumn ) ); ++ const QByteArray gid = Utils::variantToByteArray( extractQueryResult( itemQuery, ItemQueryPimItemGidColumn ) ); + if ( !gid.isEmpty() ) { + attributes.append( AKONADI_PARAM_GID " " + ImapParser::quote( gid ) ); + } +-- +1.9.3 + diff --git a/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch b/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch new file mode 100644 index 0000000..4c2d3b9 --- /dev/null +++ b/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch @@ -0,0 +1,85 @@ +From f5bfc641c38ca0dbd37c8993bc74f81c7e0c9b66 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Tue, 6 May 2014 15:27:07 +0200 +Subject: [PATCH 4/7] Fix PostgreSQL startup when akonadiserverrc contains + empty values + +When there are empty configuration options in akonadiserverrc (like Host), +we would still pick them up and use them as valid values, which is wrong, +because then we work with empty socket path etc. + +Instead we always replace non-existent and empty config values with the +default ones, so that Akonadi with PostgreSQL is always able to start. +--- + server/src/storage/dbconfigpostgresql.cpp | 27 ++++++++++++++++++++------- + 1 file changed, 20 insertions(+), 7 deletions(-) + +diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp +index 10460d4..66e4605 100644 +--- a/server/src/storage/dbconfigpostgresql.cpp ++++ b/server/src/storage/dbconfigpostgresql.cpp +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + #include + +@@ -90,13 +91,29 @@ bool DbConfigPostgresql::init( QSettings &settings ) + // read settings for current driver + settings.beginGroup( driverName() ); + mDatabaseName = settings.value( QLatin1String( "Name" ), defaultDatabaseName() ).toString(); ++ if ( mDatabaseName.isEmpty() ) { ++ mDatabaseName = defaultDatabaseName(); ++ } + mHostName = settings.value( QLatin1String( "Host" ), defaultHostName ).toString(); ++ if ( mHostName.isEmpty() ) { ++ mHostName = defaultHostName; ++ } ++ // User, password and Options can be empty and still valid, so don't override them + mUserName = settings.value( QLatin1String( "User" ) ).toString(); + mPassword = settings.value( QLatin1String( "Password" ) ).toString(); + mConnectionOptions = settings.value( QLatin1String( "Options" ), defaultOptions ).toString(); + mServerPath = settings.value( QLatin1String( "ServerPath" ), defaultServerPath ).toString(); ++ if ( mInternalServer && mServerPath.isEmpty() ) { ++ mServerPath = defaultServerPath; ++ } + mInitDbPath = settings.value( QLatin1String( "InitDbPath" ), defaultInitDbPath ).toString(); ++ if ( mInternalServer && mInitDbPath.isEmpty() ) { ++ mInitDbPath = defaultInitDbPath; ++ } + mPgData = settings.value( QLatin1String( "PgData" ), defaultPgData ).toString(); ++ if ( mPgData.isEmpty() ) { ++ mPgData = defaultPgData; ++ } + settings.endGroup(); + + // store back the default values +@@ -104,12 +121,8 @@ bool DbConfigPostgresql::init( QSettings &settings ) + settings.setValue( QLatin1String( "Name" ), mDatabaseName ); + settings.setValue( QLatin1String( "Host" ), mHostName ); + settings.setValue( QLatin1String( "Options" ), mConnectionOptions ); +- if ( !mServerPath.isEmpty() ) { +- settings.setValue( QLatin1String( "ServerPath" ), mServerPath ); +- } +- if ( !mInitDbPath.isEmpty() ) { +- settings.setValue( QLatin1String( "InitDbPath" ), mInitDbPath ); +- } ++ settings.setValue( QLatin1String( "ServerPath" ), mServerPath ); ++ settings.setValue( QLatin1String( "InitDbPath" ), mInitDbPath ); + settings.setValue( QLatin1String( "StartServer" ), mInternalServer ); + settings.endGroup(); + settings.sync(); +@@ -203,7 +216,7 @@ void DbConfigPostgresql::startInternalServer() + break; + } + +- if ( pgCtl.waitForFinished( 500 ) ) { ++ if ( pgCtl.waitForFinished( 500 ) && pgCtl.exitCode() ) { + akError() << "Database process exited unexpectedly during initial connection!"; + akError() << "executable:" << mServerPath; + akError() << "arguments:" << arguments; +-- +1.9.3 + diff --git a/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch b/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch new file mode 100644 index 0000000..80dded8 --- /dev/null +++ b/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch @@ -0,0 +1,71 @@ +From f217d9e96dc8de88916e8ca0b93dd9a7500bbbc3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Tue, 6 May 2014 16:38:17 +0200 +Subject: [PATCH 5/7] Fix PostgreSQL start when postmaster.pid is not removed + after non-clean shutdown + +When PostgreSQL is not terminated nicely, a pidfile is left behind in db_data, +which will prevent pg_ctl from starting a new PostgreSQL server. + +We check for postmaster.pid file and verify that postgres server with PID +specified in the pidfile is not running anymore, then delete the pidfile to +allow pg_ctl to start a new server. If the postgres server is still running +(possibly after Akonadi server crash), we try to connect to it right away. + +BUG: 286826 +FIXED-IN: 1.12.2 +--- + server/src/storage/dbconfigpostgresql.cpp | 37 +++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp +index 66e4605..7b61819 100644 +--- a/server/src/storage/dbconfigpostgresql.cpp ++++ b/server/src/storage/dbconfigpostgresql.cpp +@@ -166,6 +166,43 @@ void DbConfigPostgresql::startInternalServer() + QDir().mkpath( socketDir ); + } + ++// TODO Windows support ++#ifndef Q_WS_WIN ++ // If postmaster.pid exists, check whether the postgres process still exists too, ++ // because normally we shouldn't be able to get this far if Akonadi is already ++ // running. If postgres is not running, then the pidfile was left after a system ++ // crash or something similar and we can remove it (otherwise pg_ctl won't start) ++ QFile postmaster( QString::fromLatin1( "%1/postmaster.pid" ).arg( mPgData ) ); ++ if ( postmaster.exists() && postmaster.open( QIODevice::ReadOnly ) ) { ++ qDebug() << "Found a postmaster.pid pidfile, checking whether the server is still running..."; ++ QByteArray pid = postmaster.readLine(); ++ // Remvoe newline character ++ pid.truncate(pid.size() - 1); ++ QFile proc( QString::fromLatin1( "/proc/" + pid + "/stat" ) ); ++ // Check whether the process with the PID from pidfile still exists and whether ++ // it's actually still postgres or, whether the PID has been recycled in the ++ // meanwhile. ++ if ( proc.open( QIODevice::ReadOnly ) ) { ++ const QByteArray stat = proc.readAll(); ++ const QList stats = stat.split( ' ' ); ++ if ( stats.count() > 1 ) { ++ // Make sure the PID actually belongs to postgres process ++ if ( stats[1] == "(postgres)" ) { ++ // Yup, our PostgreSQL is actually running, so pretend we started the server ++ // and try to connect to it ++ qWarning() << "PostgreSQL for Akonadi is already running, trying to connect to it."; ++ return; ++ } ++ } ++ proc.close(); ++ } ++ ++ qDebug() << "No postgres process with specified PID is running. Removing the pidfile and starting a new Postgres instance..."; ++ postmaster.close(); ++ postmaster.remove(); ++ } ++#endif ++ + if ( !QFile::exists( QString::fromLatin1( "%1/PG_VERSION" ).arg( mPgData ) ) ) { + // postgres data directory not initialized yet, so call initdb on it + +-- +1.9.3 + diff --git a/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch b/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch new file mode 100644 index 0000000..35dcdd7 --- /dev/null +++ b/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch @@ -0,0 +1,34 @@ +From 74b2d3a43e19f5262ec84ccf609925eeb071d755 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Tue, 6 May 2014 16:49:32 +0200 +Subject: [PATCH 6/7] Remove space from an argument passed to postgres server + +Some users reported problem with starting PostgreSQL when there's a space +between name and value of an argument passed to postgres. Removing the +space fixes problem for them (withouth breaking PostgreSQL for those who +didn't have a problem with the space) + +Thanks SergTruf for the patch. + +BUG: 332988 +FIXED-IN: 1.12.2 +--- + server/src/storage/dbconfigpostgresql.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp +index 7b61819..ce8067b 100644 +--- a/server/src/storage/dbconfigpostgresql.cpp ++++ b/server/src/storage/dbconfigpostgresql.cpp +@@ -223,7 +223,7 @@ void DbConfigPostgresql::startInternalServer() + << QString::fromLatin1( "--pgdata=%1" ).arg( mPgData ) + // set the directory for unix domain socket communication + // -o will pass the switch to postgres +- << QString::fromLatin1( "-o \"-k %1\"" ).arg( socketDir ); ++ << QString::fromLatin1( "-o \"-k%1\"" ).arg( socketDir ); + + QProcess pgCtl; + pgCtl.start( mServerPath, arguments ); +-- +1.9.3 + diff --git a/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch b/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch new file mode 100644 index 0000000..a401e82 --- /dev/null +++ b/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch @@ -0,0 +1,33 @@ +From 583ec681003395d5f3c3443c7ec82404b187afb9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Wed, 7 May 2014 14:22:13 +0200 +Subject: [PATCH 7/7] PostgreSQL: Don't listen for TCP/IP connections + +Fixes Akonadi with internal PostgreSQL not starting when system-wide PostgreSQL +server is already running. We don't need TCP/IP anyway, because we talk to our +PostgreSQL via a Unix-socket. +--- + server/src/storage/dbconfigpostgresql.cpp | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp +index ce8067b..83a5f91 100644 +--- a/server/src/storage/dbconfigpostgresql.cpp ++++ b/server/src/storage/dbconfigpostgresql.cpp +@@ -221,9 +221,10 @@ void DbConfigPostgresql::startInternalServer() + << QString::fromLatin1( "-w" ) + << QString::fromLatin1( "--timeout=10" ) // default is 60 seconds. + << QString::fromLatin1( "--pgdata=%1" ).arg( mPgData ) +- // set the directory for unix domain socket communication +- // -o will pass the switch to postgres +- << QString::fromLatin1( "-o \"-k%1\"" ).arg( socketDir ); ++ // These options are passed to postgres ++ // -k - directory for unix domain socket communication ++ // -h - disable listening for TCP/IP ++ << QString::fromLatin1( "-o \"-k%1\" -h ''" ).arg( socketDir ); + + QProcess pgCtl; + pgCtl.start( mServerPath, arguments ); +-- +1.9.3 + diff --git a/akonadi.spec b/akonadi.spec index cd1a53b..dc09b2c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 7%{?dist} +Release: 8%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -39,7 +39,13 @@ Source10: akonadiserverrc.mysql ## upstream patches # 1.12 branch -Patch101: 0001-remove-invalid-part-before-parttable-migration.patch +Patch101: 0001-Use-per-thread-QDBusConnections.patch +Patch102: 0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch +Patch103: 0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch +Patch104: 0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch +Patch105: 0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch +Patch106: 0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch +Patch107: 0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch # master branch Patch212: 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch @@ -101,6 +107,12 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %setup -q -n akonadi-%{version} %patch101 -p1 -b .0001 +%patch102 -p1 -b .0002 +%patch103 -p1 -b .0003 +%patch104 -p1 -b .0004 +%patch105 -p1 -b .0005 +%patch106 -p1 -b .0006 +%patch107 -p1 -b .0007 %patch212 -p1 -b .0012 %patch213 -p1 -b .0013 @@ -208,6 +220,9 @@ fi %changelog +* Mon Jun 09 2014 Rex Dieter 1.12.1-8 +- pull in latest 1.12 branch commits + * Sat Jun 07 2014 Fedora Release Engineering - 1.12.1-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild From 12eb55c0c845a8b25646656964c9c0f384aa2e8d Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 3 Jul 2014 12:19:35 -0500 Subject: [PATCH 15/78] optimized mimeinfo scriptlet --- akonadi.spec | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index dc09b2c..b350ea5 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 8%{?dist} +Release: 9%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -162,15 +162,18 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" xvfb-run -a dbus-launch --exit-with-session make test -C %{_target_platform} ||: -%post -p /sbin/ldconfig +%post +/sbin/ldconfig +touch --no-create %{_datadir}/mime/packages ||: %posttrans -update-mime-database %{_datadir}/mime &> /dev/null || : +update-mime-database -n %{_datadir}/mime &> /dev/null || : %postun /sbin/ldconfig ||: if [ $1 -eq 0 ] ; then - update-mime-database %{_datadir}/mime &> /dev/null ||: + touch --no-create %{_datadir}/mime/packages ||: + update-mime-database -n %{_datadir}/mime &> /dev/null ||: fi %files @@ -220,6 +223,9 @@ fi %changelog +* Thu Jul 03 2014 Rex Dieter 1.12.1-9 +- optimized mimeinfo scriptlet + * Mon Jun 09 2014 Rex Dieter 1.12.1-8 - pull in latest 1.12 branch commits From e9c8aa0fe7a01a4d36b03d2ad72e9ba26796f016 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 3 Jul 2014 13:21:39 -0500 Subject: [PATCH 16/78] safer version --- akonadi.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index b350ea5..521887a 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -167,13 +167,13 @@ xvfb-run -a dbus-launch --exit-with-session make test -C %{_target_platform} || touch --no-create %{_datadir}/mime/packages ||: %posttrans -update-mime-database -n %{_datadir}/mime &> /dev/null || : +update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null || : %postun /sbin/ldconfig ||: if [ $1 -eq 0 ] ; then touch --no-create %{_datadir}/mime/packages ||: - update-mime-database -n %{_datadir}/mime &> /dev/null ||: + update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null ||: fi %files From 65d3207aae438ebd2b2e8026802c39cab58f2ab1 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Tue, 8 Jul 2014 13:10:40 -0500 Subject: [PATCH 17/78] scriptlet polish --- akonadi.spec | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 521887a..9a1befe 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.12.1 -Release: 9%{?dist} +Release: 10%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -164,7 +164,7 @@ xvfb-run -a dbus-launch --exit-with-session make test -C %{_target_platform} || %post /sbin/ldconfig -touch --no-create %{_datadir}/mime/packages ||: +touch --no-create %{_datadir}/mime/packages &> /dev/null || : %posttrans update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null || : @@ -172,7 +172,7 @@ update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null || : %postun /sbin/ldconfig ||: if [ $1 -eq 0 ] ; then - touch --no-create %{_datadir}/mime/packages ||: + touch --no-create %{_datadir}/mime/packages &> /dev/null || : update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null ||: fi @@ -223,6 +223,9 @@ fi %changelog +* Tue Jul 08 2014 Rex Dieter 1.12.1-10 +- scriptlet polish + * Thu Jul 03 2014 Rex Dieter 1.12.1-9 - optimized mimeinfo scriptlet From 04b405e17543fe1888431a127eff73355fad733b Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Mon, 4 Aug 2014 18:39:05 -0500 Subject: [PATCH 18/78] 1.12.91 --- .gitignore | 2 +- 0001-Use-per-thread-QDBusConnections.patch | 485 ------------------ ...id-GID-part-from-PartTable-before-st.patch | 47 -- ...f-GID-from-SQL-query-result-in-Fetch.patch | 32 -- ...tartup-when-akonadiserverrc-contains.patch | 85 --- ...tart-when-postmaster.pid-is-not-remo.patch | 71 --- ...m-an-argument-passed-to-postgres-ser.patch | 34 -- ...-Don-t-listen-for-TCP-IP-connections.patch | 33 -- ...rrency-in-our-copy-of-QSQLITE-driver.patch | 190 ------- ...ransaction-mutex-for-QSQLITE3-and-en.patch | 167 ------ akonadi.spec | 31 +- sources | 2 +- 12 files changed, 8 insertions(+), 1171 deletions(-) delete mode 100644 0001-Use-per-thread-QDBusConnections.patch delete mode 100644 0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch delete mode 100644 0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch delete mode 100644 0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch delete mode 100644 0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch delete mode 100644 0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch delete mode 100644 0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch delete mode 100644 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch delete mode 100644 0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch diff --git a/.gitignore b/.gitignore index c30bf02..5cfbff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/akonadi-1.12.1.tar.bz2 +/akonadi-1.12.91.tar.bz2 diff --git a/0001-Use-per-thread-QDBusConnections.patch b/0001-Use-per-thread-QDBusConnections.patch deleted file mode 100644 index ccb0e62..0000000 --- a/0001-Use-per-thread-QDBusConnections.patch +++ /dev/null @@ -1,485 +0,0 @@ -From a5542751bc147c7f74131006339b55e6339b3e9f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Thu, 17 Apr 2014 15:11:57 +0200 -Subject: [PATCH 1/7] Use per-thread QDBusConnections - -This moves DBusConnectionPool from Nepomuk search code to Akonadi and makes -use of it in search infrastructure and couple other classes that interact -with DBus from non-main thread. - -QDBusConnection is not thread-safe in Qt 4, so we need to workaround it by -having a connection for each thread. Qt 5 should be OK, so we can remove this -in Frameworks. - -This should fix random crashes I've been seeing when SearchTaskManager::addTask() -was called from multiple threads simultaneously. ---- - server/CMakeLists.txt | 2 +- - server/src/dbusconnectionpool.cpp | 62 +++++++++++++++++++++++++++++ - server/src/dbusconnectionpool.h | 41 +++++++++++++++++++ - server/src/handler/fetchhelper.cpp | 9 ++--- - server/src/nepomuk/dbusconnectionpool.cpp | 59 --------------------------- - server/src/nepomuk/dbusconnectionpool.h | 38 ------------------ - server/src/nepomuk/queryserviceclient.cpp | 4 +- - server/src/search/agentsearchinstance.cpp | 5 ++- - server/src/search/searchmanager.cpp | 3 +- - server/src/search/searchtaskmanager.cpp | 11 ++--- - server/src/search/searchtaskmanager.h | 2 - - server/src/storage/itemretrievalmanager.cpp | 6 +-- - server/src/storagejanitor.cpp | 3 +- - 13 files changed, 124 insertions(+), 121 deletions(-) - create mode 100644 server/src/dbusconnectionpool.cpp - create mode 100644 server/src/dbusconnectionpool.h - delete mode 100644 server/src/nepomuk/dbusconnectionpool.cpp - delete mode 100644 server/src/nepomuk/dbusconnectionpool.h - -diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt -index 571bead..1709348 100644 ---- a/server/CMakeLists.txt -+++ b/server/CMakeLists.txt -@@ -106,6 +106,7 @@ set(libakonadiprivate_SRCS - src/collectionscheduler.cpp - src/clientcapabilities.cpp - src/clientcapabilityaggregator.cpp -+ src/dbusconnectionpool.cpp - src/handler.cpp - src/handlerhelper.cpp - src/intervalcheck.cpp -@@ -206,7 +207,6 @@ if (Soprano_FOUND) - src/search/nepomuksearchengine.cpp - src/nepomuk/dbusoperators.cpp - src/nepomuk/queryserviceclient.cpp -- src/nepomuk/dbusconnectionpool.cpp - src/nepomuk/result.cpp - ) - -diff --git a/server/src/dbusconnectionpool.cpp b/server/src/dbusconnectionpool.cpp -new file mode 100644 -index 0000000..9aede4f ---- /dev/null -+++ b/server/src/dbusconnectionpool.cpp -@@ -0,0 +1,62 @@ -+/* -+ * Copyright (C) 2010 Sebastian Trueg -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 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 -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public License -+ * along with this library; see the file COPYING.LIB. If not, write to -+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#include "dbusconnectionpool.h" -+#include -+#include -+#include -+ -+namespace { -+QAtomicInt s_connectionCounter; -+ -+class DBusConnectionPoolPrivate -+{ -+public: -+ DBusConnectionPoolPrivate() -+ : m_connection( QDBusConnection::connectToBus( -+ QDBusConnection::SessionBus, -+ QString::fromLatin1("AkonadiServer-%1").arg(newNumber()) ) ) -+ { -+ } -+ ~DBusConnectionPoolPrivate() { -+ QDBusConnection::disconnectFromBus( m_connection.name() ); -+ } -+ -+ QDBusConnection connection() const { return m_connection; } -+ -+private: -+ static int newNumber() { -+ return s_connectionCounter.fetchAndAddAcquire( 1 ); -+ } -+ QDBusConnection m_connection; -+}; -+} -+ -+QThreadStorage s_perThreadConnection; -+ -+QDBusConnection Akonadi::Server::DBusConnectionPool::threadConnection() -+{ -+ if ( !QCoreApplication::instance() || QCoreApplication::instance()->thread() == QThread::currentThread() ) { -+ return QDBusConnection::sessionBus(); // main thread, use the default session bus -+ } -+ if ( !s_perThreadConnection.hasLocalData() ) { -+ s_perThreadConnection.setLocalData( new DBusConnectionPoolPrivate ); -+ } -+ return s_perThreadConnection.localData()->connection(); -+} -diff --git a/server/src/dbusconnectionpool.h b/server/src/dbusconnectionpool.h -new file mode 100644 -index 0000000..4f8a93e ---- /dev/null -+++ b/server/src/dbusconnectionpool.h -@@ -0,0 +1,41 @@ -+/* -+ * Copyright (C) 2010 Sebastian Trueg -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 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 -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public License -+ * along with this library; see the file COPYING.LIB. If not, write to -+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef DBUSCONNECTIONPOOL_H -+#define DBUSCONNECTIONPOOL_H -+ -+#include -+ -+namespace Akonadi { -+namespace Server { -+namespace DBusConnectionPool { -+ -+/** -+ * Returns a new QDBusConnection for each thread, because QDBusConnection is -+ * not thread-safe in Qt 4. -+ * -+ * FIXME: Remove in KF5 -+ */ -+QDBusConnection threadConnection(); -+ -+} -+} -+} -+ -+#endif -diff --git a/server/src/handler/fetchhelper.cpp b/server/src/handler/fetchhelper.cpp -index 6284cc9..a6888a3 100644 ---- a/server/src/handler/fetchhelper.cpp -+++ b/server/src/handler/fetchhelper.cpp -@@ -38,6 +38,7 @@ - #include "utils.h" - #include "intervalcheck.h" - #include "agentmanagerinterface.h" -+#include "dbusconnectionpool.h" - - #include - #include -@@ -252,16 +253,12 @@ bool FetchHelper::isScopeLocal( const Scope &scope ) - - query.next(); - const QString resourceName = query.value( 0 ).toString(); -- // Workaround for QDBusConnectionPrivate not being thread-safe in Qt 4, fixed in Qt 5.2 -- // TODO: Remove in KF5 -- const QDBusConnection connection = QDBusConnection::connectToBus( QDBusConnection::SessionBus, -- QString::fromLatin1( mConnection->sessionId() ) ); -+ - org::freedesktop::Akonadi::AgentManager manager( AkDBus::serviceName( AkDBus::Control ), - QLatin1String( "/AgentManager" ), -- connection ); -+ DBusConnectionPool::threadConnection() ); - const QString typeIdentifier = manager.agentInstanceType( resourceName ); - const QVariantMap properties = manager.agentCustomProperties( typeIdentifier ); -- QDBusConnection::disconnectFromBus( QString::fromLatin1( mConnection->sessionId() ) ); - return properties.value( QLatin1String( "HasLocalStorage" ), false ).toBool(); - } - -diff --git a/server/src/nepomuk/dbusconnectionpool.cpp b/server/src/nepomuk/dbusconnectionpool.cpp -deleted file mode 100644 -index 598d16f..0000000 ---- a/server/src/nepomuk/dbusconnectionpool.cpp -+++ /dev/null -@@ -1,59 +0,0 @@ --/* -- * This file is part of the Nepomuk KDE project. -- * Copyright (C) 2010 Sebastian Trueg -- * Copyright (C) 2010 David Faure -- * -- * This library is free software; you can redistribute it and/or -- * modify it under the terms of the GNU Library General Public -- * License as published by the Free Software Foundation; either -- * version 2 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 -- * Library General Public License for more details. -- * -- * You should have received a copy of the GNU Library General Public License -- * along with this library; see the file COPYING.LIB. If not, write to -- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- * Boston, MA 02110-1301, USA. -- */ -- --#include "dbusconnectionpool.h" --#include -- --namespace { --QAtomicInt s_connectionCounter; -- --class DBusConnectionPoolPrivate --{ --public: -- DBusConnectionPoolPrivate() -- : m_connection( QDBusConnection::connectToBus( -- QDBusConnection::SessionBus, -- QString::fromLatin1( "NepomukQueryServiceConnection%1" ).arg( newNumber() ) ) ) -- { -- } -- ~DBusConnectionPoolPrivate() { -- QDBusConnection::disconnectFromBus( m_connection.name() ); -- } -- -- QDBusConnection connection() const { return m_connection; } -- --private: -- static int newNumber() { -- return s_connectionCounter.fetchAndAddAcquire( 1 ); -- } -- QDBusConnection m_connection; --}; --} -- --QThreadStorage s_perThreadConnection; -- --QDBusConnection DBusConnectionPool::threadConnection() --{ -- if ( !s_perThreadConnection.hasLocalData() ) { -- s_perThreadConnection.setLocalData( new DBusConnectionPoolPrivate ); -- } -- return s_perThreadConnection.localData()->connection(); --} -diff --git a/server/src/nepomuk/dbusconnectionpool.h b/server/src/nepomuk/dbusconnectionpool.h -deleted file mode 100644 -index c5ac746..0000000 ---- a/server/src/nepomuk/dbusconnectionpool.h -+++ /dev/null -@@ -1,38 +0,0 @@ --/* -- * This file is part of the Nepomuk KDE project. -- * Copyright (C) 2010 Sebastian Trueg -- * -- * This library is free software; you can redistribute it and/or -- * modify it under the terms of the GNU Library General Public -- * License as published by the Free Software Foundation; either -- * version 2 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 -- * Library General Public License for more details. -- * -- * You should have received a copy of the GNU Library General Public License -- * along with this library; see the file COPYING.LIB. If not, write to -- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- * Boston, MA 02110-1301, USA. -- */ -- --#ifndef _DBUS_CONNECTION_POOL_H_ --#define _DBUS_CONNECTION_POOL_H_ -- --#include -- --namespace DBusConnectionPool --{ --/** -- * The DBusConnectionPool works around the problem -- * of QDBusConnection not being thread-safe. As soon as that -- * has been fixed (either directly in libdbus or with a work- -- * around in Qt) this method can be dropped in favor of -- * QDBusConnection::sessionBus(). -- */ --QDBusConnection threadConnection(); --} -- --#endif -diff --git a/server/src/nepomuk/queryserviceclient.cpp b/server/src/nepomuk/queryserviceclient.cpp -index 089a80b..b27a751 100644 ---- a/server/src/nepomuk/queryserviceclient.cpp -+++ b/server/src/nepomuk/queryserviceclient.cpp -@@ -24,7 +24,7 @@ - #include "result.h" - #include "queryserviceinterface.h" - #include "queryinterface.h" --#include -+#include "dbusconnectionpool.h" - - #include - #include -@@ -40,7 +40,7 @@ public: - Private() - : queryServiceInterface( 0 ), - queryInterface( 0 ), -- dbusConnection( DBusConnectionPool::threadConnection() ), -+ dbusConnection( Akonadi::Server::DBusConnectionPool::threadConnection() ), - m_queryActive( false ), - loop( 0 ) { - } -diff --git a/server/src/search/agentsearchinstance.cpp b/server/src/search/agentsearchinstance.cpp -index 54ffab0..ca6ef87 100644 ---- a/server/src/search/agentsearchinstance.cpp -+++ b/server/src/search/agentsearchinstance.cpp -@@ -21,6 +21,7 @@ - #include "agentsearchinterface.h" - #include "searchtaskmanager.h" - #include "akdbus.h" -+#include "dbusconnectionpool.h" - - using namespace Akonadi::Server; - -@@ -43,7 +44,7 @@ bool AgentSearchInstance::init() - mInterface = new OrgFreedesktopAkonadiAgentSearchInterface( - AkDBus::agentServiceName( mId, AkDBus::Agent ), - QLatin1String( "/Search" ), -- QDBusConnection::sessionBus() ); -+ DBusConnectionPool::threadConnection() ); - - if ( !mInterface || !mInterface->isValid() ) { - delete mInterface; -@@ -52,7 +53,7 @@ bool AgentSearchInstance::init() - } - - mServiceWatcher = new QDBusServiceWatcher( AkDBus::agentServiceName( mId, AkDBus::Agent ), -- QDBusConnection::sessionBus(), -+ DBusConnectionPool::threadConnection(), - QDBusServiceWatcher::WatchForOwnerChange, - this ); - connect( mServiceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), -diff --git a/server/src/search/searchmanager.cpp b/server/src/search/searchmanager.cpp -index 139d058..1eafb27 100644 ---- a/server/src/search/searchmanager.cpp -+++ b/server/src/search/searchmanager.cpp -@@ -26,6 +26,7 @@ - #include "agentsearchengine.h" - #include "nepomuksearchengine.h" - #include "notificationmanager.h" -+#include "dbusconnectionpool.h" - #include "searchrequest.h" - #include "searchtaskmanager.h" - #include "storage/datastore.h" -@@ -98,7 +99,7 @@ SearchManager::SearchManager( const QStringList &searchEngines, QObject *parent - - new SearchManagerAdaptor( this ); - -- QDBusConnection::sessionBus().registerObject( -+ DBusConnectionPool::threadConnection().registerObject( - QLatin1String( "/SearchManager" ), - this, - QDBusConnection::ExportAdaptors ); -diff --git a/server/src/search/searchtaskmanager.cpp b/server/src/search/searchtaskmanager.cpp -index 51bb516..e7980a4 100644 ---- a/server/src/search/searchtaskmanager.cpp -+++ b/server/src/search/searchtaskmanager.cpp -@@ -23,6 +23,7 @@ - #include "akdbus.h" - #include "connection.h" - #include "storage/selectquerybuilder.h" -+#include "dbusconnectionpool.h" - #include - - #include -@@ -36,9 +37,6 @@ SearchTaskManager *SearchTaskManager::sInstance = 0; - SearchTaskManager::SearchTaskManager() - : QObject() - , mShouldStop( false ) -- , mAgentManager( AkDBus::serviceName( AkDBus::Control ), QLatin1String( "/AgentManager" ), -- QDBusConnection::sessionBus() ) -- - { - sInstance = this; - -@@ -125,13 +123,16 @@ void SearchTaskManager::addTask( SearchTask *task ) - } - - mInstancesLock.lock(); -+ -+ org::freedesktop::Akonadi::AgentManager agentManager( AkDBus::serviceName( AkDBus::Control ), QLatin1String( "/AgentManager" ), -+ DBusConnectionPool::threadConnection() ); - do { - const QString resourceId = query.value( 1 ).toString(); - if ( !mInstances.contains( resourceId ) ) { - akDebug() << "Resource" << resourceId << "does not implement Search interface, skipping"; -- } else if ( !mAgentManager.agentInstanceOnline( resourceId ) ) { -+ } else if ( !agentManager.agentInstanceOnline( resourceId ) ) { - akDebug() << "Agent" << resourceId << "is offline, skipping"; -- } else if ( mAgentManager.agentInstanceStatus( resourceId ) > 2 ) { // 2 == Broken, 3 == Not Configured -+ } else if ( agentManager.agentInstanceStatus( resourceId ) > 2 ) { // 2 == Broken, 3 == Not Configured - akDebug() << "Agent" << resourceId << "is broken or not configured"; - } else { - const qint64 collectionId = query.value( 0 ).toLongLong(); -diff --git a/server/src/search/searchtaskmanager.h b/server/src/search/searchtaskmanager.h -index 06e1b52..9b7972b 100644 ---- a/server/src/search/searchtaskmanager.h -+++ b/server/src/search/searchtaskmanager.h -@@ -97,8 +97,6 @@ class SearchTaskManager : public QObject - TasksMap::Iterator cancelRunningTask( TasksMap::Iterator &iter ); - bool allResourceTasksCompleted( SearchTask* ) const; - -- org::freedesktop::Akonadi::AgentManager mAgentManager; -- - QMap mInstances; - QMutex mInstancesLock; - -diff --git a/server/src/storage/itemretrievalmanager.cpp b/server/src/storage/itemretrievalmanager.cpp -index d49ba66..ce88410 100644 ---- a/server/src/storage/itemretrievalmanager.cpp -+++ b/server/src/storage/itemretrievalmanager.cpp -@@ -20,6 +20,7 @@ - #include "itemretrievalmanager.h" - #include "itemretrievalrequest.h" - #include "itemretrievaljob.h" -+#include "dbusconnectionpool.h" - - #include "resourceinterface.h" - -@@ -40,10 +41,7 @@ ItemRetrievalManager *ItemRetrievalManager::sInstance = 0; - - ItemRetrievalManager::ItemRetrievalManager( QObject *parent ) - : QObject( parent ), -- mDBusConnection( -- QDBusConnection::connectToBus( -- QDBusConnection::SessionBus, -- QString::fromLatin1( "AkonadiServerItemRetrievalManager" ) ) ) -+ mDBusConnection( DBusConnectionPool::threadConnection() ) - { - // make sure we are created from the retrieval thread and only once - Q_ASSERT( QThread::currentThread() != QCoreApplication::instance()->thread() ); -diff --git a/server/src/storagejanitor.cpp b/server/src/storagejanitor.cpp -index ebe7a24..af1a407 100644 ---- a/server/src/storagejanitor.cpp -+++ b/server/src/storagejanitor.cpp -@@ -26,6 +26,7 @@ - #include "storage/parthelper.h" - #include "resourcemanager.h" - #include "entities.h" -+#include "dbusconnectionpool.h" - - #include - #include -@@ -63,7 +64,7 @@ void StorageJanitorThread::run() - - StorageJanitor::StorageJanitor( QObject *parent ) - : QObject( parent ) -- , m_connection( QDBusConnection::connectToBus( QDBusConnection::SessionBus, QLatin1String( staticMetaObject.className() ) ) ) -+ , m_connection( DBusConnectionPool::threadConnection() ) - , m_lostFoundCollectionId( -1 ) - { - DataStore::self(); --- -1.9.3 - diff --git a/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch b/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch deleted file mode 100644 index dac2e31..0000000 --- a/0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 4ca8b846baaad48ebbd723f6411f9571a3b0f5ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Tue, 22 Apr 2014 11:28:07 +0200 -Subject: [PATCH 2/7] Remove the invalid GID part from PartTable before - starting PartTable migration - -More people than we expected have invalid 'GID' part in their PartTable, -which breaks migration to schema 25, because it expects all part types -to have a valid name. - -To work around this fact, we DELETE all parts with name 'GID' from PartTable -before starting the actual migration. This will not fix the migration for -people with other invalid parts, but I haven't heard of any such. To make -this completely bullet-proof, we would need to iterate through all entries, -which would be massively slower than current INSERT INTO ... SELECT FROM approach. - -Distributions, this is a good choice for backporting into 1.12.1 ;-) - -BUG: 331867 -FIXED-IN: 1.12.2 ---- - server/src/storage/dbupdater.cpp | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/server/src/storage/dbupdater.cpp b/server/src/storage/dbupdater.cpp -index ccaf584..d6368b5 100644 ---- a/server/src/storage/dbupdater.cpp -+++ b/server/src/storage/dbupdater.cpp -@@ -261,6 +261,15 @@ bool DbUpdater::complexUpdate_25() - } - } - -+ { -+ // It appears that more users than expected have the invalid "GID" part in their -+ // PartTable, which breaks the migration below (see BKO#331867), so we apply this -+ // wanna-be fix to remove the invalid part before we start the actual migration. -+ QueryBuilder qb( QLatin1String( "PartTable" ), QueryBuilder::Delete ); -+ qb.addValueCondition( QLatin1String( "PartTable.name" ), Query::Equals, QLatin1String( "GID" ) ); -+ qb.exec(); -+ } -+ - akDebug() << "Creating a PartTable_new"; - { - TableDescription description; --- -1.9.3 - diff --git a/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch b/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch deleted file mode 100644 index 4bb61f7..0000000 --- a/0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 1d47dd274dd29bec42170d0cdf69cb9ac0b6686e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Thu, 24 Apr 2014 15:37:32 +0200 -Subject: [PATCH 3/7] Fix retrieving of GID from SQL query result in - FetchHelper - -This has been broken since the day one, but nobody noticed. I guess -we were lucky enough to always query other arguments, so that -ItemQueryPimItemGidColumn actually matched indexed of the GID column -in query. - -Another reason why we need proper unit-tests on the server... ---- - server/src/handler/fetchhelper.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/server/src/handler/fetchhelper.cpp b/server/src/handler/fetchhelper.cpp -index a6888a3..4d9f778 100644 ---- a/server/src/handler/fetchhelper.cpp -+++ b/server/src/handler/fetchhelper.cpp -@@ -367,7 +367,7 @@ bool FetchHelper::fetchItems( const QByteArray &responseIdentifier ) - } - } - if ( mFetchScope.gidRequested() ) { -- const QByteArray gid = Utils::variantToByteArray( itemQuery.value( ItemQueryPimItemGidColumn ) ); -+ const QByteArray gid = Utils::variantToByteArray( extractQueryResult( itemQuery, ItemQueryPimItemGidColumn ) ); - if ( !gid.isEmpty() ) { - attributes.append( AKONADI_PARAM_GID " " + ImapParser::quote( gid ) ); - } --- -1.9.3 - diff --git a/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch b/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch deleted file mode 100644 index 4c2d3b9..0000000 --- a/0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch +++ /dev/null @@ -1,85 +0,0 @@ -From f5bfc641c38ca0dbd37c8993bc74f81c7e0c9b66 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Tue, 6 May 2014 15:27:07 +0200 -Subject: [PATCH 4/7] Fix PostgreSQL startup when akonadiserverrc contains - empty values - -When there are empty configuration options in akonadiserverrc (like Host), -we would still pick them up and use them as valid values, which is wrong, -because then we work with empty socket path etc. - -Instead we always replace non-existent and empty config values with the -default ones, so that Akonadi with PostgreSQL is always able to start. ---- - server/src/storage/dbconfigpostgresql.cpp | 27 ++++++++++++++++++++------- - 1 file changed, 20 insertions(+), 7 deletions(-) - -diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp -index 10460d4..66e4605 100644 ---- a/server/src/storage/dbconfigpostgresql.cpp -+++ b/server/src/storage/dbconfigpostgresql.cpp -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - - #include - -@@ -90,13 +91,29 @@ bool DbConfigPostgresql::init( QSettings &settings ) - // read settings for current driver - settings.beginGroup( driverName() ); - mDatabaseName = settings.value( QLatin1String( "Name" ), defaultDatabaseName() ).toString(); -+ if ( mDatabaseName.isEmpty() ) { -+ mDatabaseName = defaultDatabaseName(); -+ } - mHostName = settings.value( QLatin1String( "Host" ), defaultHostName ).toString(); -+ if ( mHostName.isEmpty() ) { -+ mHostName = defaultHostName; -+ } -+ // User, password and Options can be empty and still valid, so don't override them - mUserName = settings.value( QLatin1String( "User" ) ).toString(); - mPassword = settings.value( QLatin1String( "Password" ) ).toString(); - mConnectionOptions = settings.value( QLatin1String( "Options" ), defaultOptions ).toString(); - mServerPath = settings.value( QLatin1String( "ServerPath" ), defaultServerPath ).toString(); -+ if ( mInternalServer && mServerPath.isEmpty() ) { -+ mServerPath = defaultServerPath; -+ } - mInitDbPath = settings.value( QLatin1String( "InitDbPath" ), defaultInitDbPath ).toString(); -+ if ( mInternalServer && mInitDbPath.isEmpty() ) { -+ mInitDbPath = defaultInitDbPath; -+ } - mPgData = settings.value( QLatin1String( "PgData" ), defaultPgData ).toString(); -+ if ( mPgData.isEmpty() ) { -+ mPgData = defaultPgData; -+ } - settings.endGroup(); - - // store back the default values -@@ -104,12 +121,8 @@ bool DbConfigPostgresql::init( QSettings &settings ) - settings.setValue( QLatin1String( "Name" ), mDatabaseName ); - settings.setValue( QLatin1String( "Host" ), mHostName ); - settings.setValue( QLatin1String( "Options" ), mConnectionOptions ); -- if ( !mServerPath.isEmpty() ) { -- settings.setValue( QLatin1String( "ServerPath" ), mServerPath ); -- } -- if ( !mInitDbPath.isEmpty() ) { -- settings.setValue( QLatin1String( "InitDbPath" ), mInitDbPath ); -- } -+ settings.setValue( QLatin1String( "ServerPath" ), mServerPath ); -+ settings.setValue( QLatin1String( "InitDbPath" ), mInitDbPath ); - settings.setValue( QLatin1String( "StartServer" ), mInternalServer ); - settings.endGroup(); - settings.sync(); -@@ -203,7 +216,7 @@ void DbConfigPostgresql::startInternalServer() - break; - } - -- if ( pgCtl.waitForFinished( 500 ) ) { -+ if ( pgCtl.waitForFinished( 500 ) && pgCtl.exitCode() ) { - akError() << "Database process exited unexpectedly during initial connection!"; - akError() << "executable:" << mServerPath; - akError() << "arguments:" << arguments; --- -1.9.3 - diff --git a/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch b/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch deleted file mode 100644 index 80dded8..0000000 --- a/0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch +++ /dev/null @@ -1,71 +0,0 @@ -From f217d9e96dc8de88916e8ca0b93dd9a7500bbbc3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Tue, 6 May 2014 16:38:17 +0200 -Subject: [PATCH 5/7] Fix PostgreSQL start when postmaster.pid is not removed - after non-clean shutdown - -When PostgreSQL is not terminated nicely, a pidfile is left behind in db_data, -which will prevent pg_ctl from starting a new PostgreSQL server. - -We check for postmaster.pid file and verify that postgres server with PID -specified in the pidfile is not running anymore, then delete the pidfile to -allow pg_ctl to start a new server. If the postgres server is still running -(possibly after Akonadi server crash), we try to connect to it right away. - -BUG: 286826 -FIXED-IN: 1.12.2 ---- - server/src/storage/dbconfigpostgresql.cpp | 37 +++++++++++++++++++++++++++++++ - 1 file changed, 37 insertions(+) - -diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp -index 66e4605..7b61819 100644 ---- a/server/src/storage/dbconfigpostgresql.cpp -+++ b/server/src/storage/dbconfigpostgresql.cpp -@@ -166,6 +166,43 @@ void DbConfigPostgresql::startInternalServer() - QDir().mkpath( socketDir ); - } - -+// TODO Windows support -+#ifndef Q_WS_WIN -+ // If postmaster.pid exists, check whether the postgres process still exists too, -+ // because normally we shouldn't be able to get this far if Akonadi is already -+ // running. If postgres is not running, then the pidfile was left after a system -+ // crash or something similar and we can remove it (otherwise pg_ctl won't start) -+ QFile postmaster( QString::fromLatin1( "%1/postmaster.pid" ).arg( mPgData ) ); -+ if ( postmaster.exists() && postmaster.open( QIODevice::ReadOnly ) ) { -+ qDebug() << "Found a postmaster.pid pidfile, checking whether the server is still running..."; -+ QByteArray pid = postmaster.readLine(); -+ // Remvoe newline character -+ pid.truncate(pid.size() - 1); -+ QFile proc( QString::fromLatin1( "/proc/" + pid + "/stat" ) ); -+ // Check whether the process with the PID from pidfile still exists and whether -+ // it's actually still postgres or, whether the PID has been recycled in the -+ // meanwhile. -+ if ( proc.open( QIODevice::ReadOnly ) ) { -+ const QByteArray stat = proc.readAll(); -+ const QList stats = stat.split( ' ' ); -+ if ( stats.count() > 1 ) { -+ // Make sure the PID actually belongs to postgres process -+ if ( stats[1] == "(postgres)" ) { -+ // Yup, our PostgreSQL is actually running, so pretend we started the server -+ // and try to connect to it -+ qWarning() << "PostgreSQL for Akonadi is already running, trying to connect to it."; -+ return; -+ } -+ } -+ proc.close(); -+ } -+ -+ qDebug() << "No postgres process with specified PID is running. Removing the pidfile and starting a new Postgres instance..."; -+ postmaster.close(); -+ postmaster.remove(); -+ } -+#endif -+ - if ( !QFile::exists( QString::fromLatin1( "%1/PG_VERSION" ).arg( mPgData ) ) ) { - // postgres data directory not initialized yet, so call initdb on it - --- -1.9.3 - diff --git a/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch b/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch deleted file mode 100644 index 35dcdd7..0000000 --- a/0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 74b2d3a43e19f5262ec84ccf609925eeb071d755 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Tue, 6 May 2014 16:49:32 +0200 -Subject: [PATCH 6/7] Remove space from an argument passed to postgres server - -Some users reported problem with starting PostgreSQL when there's a space -between name and value of an argument passed to postgres. Removing the -space fixes problem for them (withouth breaking PostgreSQL for those who -didn't have a problem with the space) - -Thanks SergTruf for the patch. - -BUG: 332988 -FIXED-IN: 1.12.2 ---- - server/src/storage/dbconfigpostgresql.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp -index 7b61819..ce8067b 100644 ---- a/server/src/storage/dbconfigpostgresql.cpp -+++ b/server/src/storage/dbconfigpostgresql.cpp -@@ -223,7 +223,7 @@ void DbConfigPostgresql::startInternalServer() - << QString::fromLatin1( "--pgdata=%1" ).arg( mPgData ) - // set the directory for unix domain socket communication - // -o will pass the switch to postgres -- << QString::fromLatin1( "-o \"-k %1\"" ).arg( socketDir ); -+ << QString::fromLatin1( "-o \"-k%1\"" ).arg( socketDir ); - - QProcess pgCtl; - pgCtl.start( mServerPath, arguments ); --- -1.9.3 - diff --git a/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch b/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch deleted file mode 100644 index a401e82..0000000 --- a/0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 583ec681003395d5f3c3443c7ec82404b187afb9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Wed, 7 May 2014 14:22:13 +0200 -Subject: [PATCH 7/7] PostgreSQL: Don't listen for TCP/IP connections - -Fixes Akonadi with internal PostgreSQL not starting when system-wide PostgreSQL -server is already running. We don't need TCP/IP anyway, because we talk to our -PostgreSQL via a Unix-socket. ---- - server/src/storage/dbconfigpostgresql.cpp | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/server/src/storage/dbconfigpostgresql.cpp b/server/src/storage/dbconfigpostgresql.cpp -index ce8067b..83a5f91 100644 ---- a/server/src/storage/dbconfigpostgresql.cpp -+++ b/server/src/storage/dbconfigpostgresql.cpp -@@ -221,9 +221,10 @@ void DbConfigPostgresql::startInternalServer() - << QString::fromLatin1( "-w" ) - << QString::fromLatin1( "--timeout=10" ) // default is 60 seconds. - << QString::fromLatin1( "--pgdata=%1" ).arg( mPgData ) -- // set the directory for unix domain socket communication -- // -o will pass the switch to postgres -- << QString::fromLatin1( "-o \"-k%1\"" ).arg( socketDir ); -+ // These options are passed to postgres -+ // -k - directory for unix domain socket communication -+ // -h - disable listening for TCP/IP -+ << QString::fromLatin1( "-o \"-k%1\" -h ''" ).arg( socketDir ); - - QProcess pgCtl; - pgCtl.start( mServerPath, arguments ); --- -1.9.3 - diff --git a/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch b/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch deleted file mode 100644 index 5717639..0000000 --- a/0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch +++ /dev/null @@ -1,190 +0,0 @@ -diff --git a/qsqlite/src/qsql_sqlite.cpp b/qsqlite/src/qsql_sqlite.cpp -index c1e9508..5da232f 100644 ---- a/qsqlite/src/qsql_sqlite.cpp -+++ b/qsqlite/src/qsql_sqlite.cpp -@@ -528,7 +528,7 @@ static int qGetSqliteOpenMode(QString opts) - return SQLITE_OPEN_READONLY; - } - // The SQLITE_OPEN_NOMUTEX flag causes the database connection to be in the multi-thread mode -- return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; -+ return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_SHAREDCACHE; - } - - /* -@@ -543,8 +543,10 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c - if (db.isEmpty()) - return false; - -+ sqlite3_enable_shared_cache(1); - if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, qGetSqliteOpenMode(conOpts), NULL) == SQLITE_OK) { - sqlite3_busy_timeout(d->access, qGetSqliteTimeout(conOpts)); -+ sqlite3_extended_result_codes(d->access, 1); - setOpen(true); - setOpenError(false); - return true; -diff --git a/qsqlite/src/sqlite_blocking.cpp b/qsqlite/src/sqlite_blocking.cpp -index c0fe3f2..180685c 100644 ---- a/qsqlite/src/sqlite_blocking.cpp -+++ b/qsqlite/src/sqlite_blocking.cpp -@@ -1,63 +1,94 @@ -+/* -+ Copyright (c) 2009 Bertjan Broeksema -+ Copyright (c) 2014 Daniel Vrátil -+ -+ This library is free software; you can redistribute it and/or modify it -+ under the terms of the GNU Library General Public License as published by -+ the Free Software Foundation; either version 2 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 Library General Public -+ License for more details. -+ -+ You should have received a copy of the GNU Library General Public License -+ along with this library; see the file COPYING.LIB. If not, write to the -+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -+ 02110-1301, USA. -+*/ -+ - #include "sqlite_blocking.h" - - #include --#ifndef _WIN32 --#include --#else --#include --#define usleep(x) Sleep(x/1000) --#endif -- --#include "qdebug.h" -+ -+#include -+#include - #include "qstringbuilder.h" - #include "qthread.h" -+#include -+ -+/* Based on example in http://www.sqlite.org/unlock_notify.html */ - --QString debugString() -+struct UnlockNotification { -+ bool fired; -+ QWaitCondition cond; -+ QMutex mutex; -+}; -+ -+static void qSqlite3UnlockNotifyCb(void **apArg, int nArg) - { -- return QString( QLatin1Literal("[QSQLITE3: ") + QString::number( quint64( QThread::currentThreadId() ) ) + QLatin1Literal("] ") ); -+ for (int i = 0; i < nArg; ++i) { -+ UnlockNotification *ntf = static_cast(apArg[i]); -+ ntf->mutex.lock(); -+ ntf->fired = true; -+ ntf->cond.wakeOne(); -+ ntf->mutex.unlock(); -+ } - } - --int sqlite3_blocking_step( sqlite3_stmt *pStmt ) -+static int qSqlite3WaitForUnlockNotify(sqlite3 *db) - { -- // NOTE: The example at http://www.sqlite.org/unlock_notify.html says to wait -- // for SQLITE_LOCK but for some reason I don't understand I get -- // SQLITE_BUSY. -- int rc = sqlite3_step( pStmt ); -- -- QThread::currentThreadId(); -- if ( rc == SQLITE_BUSY ) -- qDebug() << debugString() << "sqlite3_blocking_step: Entering while loop"; -- -- while( rc == SQLITE_BUSY ) { -- usleep(5000); -- sqlite3_reset( pStmt ); -- rc = sqlite3_step( pStmt ); -- -- if ( rc != SQLITE_BUSY ) { -- qDebug() << debugString() << "sqlite3_blocking_step: Leaving while loop"; -+ int rc; -+ UnlockNotification un; -+ un.fired = false; -+ -+ rc = sqlite3_unlock_notify(db, qSqlite3UnlockNotifyCb, (void *)&un); -+ Q_ASSERT(rc == SQLITE_LOCKED || rc == SQLITE_OK); -+ -+ if (rc == SQLITE_OK) { -+ un.mutex.lock(); -+ if (!un.fired) { -+ un.cond.wait(&un.mutex); - } -+ un.mutex.unlock(); - } - - return rc; - } - --int sqlite3_blocking_prepare16_v2( sqlite3 *db, /* Database handle. */ -- const void *zSql, /* SQL statement, UTF-16 encoded */ -- int nSql, /* Length of zSql in bytes. */ -- sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ -- const void **pzTail /* OUT: Pointer to unused portion of zSql */ ) -+int sqlite3_blocking_step(sqlite3_stmt *pStmt) - { -- int rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); -- -- if ( rc == SQLITE_BUSY ) -- qDebug() << debugString() << "sqlite3_blocking_prepare16_v2: Entering while loop"; -+ int rc; -+ while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_step(pStmt))) { -+ rc = qSqlite3WaitForUnlockNotify(sqlite3_db_handle(pStmt)); -+ if (rc != SQLITE_OK) { -+ break; -+ } -+ sqlite3_reset(pStmt); -+ } - -- while( rc == SQLITE_BUSY ) { -- usleep(500000); -- rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); -+ return rc; -+} - -- if ( rc != SQLITE_BUSY ) { -- qDebug() << debugString() << "sqlite3_prepare16_v2: Leaving while loop"; -+int sqlite3_blocking_prepare16_v2(sqlite3 *db, const void *zSql, int nSql, -+ sqlite3_stmt **ppStmt, const void **pzTail) -+{ -+ int rc; -+ while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_prepare16_v2(db, zSql, nSql, ppStmt, pzTail))) { -+ rc = qSqlite3WaitForUnlockNotify(db); -+ if (rc != SQLITE_OK) { -+ break; - } - } - -diff --git a/qsqlite/src/sqlite_blocking.h b/qsqlite/src/sqlite_blocking.h -index 0d6f6a0..9f13946 100644 ---- a/qsqlite/src/sqlite_blocking.h -+++ b/qsqlite/src/sqlite_blocking.h -@@ -1,3 +1,22 @@ -+/* -+ Copyright (c) 2009 Bertjan Broeksema -+ -+ This library is free software; you can redistribute it and/or modify it -+ under the terms of the GNU Library General Public License as published by -+ the Free Software Foundation; either version 2 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 Library General Public -+ License for more details. -+ -+ You should have received a copy of the GNU Library General Public License -+ along with this library; see the file COPYING.LIB. If not, write to the -+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -+ 02110-1301, USA. -+*/ -+ - #ifndef SQLITE_BLOCKING_H - #define SQLITE_BLOCKING_H diff --git a/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch b/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch deleted file mode 100644 index 34de4e1..0000000 --- a/0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 24413dc44b0637d6c64e6b2105c2bcf1b99849a5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Vr=C3=A1til?= -Date: Sun, 6 Apr 2014 19:50:38 +0200 -Subject: [PATCH 13/16] Disable global transaction mutex for QSQLITE3 and - enable transaction recording - -Our QSQLITE3 driver now supports concurrency, so we don't need to serialize -transactions in DataStore anymore. It is however still needed for the -QSQLITE driver shipped with Qt. - -Secondary, concurrency support also means possible transactions deadlocks and -timeouts, so we also need to enable transaction recording and replaying for -the QSQLITE3 backend. ---- - server/src/storage/datastore.cpp | 18 +++++++++++------- - server/src/storage/datastore.h | 2 +- - server/src/storage/dbtype.cpp | 5 +++++ - server/src/storage/dbtype.h | 3 +++ - server/src/storage/querybuilder.cpp | 20 ++++++++++++++++---- - server/src/storage/querybuilder.h | 2 +- - 6 files changed, 37 insertions(+), 13 deletions(-) - -diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp -index 57d1e4e..0f04fa5 100644 ---- a/server/src/storage/datastore.cpp -+++ b/server/src/storage/datastore.cpp -@@ -61,8 +61,8 @@ using namespace Akonadi::Server; - static QMutex sTransactionMutex; - bool DataStore::s_hasForeignKeyConstraints = false; - --#define TRANSACTION_MUTEX_LOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.lock() --#define TRANSACTION_MUTEX_UNLOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.unlock() -+#define TRANSACTION_MUTEX_LOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.lock() -+#define TRANSACTION_MUTEX_UNLOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.unlock() - - /*************************************************************************** - * DataStore * -@@ -1083,23 +1083,27 @@ QDateTime DataStore::dateTimeToQDateTime( const QByteArray &dateTime ) - - void DataStore::addQueryToTransaction( const QSqlQuery &query, bool isBatch ) - { -- DbType::Type dbType = DbType::type( m_database ); - // This is used for replaying deadlocked transactions, so only record queries - // for backends that support concurrent transactions. -- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) { -+ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) { - return; - } - - m_transactionQueries.append( qMakePair( query, isBatch ) ); - } - --QSqlQuery DataStore::retryLastTransaction() -+QSqlQuery DataStore::retryLastTransaction( bool rollbackFirst ) - { -- DbType::Type dbType = DbType::type( m_database ); -- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) { -+ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) { - return QSqlQuery(); - } - -+ if ( rollbackFirst ) { -+ // In some cases the SQL database won't rollback the failed transaction, so -+ // we need to do it manually -+ m_database.driver()->rollbackTransaction(); -+ } -+ - // The database has rolled back the actual transaction, so reset the counter - // to 0 and start a new one in beginTransaction(). Then restore the level - // because this has to be completely transparent to the original caller -diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h -index 8b4a2b7..8a0fe01 100644 ---- a/server/src/storage/datastore.h -+++ b/server/src/storage/datastore.h -@@ -317,7 +317,7 @@ protected: - * @return Returns an invalid query when error occurs, or the last replayed - * query on success. - */ -- QSqlQuery retryLastTransaction(); -+ QSqlQuery retryLastTransaction( bool rollbackFirst ); - - private Q_SLOTS: - void sendKeepAliveQuery(); -diff --git a/server/src/storage/dbtype.cpp b/server/src/storage/dbtype.cpp -index 495f532..7df2fb1 100644 ---- a/server/src/storage/dbtype.cpp -+++ b/server/src/storage/dbtype.cpp -@@ -39,3 +39,8 @@ DbType::Type DbType::typeForDriverName( const QString &driverName ) - } - return Unknown; - } -+ -+bool DbType::isSystemSQLite( const QSqlDatabase &db ) -+{ -+ return db.driverName() == QLatin1String( "QSQLITE" ); -+} -diff --git a/server/src/storage/dbtype.h b/server/src/storage/dbtype.h -index a95a833..3595604 100644 ---- a/server/src/storage/dbtype.h -+++ b/server/src/storage/dbtype.h -@@ -42,6 +42,9 @@ namespace DbType - /** Returns the type for the given driver name. */ - Type typeForDriverName( const QString &driverName ); - -+ /** Returns true when using QSQLITE driver shipped with Qt, FALSE otherwise */ -+ bool isSystemSQLite( const QSqlDatabase &db ); -+ - } // namespace DbType - } // namespace Server - } // namespace Akonadi -diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp -index 0abad4a..0530b11 100644 ---- a/server/src/storage/querybuilder.cpp -+++ b/server/src/storage/querybuilder.cpp -@@ -320,10 +320,10 @@ QString QueryBuilder::buildQuery() - return statement; - } - --bool QueryBuilder::retryLastTransaction() -+bool QueryBuilder::retryLastTransaction( bool rollback ) - { - #ifndef QUERYBUILDER_UNITTEST -- mQuery = DataStore::self()->retryLastTransaction(); -+ mQuery = DataStore::self()->retryLastTransaction( rollback ); - return !mQuery.lastError().isValid(); - #else - return true; -@@ -400,9 +400,21 @@ bool QueryBuilder::exec() - akDebug() << mQuery.lastError().text(); - return retryLastTransaction(); - } -+ } else if ( mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite( DataStore::self()->database() ) ) { -+ const int error = mQuery.lastError().number(); -+ if ( error == 6 /* SQLITE_LOCKED */ ) { -+ akDebug() << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; -+ akDebug() << mQuery.lastError().text(); -+ return retryLastTransaction(); -+ } else if ( error == 5 /* SQLITE_BUSY */ ) { -+ akDebug() << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; -+ akDebug() << mQuery.lastError().text(); -+ return retryLastTransaction( true ); -+ } - } else if ( mDatabaseType == DbType::Sqlite ) { -- // We can't have a transaction deadlock in SQLite, because it does not support -- // concurrent transactions and DataStore serializes them through a global lock. -+ // We can't have a transaction deadlock in SQLite when using driver shipped -+ // with Qt, because it does not support concurrent transactions and DataStore -+ // serializes them through a global lock. - } - - akError() << "DATABASE ERROR:"; -diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h -index 235a099..b380f93 100644 ---- a/server/src/storage/querybuilder.h -+++ b/server/src/storage/querybuilder.h -@@ -244,7 +244,7 @@ class QueryBuilder - */ - void sqliteAdaptUpdateJoin( Query::Condition &cond ); - -- bool retryLastTransaction(); -+ bool retryLastTransaction( bool rollback = false); - - private: - QString mTable; --- -1.9.0 - diff --git a/akonadi.spec b/akonadi.spec index 9a1befe..6fa614c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,8 +18,8 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.12.1 -Release: 10%{?dist} +Version: 1.12.91 +Release: 1%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -38,20 +38,8 @@ Source10: akonadiserverrc.mysql ## upstreamable patches ## upstream patches -# 1.12 branch -Patch101: 0001-Use-per-thread-QDBusConnections.patch -Patch102: 0002-Remove-the-invalid-GID-part-from-PartTable-before-st.patch -Patch103: 0003-Fix-retrieving-of-GID-from-SQL-query-result-in-Fetch.patch -Patch104: 0004-Fix-PostgreSQL-startup-when-akonadiserverrc-contains.patch -Patch105: 0005-Fix-PostgreSQL-start-when-postmaster.pid-is-not-remo.patch -Patch106: 0006-Remove-space-from-an-argument-passed-to-postgres-ser.patch -Patch107: 0007-PostgreSQL-Don-t-listen-for-TCP-IP-connections.patch -# master branch -Patch212: 0012-Enable-concurrency-in-our-copy-of-QSQLITE-driver.patch -Patch213: 0013-Disable-global-transaction-mutex-for-QSQLITE3-and-en.patch - -%define mysql_conf_timestamp 20140415 +%define mysql_conf_timestamp 20140709 BuildRequires: automoc4 BuildRequires: boost-devel @@ -106,16 +94,6 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %prep %setup -q -n akonadi-%{version} -%patch101 -p1 -b .0001 -%patch102 -p1 -b .0002 -%patch103 -p1 -b .0003 -%patch104 -p1 -b .0004 -%patch105 -p1 -b .0005 -%patch106 -p1 -b .0006 -%patch107 -p1 -b .0007 -%patch212 -p1 -b .0012 -%patch213 -p1 -b .0013 - %build mkdir -p %{_target_platform} @@ -223,6 +201,9 @@ fi %changelog +* Mon Aug 04 2014 Rex Dieter 1.12.91-1 +- 1.12.91 + * Tue Jul 08 2014 Rex Dieter 1.12.1-10 - scriptlet polish diff --git a/sources b/sources index 7eb8642..2f7d2b1 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -9a4a99d10e003a267a515fc60de4f817 akonadi-1.12.1.tar.bz2 +6ba7a7329ac48495d44b410b00bc27b5 akonadi-1.12.91.tar.bz2 From baf6f13cedcbe8e80cdd90ae9b0f0ffc9f3a86b8 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 14 Aug 2014 10:47:09 -0500 Subject: [PATCH 19/78] 1.13.0 --- .gitignore | 2 +- akonadi.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5cfbff8..d95e0b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/akonadi-1.12.91.tar.bz2 +/akonadi-1.13.0.tar.bz2 diff --git a/akonadi.spec b/akonadi.spec index 6fa614c..cec0b77 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.12.91 +Version: 1.13.0 Release: 1%{?dist} License: LGPLv2+ @@ -201,6 +201,9 @@ fi %changelog +* Thu Aug 14 2014 Rex Dieter 1.13.0-1 +- 1.13.0 + * Mon Aug 04 2014 Rex Dieter 1.12.91-1 - 1.12.91 diff --git a/sources b/sources index 2f7d2b1..4957149 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -6ba7a7329ac48495d44b410b00bc27b5 akonadi-1.12.91.tar.bz2 +84eb2e471bd6bdfe54a2a2f1d858c07d akonadi-1.13.0.tar.bz2 From 5c1d74f712e1149e0d99714ac0e3d709767ea570 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 15 Aug 2014 20:14:12 +0000 Subject: [PATCH 20/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index cec0b77..4eac77a 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -201,6 +201,9 @@ fi %changelog +* Fri Aug 15 2014 Fedora Release Engineering - 1.13.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + * Thu Aug 14 2014 Rex Dieter 1.13.0-1 - 1.13.0 From 338a460da5c1e9cbe6528a0251b1cb3651acb00f Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 4 Sep 2014 12:58:37 -0500 Subject: [PATCH 21/78] no soprano for f20 now too (where kde-4.13 landed recently) --- akonadi.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 4eac77a..bc8d3ff 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -12,7 +12,7 @@ %endif # legacy nepomuk/soprano support (ie, kde < 4.13) -%if 0%{?fedora} < 21 +%if 0%{?fedora} < 20 %define soprano 1 %endif From a87e2990ffc68380b208c07ab3ccda2c00f763c9 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Mon, 15 Sep 2014 21:23:49 -0500 Subject: [PATCH 22/78] pull in some upstream fixes --- ...MAKE_FLAGS-the-right-way-in-try_comp.patch | 38 ++++ ...-test-directories-if-AKONADI_BUILD_T.patch | 82 +++++++ ...ifying-items-tags-via-Tag-RID-or-GID.patch | 201 ++++++++++++++++++ 0004-Fix-typo-in-if-condition.patch | 27 +++ ...r-overflow-in-AKTEST_FAKESERVER_MAIN.patch | 25 +++ akonadi.spec | 12 +- 6 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch create mode 100644 0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch create mode 100644 0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch create mode 100644 0004-Fix-typo-in-if-condition.patch create mode 100644 0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch diff --git a/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch b/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch new file mode 100644 index 0000000..0bad3aa --- /dev/null +++ b/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch @@ -0,0 +1,38 @@ +From b60702e0b7041c56a3cb52c209204d28406f3ce5 Mon Sep 17 00:00:00 2001 +From: Raphael Kubo da Costa +Date: Wed, 13 Aug 2014 14:43:04 +0300 +Subject: [PATCH 1/5] 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() +-- +1.9.3 + diff --git a/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch b/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch new file mode 100644 index 0000000..8efd56e --- /dev/null +++ b/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch @@ -0,0 +1,82 @@ +From 2146519108ec66300328b7b3979477c7789795d3 Mon Sep 17 00:00:00 2001 +From: Raphael Kubo da Costa +Date: Wed, 13 Aug 2014 23:22:11 +0300 +Subject: [PATCH 2/5] 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) +-- +1.9.3 + diff --git a/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch b/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch new file mode 100644 index 0000000..8538502 --- /dev/null +++ b/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch @@ -0,0 +1,201 @@ +From 9734074267bacd39aeb29c7a0d7df7cadb212d89 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Fri, 11 Jul 2014 18:33:39 +0200 +Subject: [PATCH 3/5] 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 & + 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 &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 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 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 &flags ); + bool addFlags( const PimItem::List &items, const QVector &flags, bool &flagsChanged ); + bool deleteFlags( const PimItem::List &items, const QVector &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 &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 &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"); + } +-- +1.9.3 + diff --git a/0004-Fix-typo-in-if-condition.patch b/0004-Fix-typo-in-if-condition.patch new file mode 100644 index 0000000..b3febdc --- /dev/null +++ b/0004-Fix-typo-in-if-condition.patch @@ -0,0 +1,27 @@ +From e52f9be20e566e507e77421f1243f51aa2fe8e55 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Mon, 25 Aug 2014 14:35:14 +0200 +Subject: [PATCH 4/5] 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" ); + } + +-- +1.9.3 + diff --git a/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch new file mode 100644 index 0000000..8cfaaae --- /dev/null +++ b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch @@ -0,0 +1,25 @@ +From 01c86229f9e26d9e036f6f2ab405659ed836b5c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Mon, 8 Sep 2014 15:36:18 +0200 +Subject: [PATCH 5/5] 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]; \ + } \ +-- +1.9.3 + diff --git a/akonadi.spec b/akonadi.spec index bc8d3ff..f3dc531 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 2%{?dist} +Release: 3%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -38,6 +38,11 @@ Source10: akonadiserverrc.mysql ## upstreamable patches ## upstream patches +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 %define mysql_conf_timestamp 20140709 @@ -92,7 +97,7 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %prep -%setup -q -n akonadi-%{version} +%autosetup -p1 -n akonadi-%{version} %build @@ -201,6 +206,9 @@ fi %changelog +* Mon Sep 15 2014 Rex Dieter 1.13.0-3 +- pull in some upstream fixes + * Fri Aug 15 2014 Fedora Release Engineering - 1.13.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild From b80c244f3022f8f503da30e290c43a159b88d865 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sat, 27 Sep 2014 16:46:27 -0500 Subject: [PATCH 23/78] explicitly s/mysql-server/mariadb-server/ --- akonadi.spec | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index f3dc531..59f20c4 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 3%{?dist} +Release: 4%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -61,7 +61,7 @@ BuildRequires: pkgconfig(sqlite3) >= 3.6.23 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: mariadb-server BuildRequires: postgresql-server %{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} @@ -82,7 +82,7 @@ Summary: Akonadi MySQL backend support # upgrade path Obsoletes: akonadi < 1.7.90-2 Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: mysql-server +Requires: mariadb-server Requires: qt4-mysql%{?_isa} Requires(post): %{_sbindir}/update-alternatives Requires(postun): %{_sbindir}/update-alternatives @@ -206,6 +206,9 @@ fi %changelog +* Sat Sep 27 2014 Rex Dieter 1.13.0-4 +- explicitly s/mysql-server/mariadb-server/ + * Mon Sep 15 2014 Rex Dieter 1.13.0-3 - pull in some upstream fixes From 002028f889864c399df6bbc07cf0c795e6e938e1 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sat, 27 Sep 2014 16:50:06 -0500 Subject: [PATCH 24/78] explicitly Requires: mariadb-server/mysql-server as appropriate --- akonadi.spec | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 59f20c4..7e0dffb 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -16,6 +16,11 @@ %define soprano 1 %endif +%global mysql mysql +%if 0%{?fedora} || 0%{?rhel} > 6 +%global mysql mariadb +%endif + Summary: PIM Storage Service Name: akonadi Version: 1.13.0 @@ -61,7 +66,7 @@ BuildRequires: pkgconfig(sqlite3) >= 3.6.23 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: mariadb-server +BuildRequires: %{mysql}-server BuildRequires: postgresql-server %{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} @@ -82,7 +87,7 @@ Summary: Akonadi MySQL backend support # upgrade path Obsoletes: akonadi < 1.7.90-2 Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: mariadb-server +Requires: %{mysql}-server Requires: qt4-mysql%{?_isa} Requires(post): %{_sbindir}/update-alternatives Requires(postun): %{_sbindir}/update-alternatives @@ -207,7 +212,7 @@ fi %changelog * Sat Sep 27 2014 Rex Dieter 1.13.0-4 -- explicitly s/mysql-server/mariadb-server/ +- explicitly Requires: mariadb-server/mysql-server as appropriate * Mon Sep 15 2014 Rex Dieter 1.13.0-3 - pull in some upstream fixes From 8acc3d6ce7909eb1ed0cfb575bc59cef48703340 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Fri, 31 Oct 2014 08:02:57 -0500 Subject: [PATCH 25/78] latest 1.13 branch fixes --- ...r-overflow-in-AKTEST_FAKESERVER_MAIN.patch | 2 +- ...-t-crash-when-setmntent-returns-NULL.patch | 29 ++++++++++++ ...t-from-Q_ASSERT-breaks-unit-tests-in.patch | 37 +++++++++++++++ ...ed-variable-warnings-in-release-mode.patch | 32 +++++++++++++ ...piler-supports-all-required-C-11-fea.patch | 45 +++++++++++++++++++ akonadi.spec | 10 ++++- 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 0006-Don-t-crash-when-setmntent-returns-NULL.patch create mode 100644 0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch create mode 100644 0008-Suppress-unused-variable-warnings-in-release-mode.patch create mode 100644 0009-Test-whether-compiler-supports-all-required-C-11-fea.patch diff --git a/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch index 8cfaaae..79ca35f 100644 --- a/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch +++ b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch @@ -1,7 +1,7 @@ From 01c86229f9e26d9e036f6f2ab405659ed836b5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 8 Sep 2014 15:36:18 +0200 -Subject: [PATCH 5/5] Fix buffer overflow in AKTEST_FAKESERVER_MAIN() +Subject: [PATCH 5/9] Fix buffer overflow in AKTEST_FAKESERVER_MAIN() --- shared/aktest.h | 2 +- diff --git a/0006-Don-t-crash-when-setmntent-returns-NULL.patch b/0006-Don-t-crash-when-setmntent-returns-NULL.patch new file mode 100644 index 0000000..892b402 --- /dev/null +++ b/0006-Don-t-crash-when-setmntent-returns-NULL.patch @@ -0,0 +1,29 @@ +From ca59eb345cfef368242929ea33beca4bff837e9d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Thu, 18 Sep 2014 16:54:26 +0200 +Subject: [PATCH 6/9] 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; +-- +1.9.3 + diff --git a/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch b/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch new file mode 100644 index 0000000..5335649 --- /dev/null +++ b/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch @@ -0,0 +1,37 @@ +From c516ec5c28d603aea0df6165f66a3a5d0a0191c4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Fri, 19 Sep 2014 10:50:23 +0200 +Subject: [PATCH 7/9] 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; + } + +-- +1.9.3 + diff --git a/0008-Suppress-unused-variable-warnings-in-release-mode.patch b/0008-Suppress-unused-variable-warnings-in-release-mode.patch new file mode 100644 index 0000000..91fe98a --- /dev/null +++ b/0008-Suppress-unused-variable-warnings-in-release-mode.patch @@ -0,0 +1,32 @@ +From b35fcb64c3ba3df95f62d0d129adb791ce2bad15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Fri, 19 Sep 2014 11:10:13 +0200 +Subject: [PATCH 8/9] 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; + } + +-- +1.9.3 + diff --git a/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch b/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch new file mode 100644 index 0000000..f965f37 --- /dev/null +++ b/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch @@ -0,0 +1,45 @@ +From e1c69c277ea6005cc358434679b83fa1cb752756 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Tue, 23 Sep 2014 18:00:34 +0200 +Subject: [PATCH 9/9] 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) +-- +1.9.3 + diff --git a/akonadi.spec b/akonadi.spec index 7e0dffb..b0e4f12 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -24,7 +24,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 4%{?dist} +Release: 5%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -48,6 +48,11 @@ 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 + %define mysql_conf_timestamp 20140709 @@ -211,6 +216,9 @@ fi %changelog +* Fri Oct 31 2014 Rex Dieter 1.13.0-5 +- latest 1.13 branch fixes + * Sat Sep 27 2014 Rex Dieter 1.13.0-4 - explicitly Requires: mariadb-server/mysql-server as appropriate From f8437d9b98ad772125d5b13d5e64ec1a2afcfdf3 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 8 Jan 2015 08:58:11 -0600 Subject: [PATCH 26/78] drop el6/cmake hacks --- akonadi.spec | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index b0e4f12..2a03b15 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -5,12 +5,6 @@ # 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 - # legacy nepomuk/soprano support (ie, kde < 4.13) %if 0%{?fedora} < 20 %define soprano 1 @@ -24,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 5%{?dist} +Release: 6%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -58,7 +52,7 @@ Patch9: 0009-Test-whether-compiler-supports-all-required-C-11-fea.patch BuildRequires: automoc4 BuildRequires: boost-devel -BuildRequires: %{cmake_pkg} >= 2.8.8 +BuildRequires: cmake >= 2.8.8 # for xsltproc BuildRequires: libxslt BuildRequires: pkgconfig(QtDBus) pkgconfig(QtSql) pkgconfig(QtXml) @@ -113,7 +107,7 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %build mkdir -p %{_target_platform} pushd %{_target_platform} -%{?cmake28}%{!?cmake28:%{?cmake}} \ +%{cmake} \ -DCONFIG_INSTALL_DIR=%{_sysconfdir} \ %{?database_backend:-DDATABASE_BACKEND=%{database_backend}} \ -DINSTALL_QSQLITE_IN_QT_PREFIX:BOOL=ON \ @@ -216,6 +210,9 @@ fi %changelog +* Thu Jan 08 2015 Rex Dieter 1.13.0-6 +- drop el6/cmake hacks + * Fri Oct 31 2014 Rex Dieter 1.13.0-5 - latest 1.13 branch fixes From ea4f267da47ea5de95d824bb76370e6bec39e73d Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Tue, 27 Jan 2015 18:02:57 +0100 Subject: [PATCH 27/78] Rebuild for boost 1.57.0 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 2a03b15..841e0d4 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 6%{?dist} +Release: 7%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -210,6 +210,9 @@ fi %changelog +* Tue Jan 27 2015 Petr Machata - 1.13.0-7 +- Rebuild for boost 1.57.0 + * Thu Jan 08 2015 Rex Dieter 1.13.0-6 - drop el6/cmake hacks From adf30afcb0ca46b6eedf9bdedbd9275f5b52e4f4 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sat, 31 Jan 2015 22:05:52 -0600 Subject: [PATCH 28/78] latest 1.13 branch fixes --- ...MAKE_FLAGS-the-right-way-in-try_comp.patch | 4 +- ...-test-directories-if-AKONADI_BUILD_T.patch | 6 +- ...ifying-items-tags-via-Tag-RID-or-GID.patch | 4 +- 0004-Fix-typo-in-if-condition.patch | 4 +- ...r-overflow-in-AKTEST_FAKESERVER_MAIN.patch | 4 +- ...-t-crash-when-setmntent-returns-NULL.patch | 4 +- ...t-from-Q_ASSERT-breaks-unit-tests-in.patch | 6 +- ...ed-variable-warnings-in-release-mode.patch | 4 +- ...piler-supports-all-required-C-11-fea.patch | 4 +- ...ng-a-QTimer-with-a-negative-interval.patch | 27 + 0011-Convert-some-qDebugs-to-akDebugs.patch | 119 +++ ...the-amount-of-allocations-required-t.patch | 604 +++++++++++++++ ...y-strings-for-table-and-column-names.patch | 176 +++++ ...o-semicolon-after-Q_DECLARE_METATYPE.patch | 38 + ...-instead-of-manual-lock-unlock-calls.patch | 112 +++ ...t-instead-of-a-plain-bool-for-Entity.patch | 37 + ...-one-hash-lookup-to-retrieve-value-f.patch | 32 + ...kip-value-condition-on-invalid-flags.patch | 38 + ...-Do-not-retrieve-known-key-used-in-t.patch | 107 +++ ...-amount-of-SQL-queries-by-caching-Pa.patch | 262 +++++++ ...t-for-CASE.WHEN.THEN-SQL-statements-.patch | 241 ++++++ ...for-CollectionStatistics-to-signific.patch | 691 ++++++++++++++++++ ...-new-PartType-when-it-does-not-exist.patch | 105 +++ ...ix-compilation-with-strict-iterators.patch | 25 + ...alls-to-PimItem-flags-and-PimItem-ta.patch | 58 ++ ...e-collection-listing-in-SearchHelper.patch | 374 ++++++++++ ...ts-in-StatisticsCache-as-suggested-b.patch | 185 +++++ ...r-benchmark-and-keep-static-data-aro.patch | 74 ++ ...t-of-allocations-by-preallocating-a-.patch | 61 ++ ...capacity-of-16-for-the-returned-list.patch | 119 +++ akonadi.spec | 27 +- 31 files changed, 3530 insertions(+), 22 deletions(-) create mode 100644 0010-prevent-starting-a-QTimer-with-a-negative-interval.patch create mode 100644 0011-Convert-some-qDebugs-to-akDebugs.patch create mode 100644 0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch create mode 100644 0013-Intern-entity-strings-for-table-and-column-names.patch create mode 100644 0014-No-semicolon-after-Q_DECLARE_METATYPE.patch create mode 100644 0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch create mode 100644 0016-Use-an-QAtomicInt-instead-of-a-plain-bool-for-Entity.patch create mode 100644 0017-Optimize-Only-do-one-hash-lookup-to-retrieve-value-f.patch create mode 100644 0018-Optimize-Skip-value-condition-on-invalid-flags.patch create mode 100644 0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch create mode 100644 0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch create mode 100644 0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch create mode 100644 0022-Implement-cache-for-CollectionStatistics-to-signific.patch create mode 100644 0023-Always-create-a-new-PartType-when-it-does-not-exist.patch create mode 100644 0024-Fix-compilation-with-strict-iterators.patch create mode 100644 0025-Avoid-repeated-calls-to-PimItem-flags-and-PimItem-ta.patch create mode 100644 0026-Avoid-recursive-collection-listing-in-SearchHelper.patch create mode 100644 0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch create mode 100644 0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch create mode 100644 0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch create mode 100644 0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch diff --git a/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch b/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch index 0bad3aa..c7f6ea7 100644 --- a/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch +++ b/0001-FindSqlite-Use-CMAKE_FLAGS-the-right-way-in-try_comp.patch @@ -1,7 +1,7 @@ From b60702e0b7041c56a3cb52c209204d28406f3ce5 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Wed, 13 Aug 2014 14:43:04 +0300 -Subject: [PATCH 1/5] FindSqlite: Use CMAKE_FLAGS the right way in +Subject: [PATCH 01/30] FindSqlite: Use CMAKE_FLAGS the right way in try_compile(). This fixes f90774f1 ("Check whether Sqlite is compiled with @@ -34,5 +34,5 @@ index ad8cdb4..c43a7b5 100644 message(STATUS "Sqlite ${SQLITE_VERSION} was found, but it is not compiled with -DSQLITE_ENABLE_UNLOCK_NOTIFY") endif() -- -1.9.3 +2.1.0 diff --git a/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch b/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch index 8efd56e..e5088e6 100644 --- a/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch +++ b/0002-Do-not-enter-the-test-directories-if-AKONADI_BUILD_T.patch @@ -1,8 +1,8 @@ From 2146519108ec66300328b7b3979477c7789795d3 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Wed, 13 Aug 2014 23:22:11 +0300 -Subject: [PATCH 2/5] Do not enter the test/ directories if AKONADI_BUILD_TESTS - is off. +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 @@ -78,5 +78,5 @@ index e4829f3..275938d 100644 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS}") if(MYSQLD_EXECUTABLE) -- -1.9.3 +2.1.0 diff --git a/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch b/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch index 8538502..e2a495c 100644 --- a/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch +++ b/0003-STORE-Allow-modifying-items-tags-via-Tag-RID-or-GID.patch @@ -1,7 +1,7 @@ From 9734074267bacd39aeb29c7a0d7df7cadb212d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 11 Jul 2014 18:33:39 +0200 -Subject: [PATCH 3/5] STORE: Allow modifying items tags via Tag RID or GID +Subject: [PATCH 03/30] STORE: Allow modifying items tags via Tag RID or GID Tags RID is of course allowed only to resources @@ -197,5 +197,5 @@ index 763ea30..634a26c 100644 throw HandlerException("Unable to resolve tags"); } -- -1.9.3 +2.1.0 diff --git a/0004-Fix-typo-in-if-condition.patch b/0004-Fix-typo-in-if-condition.patch index b3febdc..b0ae0da 100644 --- a/0004-Fix-typo-in-if-condition.patch +++ b/0004-Fix-typo-in-if-condition.patch @@ -1,7 +1,7 @@ From e52f9be20e566e507e77421f1243f51aa2fe8e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 25 Aug 2014 14:35:14 +0200 -Subject: [PATCH 4/5] Fix typo in if condition +Subject: [PATCH 04/30] Fix typo in if condition BUG: 338483 FIXED-IN: 1.13.1 @@ -23,5 +23,5 @@ index 43f03ba..ad3682f 100644 } -- -1.9.3 +2.1.0 diff --git a/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch index 79ca35f..0203e45 100644 --- a/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch +++ b/0005-Fix-buffer-overflow-in-AKTEST_FAKESERVER_MAIN.patch @@ -1,7 +1,7 @@ From 01c86229f9e26d9e036f6f2ab405659ed836b5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 8 Sep 2014 15:36:18 +0200 -Subject: [PATCH 5/9] Fix buffer overflow in AKTEST_FAKESERVER_MAIN() +Subject: [PATCH 05/30] Fix buffer overflow in AKTEST_FAKESERVER_MAIN() --- shared/aktest.h | 2 +- @@ -21,5 +21,5 @@ index b1b9caa..3026304 100644 fakeArgv[i] = options[i]; \ } \ -- -1.9.3 +2.1.0 diff --git a/0006-Don-t-crash-when-setmntent-returns-NULL.patch b/0006-Don-t-crash-when-setmntent-returns-NULL.patch index 892b402..e9f2e4c 100644 --- a/0006-Don-t-crash-when-setmntent-returns-NULL.patch +++ b/0006-Don-t-crash-when-setmntent-returns-NULL.patch @@ -1,7 +1,7 @@ From ca59eb345cfef368242929ea33beca4bff837e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Thu, 18 Sep 2014 16:54:26 +0200 -Subject: [PATCH 6/9] Don't crash when setmntent returns NULL +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 @@ -25,5 +25,5 @@ index b04a812..b51c330 100644 if (qstrcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) { continue; -- -1.9.3 +2.1.0 diff --git a/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch b/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch index 5335649..15386e4 100644 --- a/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch +++ b/0007-Don-t-call-insert-from-Q_ASSERT-breaks-unit-tests-in.patch @@ -1,8 +1,8 @@ From c516ec5c28d603aea0df6165f66a3a5d0a0191c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 19 Sep 2014 10:50:23 +0200 -Subject: [PATCH 7/9] Don't call insert() from Q_ASSERT - breaks unit-tests in - Release mode +Subject: [PATCH 07/30] Don't call insert() from Q_ASSERT - breaks unit-tests + in Release mode --- server/tests/unittest/collectionreferencetest.cpp | 6 ++++-- @@ -33,5 +33,5 @@ index 1700c75..1b10c55 100644 } -- -1.9.3 +2.1.0 diff --git a/0008-Suppress-unused-variable-warnings-in-release-mode.patch b/0008-Suppress-unused-variable-warnings-in-release-mode.patch index 91fe98a..1f4c67e 100644 --- a/0008-Suppress-unused-variable-warnings-in-release-mode.patch +++ b/0008-Suppress-unused-variable-warnings-in-release-mode.patch @@ -1,7 +1,7 @@ From b35fcb64c3ba3df95f62d0d129adb791ce2bad15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 19 Sep 2014 11:10:13 +0200 -Subject: [PATCH 8/9] Suppress unused variable warnings in release mode +Subject: [PATCH 08/30] Suppress unused variable warnings in release mode --- server/tests/unittest/collectionreferencetest.cpp | 2 ++ @@ -28,5 +28,5 @@ index 1b10c55..9c98f28 100644 } -- -1.9.3 +2.1.0 diff --git a/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch b/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch index f965f37..7bd55ce 100644 --- a/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch +++ b/0009-Test-whether-compiler-supports-all-required-C-11-fea.patch @@ -1,7 +1,7 @@ From e1c69c277ea6005cc358434679b83fa1cb752756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Tue, 23 Sep 2014 18:00:34 +0200 -Subject: [PATCH 9/9] Test whether compiler supports all required C++11 +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 @@ -41,5 +41,5 @@ index e081d23..2d790c9 100644 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) -- -1.9.3 +2.1.0 diff --git a/0010-prevent-starting-a-QTimer-with-a-negative-interval.patch b/0010-prevent-starting-a-QTimer-with-a-negative-interval.patch new file mode 100644 index 0000000..59b52a3 --- /dev/null +++ b/0010-prevent-starting-a-QTimer-with-a-negative-interval.patch @@ -0,0 +1,27 @@ +From de9bd9043e8878fc472ced1669bc7d49b07c2062 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ren=C3=A9=20J=2EV=2E=20Bertin?= +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 + diff --git a/0011-Convert-some-qDebugs-to-akDebugs.patch b/0011-Convert-some-qDebugs-to-akDebugs.patch new file mode 100644 index 0000000..f4ee199 --- /dev/null +++ b/0011-Convert-some-qDebugs-to-akDebugs.patch @@ -0,0 +1,119 @@ +From 1d79c645ffbd858517f07cee3143dc64fac7c3e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 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 &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 &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 + diff --git a/0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch b/0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch new file mode 100644 index 0000000..2b42a63 --- /dev/null +++ b/0012-Optimize-Reduce-the-amount-of-allocations-required-t.patch @@ -0,0 +1,604 @@ +From 63f49d233ca8a4fdd3e8937ea1c80d5e57a1cbdc Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 &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 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 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& 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 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& 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 mRootCondition; ++ Query::Condition mRootCondition[NUM_CONDITIONS]; + QSqlQuery mQuery; + QueryType mType; + QStringList mColumns; +- QList mBindValues; ++ QVector mBindValues; + QVector > mSortColumns; + QStringList mGroupColumns; + QVector > 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) ++ + using namespace Akonadi::Server; + + void QueryBuilderTest::testQueryBuilder_data() + { ++ qRegisterMetaType >(); + mBuilders.clear(); + QTest::addColumn( "qbId" ); + QTest::addColumn( "sql" ); +- QTest::addColumn >( "bindValues" ); ++ QTest::addColumn >( "bindValues" ); + + QueryBuilder qb( "table", QueryBuilder::Select ); + qb.addColumn( "col1" ); + mBuilders << qb; +- QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QList(); ++ QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QVector(); + + qb.addColumn( "col2" ); + mBuilders << qb; +- QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QList(); ++ QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QVector(); + + qb.addValueCondition( "col1", Query::Equals, QVariant( 5 ) ); +- QList bindVals; ++ QVector 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(); ++ QTest::newRow( "single aggregation" ) << mBuilders.count() << QString( "SELECT count(col1) FROM table" ) << QVector(); + + 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(); ++ QTest::newRow( "single order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC" ) << QVector(); + + 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(); ++ QTest::newRow( "multiple order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC, col2 DESC" ) << QVector(); + + 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(); ++ QTest::newRow( "SELECT with LIMIT" ) << mBuilders.count() << QString( "SELECT col1 FROM table LIMIT 1" ) << QVector(); + + 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, bindValues ); ++ QFETCH( QVector, 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 + diff --git a/0013-Intern-entity-strings-for-table-and-column-names.patch b/0013-Intern-entity-strings-for-table-and-column-names.patch new file mode 100644 index 0000000..1b40f2a --- /dev/null +++ b/0013-Intern-entity-strings-for-table-and-column-names.patch @@ -0,0 +1,176 @@ +From a04809a44c235bed854adc3bd49ca75b9673bf1f Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 ::::tableName() + { +- return QLatin1String( "" ); ++ static const QString tableName = QLatin1String( "" ); ++ return tableName; + } + + QStringList ::columnNames() + { +- QStringList rv; ++ static const QStringList columns = QStringList() + +- rv.append( QLatin1String( "" ) ); ++ << Column() + +- return rv; ++ ; ++ return columns; + } + + QStringList ::fullColumnNames() + { +- QStringList rv; ++ static const QStringList columns = QStringList() + +- rv.append( QLatin1String( "." ) ); ++ << FullColumnName() + +- return rv; ++ ; ++ return columns; + } + + + QString ::Column() + { +- return QLatin1String( "" ); ++ static const QString column = QLatin1String( "" ); ++ return column; + } + + QString ::FullColumnName() + { +- return tableName() + QLatin1String( "." ); ++ static const QString column = QLatin1String( "." ); ++ return column; + } + + +@@ -399,7 +404,6 @@ QVector<> Relation + + +-Table + + // data retrieval for n:m relations + QVector<> ::s() const +@@ -408,14 +412,17 @@ QVector<> >(); + +- QueryBuilder qb( QLatin1String(""), QueryBuilder::Select ); ++ QueryBuilder qb( ::tableName(), QueryBuilder::Select ); ++ static const QStringList columns = QStringList() + +- qb.addColumn( QLatin1String("." ) ); ++ << ::FullColumnName() + +- qb.addJoin( QueryBuilder::InnerJoin, QLatin1String(""), +- QLatin1String("._"), +- QLatin1String(".") ); +- qb.addValueCondition( QLatin1String("._"), Query::Equals, id() ); ++ ; ++ qb.addColumns(columns); ++ qb.addJoin( QueryBuilder::InnerJoin, ::tableName(), ++ ::rightFullColumnName(), ++ ::FullColumnName() ); ++ qb.addValueCondition( ::leftFullColumnName(), Query::Equals, id() ); + + if ( !qb.exec() ) { + akDebug() << "Error during selection of records from table Relation" +@@ -546,7 +553,7 @@ bool ::update() + + + +- qb.addValueCondition( QLatin1String("id"), Query::Equals, id() ); ++ qb.addValueCondition( idColumn(), Query::Equals, id() ); + + + if ( !qb.exec() ) { +@@ -622,27 +629,32 @@ void ::enableCache( bool enable ) + // SQL table information + QString ::tableName() + { +- return QLatin1String( "" ); ++ static const QString table = QLatin1String( "" ); ++ return table; + } + + QString ::leftColumn() + { +- return QLatin1String( "_" ); ++ static const QString column = QLatin1String( "_" ); ++ return column; + } + + QString ::leftFullColumnName() + { +- return tableName() + QLatin1String( "." ) + leftColumn(); ++ static const QString column = QLatin1String( "._" ); ++ return column; + } + + QString ::rightColumn() + { +- return QLatin1String( "_" ); ++ static const QString column = QLatin1String( "_" ); ++ return column; + } + + QString ::rightFullColumnName() + { +- return tableName() + QLatin1String( "." ) + rightColumn(); ++ static const QString column = QLatin1String( "._" ); ++ return column; + } + + +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>() + + << QLatin1String( "Table" ) + +@@ -182,7 +182,7 @@ set( ) + + QueryBuilder qb( tableName(), QueryBuilder::Select ); + qb.addColumns( columnNames() ); +- qb.addValueCondition( QLatin1String(""), Query::Equals, ); ++ qb.addValueCondition( Column(), Query::Equals, ); + if ( !qb.exec() ) { + akDebug() << "Error during selection of record with " + << << "from table" << tableName() +-- +2.1.0 + diff --git a/0014-No-semicolon-after-Q_DECLARE_METATYPE.patch b/0014-No-semicolon-after-Q_DECLARE_METATYPE.patch new file mode 100644 index 0000000..684ece9 --- /dev/null +++ b/0014-No-semicolon-after-Q_DECLARE_METATYPE.patch @@ -0,0 +1,38 @@ +From f49b99f5a49da1a78b0ced930b6438bb53b49fdd Mon Sep 17 00:00:00 2001 +From: Volker Krause +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) + Q_DECLARE_METATYPE(QVector) + Q_DECLARE_METATYPE(QVector) +-- +2.1.0 + diff --git a/0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch b/0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch new file mode 100644 index 0000000..8b4f79c --- /dev/null +++ b/0015-Use-QMutexLocker-instead-of-manual-lock-unlock-calls.patch @@ -0,0 +1,112 @@ +From f5a0e3f1f4787b6a48880e42463ae38dce336a8f Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 ::Private::addToCache( const +- cacheMutex.lock(); ++ QMutexLocker lock(&cacheMutex); + + idCache.insert( entry.id(), entry ); + + + nameCache.insert( entry.name(), entry ); + +- cacheMutex.unlock(); + } + + +@@ -264,12 +263,10 @@ int ::count( const QString &column, const + bool ::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 ::exists( qint64 id ) + bool ::exists( const &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 ::remove( qint64 id ) + void ::invalidateCache() const + { + if ( Private::cacheEnabled ) { +- Private::cacheMutex.lock(); ++ QMutexLocker lock(&Private::cacheMutex); + + Private::idCache.remove( id() ); + + + Private::nameCache.remove( name() ); + +- Private::cacheMutex.unlock(); + } + } + + void ::invalidateCompleteCache() + { + if ( Private::cacheEnabled ) { +- Private::cacheMutex.lock(); ++ QMutexLocker lock(&Private::cacheMutex); + + Private::idCache.clear(); + + + Private::nameCache.clear(); + +- 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( ) + + + if ( Private::cacheEnabled ) { +- Private::cacheMutex.lock(); ++ QMutexLocker lock(&Private::cacheMutex); + if ( Private::.contains( ) ) { + const tmp = Private::.value( ); +- Private::cacheMutex.unlock(); + return tmp; + } +- Private::cacheMutex.unlock(); + } + + QSqlDatabase db = DataStore::self()->database(); +-- +2.1.0 + diff --git a/0016-Use-an-QAtomicInt-instead-of-a-plain-bool-for-Entity.patch b/0016-Use-an-QAtomicInt-instead-of-a-plain-bool-for-Entity.patch new file mode 100644 index 0000000..658e55f --- /dev/null +++ b/0016-Use-an-QAtomicInt-instead-of-a-plain-bool-for-Entity.patch @@ -0,0 +1,37 @@ +From 8a113985cda1693c8158916065bd54e57d028cda Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 ::Private : public QSharedData + static void addToCache( const & entry ); + + // cache +- static bool cacheEnabled; ++ static QAtomicInt cacheEnabled; + static QMutex cacheMutex; + + static QHash<qint64, > idCache; +@@ -111,7 +111,7 @@ class ::Private : public QSharedData + + + // static members +-bool ::Private::cacheEnabled = false; ++QAtomicInt ::Private::cacheEnabled(0); + QMutex ::Private::cacheMutex; + + QHash<qint64, > ::Private::idCache; +-- +2.1.0 + diff --git a/0017-Optimize-Only-do-one-hash-lookup-to-retrieve-value-f.patch b/0017-Optimize-Only-do-one-hash-lookup-to-retrieve-value-f.patch new file mode 100644 index 0000000..c2a0728 --- /dev/null +++ b/0017-Optimize-Only-do-one-hash-lookup-to-retrieve-value-f.patch @@ -0,0 +1,32 @@ +From 202ffa522668087cc133026febf21a7de8963218 Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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( ) + + if ( Private::cacheEnabled ) { + QMutexLocker lock(&Private::cacheMutex); +- if ( Private::.contains( ) ) { +- const tmp = Private::.value( ); +- return tmp; ++ QHash<, >::const_iterator it = Private::.constFind(); ++ if ( it != Private::.constEnd() ) { ++ return it.value(); + } + } + +-- +2.1.0 + diff --git a/0018-Optimize-Skip-value-condition-on-invalid-flags.patch b/0018-Optimize-Skip-value-condition-on-invalid-flags.patch new file mode 100644 index 0000000..391d5c9 --- /dev/null +++ b/0018-Optimize-Skip-value-condition-on-invalid-flags.patch @@ -0,0 +1,38 @@ +From 7cbff48f5782d1f7f844678e6b785aeb419b0c47 Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 + diff --git a/0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch b/0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch new file mode 100644 index 0000000..bb8c333 --- /dev/null +++ b/0019-Optimize-queries-Do-not-retrieve-known-key-used-in-t.patch @@ -0,0 +1,107 @@ +From e52b57b7a9f0303c0c710e60870d0ec265d32541 Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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::, Q_MOVABLE_T + + using namespace Akonadi::Server; + ++static QStringList removeEntry(QStringList list, const QString& entry) ++{ ++ list.removeOne(entry); ++ return list; ++} ++ + + + +@@ -179,7 +185,8 @@ set( ) + return (); + + QueryBuilder qb( tableName(), QueryBuilder::Select ); +- qb.addColumns( columnNames() ); ++ static const QStringList columns = removeEntry(columnNames(), Column()); ++ qb.addColumns( columns ); + qb.addValueCondition( Column(), Query::Equals, ); + if ( !qb.exec() ) { + akDebug() << "Error during selection of record with " +@@ -191,21 +198,36 @@ set( ) + return (); + } + ++ ++ int valueIndex = 0; ++ ++ const value = ++ ++ ++ ; ++ ++ ++ (qb.query().isNull(valueIndex)) ? ++ () : ++ ++ ++ Utils::variantToString( qb.query().value( valueIndex ) ) ++ ++ ++ static_cast<Tristate>(qb.query().value( valueIndex ).value<int>()) ++ ++ ++ qb.query().value( valueIndex ).value<>() ++ ++ ++ ; ++valueIndex; ++ ++ ++ ++ + rv( + +- (qb.query().isNull()) ? +- () : +- +- +- Utils::variantToString( qb.query().value( ) ) +- +- +- static_cast<Tristate>(qb.query().value( ).value<int>()) +- +- +- qb.query().value( ).value<>() +- +- ++ value + , + + ); +-- +2.1.0 + diff --git a/0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch b/0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch new file mode 100644 index 0000000..b433f23 --- /dev/null +++ b/0020-Avoid-ridiculous-amount-of-SQL-queries-by-caching-Pa.patch @@ -0,0 +1,262 @@ +From 215b188d891d5236fe94131d176d7ddc3ae02d5d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 : private Entity + static retrieveById( qint64 id ); + + +- ++ + /** Returns the record with name @p name. */ + static retrieveByName( const &name ); + + ++ ++ ++ static PartType retrieveByFQName( const QString &ns, const QString &name ); ++ ++ + /** Retrieve all records from this table. */ + static ::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 ::Private::addToCache( const + ++ ++ ++ ++ nameCache.insert( entry.ns() + QLatin1Char(':') + entry.name(), entry ); ++ ++ + nameCache.insert( entry.name(), entry ); ++ ++ + + } + +@@ -323,7 +331,7 @@ QVector< > ++ + ::retrieveByName( const &name ) + { + +@@ -333,6 +341,19 @@ QVector< > ++PartType PartType::retrieveByFQName( const QString & ns, const QString & name ) ++{ ++ const QString fqname = ns + QLatin1Char(':') + name; ++ ++ ns ++ name ++ fqname ++ nameCache ++ ++} ++ ++ + QVector<> ::retrieveAll() + { + QSqlDatabase db = DataStore::self()->database(); +@@ -588,7 +609,15 @@ void ::invalidateCache() const + Private::idCache.remove( id() ); + + ++ ++ ++ ++ Private::nameCache.remove( ns() + QLatin1Char(':') + name() ); ++ ++ + Private::nameCache.remove( name() ); ++ ++ + + } + } +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( ) + + + ++ ++ + + + + if ( Private::cacheEnabled ) { + QMutexLocker lock(&Private::cacheMutex); +- QHash<, >::const_iterator it = Private::.constFind(); ++ QHash<, >::const_iterator it = Private::.constFind(); + if ( it != Private::.constEnd() ) { + return it.value(); + } +@@ -188,6 +190,9 @@ set( ) + static const QStringList columns = removeEntry(columnNames(), Column()); + qb.addColumns( columns ); + qb.addValueCondition( Column(), Query::Equals, ); ++ ++ qb.addValueCondition( Column(), Query::Equals, ); ++ + if ( !qb.exec() ) { + akDebug() << "Error during selection of record with " + << << "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 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 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 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 + diff --git a/0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch b/0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch new file mode 100644 index 0000000..d452356 --- /dev/null +++ b/0021-Implement-support-for-CASE.WHEN.THEN-SQL-statements-.patch @@ -0,0 +1,241 @@ +From 9698d589e4c2b489f406fe1a823d4bb42c322f71 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 > 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 < +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 + + 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 ++ * ++ * 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 ++ ++#include ++ ++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 ++ * ++ * 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 ++#include ++ ++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 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 &flags, +- bool *flagsChanged, bool silent ) ++ bool *flagsChanged, const Collection &col, bool silent ) + { + QSet removedFlags; + QSet addedFlags; +@@ -258,7 +258,7 @@ bool DataStore::setItemsFlags( const PimItem::List &items, const QVector & + } + + 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 &flags, +- bool *flagsChanged, bool silent ) ++ bool *flagsChanged, const Collection &col, bool silent ) + { + QSet removedFlags; + QVariantList itemsIds; +@@ -393,7 +393,7 @@ bool DataStore::removeItemsFlags( const PimItem::List &items, const QVectoritemsFlagsChanged( items, QSet(), removedFlags ); ++ mNotificationCollector->itemsFlagsChanged( items, QSet(), 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 &flags, bool *flagsChanged = 0, bool silent = false ); ++ virtual bool setItemsFlags( const PimItem::List &items, const QVector &flags, ++ bool *flagsChanged = 0, const Collection &col = Collection(), bool silent = false ); + virtual bool appendItemsFlags( const PimItem::List &items, const QVector &flags, bool *flagsChanged = 0, + bool checkIfExists = true, const Collection &col = Collection(), bool silent = false ); +- virtual bool removeItemsFlags( const PimItem::List &items, const QVector &flags, bool *tagsChanged = 0, bool silent = false ); ++ virtual bool removeItemsFlags( const PimItem::List &items, const QVector &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() ); + } + +@@ -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 &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 &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 &flags, + bool *flagsChanged = 0, ++ const Collection &col = Collection(), + bool silent = false ); + virtual bool appendItemsFlags( const PimItem::List &items, + const QVector &flags, +@@ -51,6 +52,7 @@ class FakeDataStore: public DataStore + virtual bool removeItemsFlags( const PimItem::List &items, + const QVector &flags, + bool *flagsChanged = 0, ++ const Collection &col = Collection(), + bool silent = false ); + + virtual bool setItemsTags( const PimItem::List &items, +-- +2.1.0 + diff --git a/0023-Always-create-a-new-PartType-when-it-does-not-exist.patch b/0023-Always-create-a-new-PartType-when-it-does-not-exist.patch new file mode 100644 index 0000000..ba6a473 --- /dev/null +++ b/0023-Always-create-a-new-PartType-when-it-does-not-exist.patch @@ -0,0 +1,105 @@ +From 1ce732668b2b3e4d735665bd60e1a18f139b1de2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 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 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 + diff --git a/0024-Fix-compilation-with-strict-iterators.patch b/0024-Fix-compilation-with-strict-iterators.patch new file mode 100644 index 0000000..6eb77f8 --- /dev/null +++ b/0024-Fix-compilation-with-strict-iterators.patch @@ -0,0 +1,25 @@ +From fcae659e9be22b00b0efe52f19a89b8fce26a925 Mon Sep 17 00:00:00 2001 +From: David Faure +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 + diff --git a/0025-Avoid-repeated-calls-to-PimItem-flags-and-PimItem-ta.patch b/0025-Avoid-repeated-calls-to-PimItem-flags-and-PimItem-ta.patch new file mode 100644 index 0000000..4678e9c --- /dev/null +++ b/0025-Avoid-repeated-calls-to-PimItem-flags-and-PimItem-ta.patch @@ -0,0 +1,58 @@ +From 55dc6d141a20e2438308214ab60c18e282dd7b43 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 & + 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 & + } + + 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 + diff --git a/0026-Avoid-recursive-collection-listing-in-SearchHelper.patch b/0026-Avoid-recursive-collection-listing-in-SearchHelper.patch new file mode 100644 index 0000000..e5f6fba --- /dev/null +++ b/0026-Avoid-recursive-collection-listing-in-SearchHelper.patch @@ -0,0 +1,374 @@ +From 059d52845cbbc10e882764f64245c5995af4e741 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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() << 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 + #include "entities.h" + + #include +@@ -89,55 +90,77 @@ QString SearchHelper::extractMimetype( const QList &junks, int start + } + + +-QVector SearchHelper::listCollectionsRecursive( const QVector &ancestors, const QStringList &mimeTypes ) ++static qint64 parentCollectionId(qint64 collectionId) + { +- QVector recursiveChildren; +- Q_FOREACH ( qint64 ancestor, ancestors ) { +- QVector 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 SearchHelper::matchSubcollectionsByMimeType(const QVector &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(); ++ } ++ ++ QMap /* 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 results; ++ if (ancestors.contains(0)) { ++ Q_FOREACH (const QVector &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 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 splitLine( const QByteArray &line ); + static QString extractMimetype( const QList &junks, int start ); +- static QVector listCollectionsRecursive( const QVector &ancestors, const QStringList &mimeTypes ); ++ static QVector matchSubcollectionsByMimeType( const QVector &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 ++ * ++ * 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 ++ ++#include ++ ++using namespace Akonadi::Server; ++ ++Q_DECLARE_METATYPE(QList) ++Q_DECLARE_METATYPE(QList) ++ ++ ++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>("ancestors"); ++ QTest::addColumn("mimetypes"); ++ QTest::addColumn>("expectedResults"); ++ ++ QTest::newRow("") << QVector({ 0 }) ++ << QStringList({ QLatin1String("text/plain") }) ++ << QVector({ col4.id(), col5.id(), col7.id() }); ++ QTest::newRow("") << QVector({ 0 }) ++ << QStringList({ QLatin1String("application/octet-stream") }) ++ << QVector({ col2.id(), col3.id(), col6.id(), col8.id() }); ++ QTest::newRow("") << QVector({ col1.id() }) ++ << QStringList({ QLatin1String("text/plain") }) ++ << QVector({ col4.id() }); ++ QTest::newRow("") << QVector({ col1.id() }) ++ << QStringList({ QLatin1String("unique/mime-type") }) ++ << QVector(); ++ QTest::newRow("") << QVector({ col2.id(), col7.id() }) ++ << QStringList({ QLatin1String("application/octet-stream") }) ++ << QVector({ col3.id(), col8.id() }); ++ } ++ ++ void testSearchHelperCollectionListing() ++ { ++ QFETCH(QVector, ancestors); ++ QFETCH(QStringList, mimetypes); ++ QFETCH(QVector, expectedResults); ++ ++ QVector 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 + diff --git a/0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch b/0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch new file mode 100644 index 0000000..faac95b --- /dev/null +++ b/0027-Minor-improvements-in-StatisticsCache-as-suggested-b.patch @@ -0,0 +1,185 @@ +From ac118e12fca25826340b6c8561939be19c4b7170 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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() ); + } +@@ -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 + diff --git a/0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch b/0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch new file mode 100644 index 0000000..20d26c5 --- /dev/null +++ b/0028-Extend-imapparser-benchmark-and-keep-static-data-aro.patch @@ -0,0 +1,74 @@ +From da5751c7b1589d2ea5800a3cf96dfc93b23b9783 Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 + diff --git a/0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch b/0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch new file mode 100644 index 0000000..279a966 --- /dev/null +++ b/0029-Reduce-the-amount-of-allocations-by-preallocating-a-.patch @@ -0,0 +1,61 @@ +From 89357c7b0fc5e76091510af504058c036fa1b2f9 Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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( "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 + diff --git a/0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch b/0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch new file mode 100644 index 0000000..bf96092 --- /dev/null +++ b/0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch @@ -0,0 +1,119 @@ +From c733429f4fa9696fb027ddc946e54f6bbb68deaf Mon Sep 17 00:00:00 2001 +From: Milian Wolff +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 ) + class ImapParserBenchmark : public QObject + { + Q_OBJECT ++ private: ++ void geneateParseParenthesizedListData() ++ { ++ QTest::addColumn( "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( "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 result; +@@ -95,6 +101,20 @@ class ImapParserBenchmark : public QObject + } + } + ++ void parseParenthesizedQList_data() ++ { ++ geneateParseParenthesizedListData(); ++ } ++ ++ void parseParenthesizedQList() ++ { ++ QFETCH( QByteArray, data ); ++ QList result; ++ QBENCHMARK { ++ ImapParser::parseParenthesizedList( data, result, 0 ); ++ } ++ } ++ + void parseString_data() + { + QTest::addColumn( "data" ); +-- +2.1.0 + diff --git a/akonadi.spec b/akonadi.spec index 841e0d4..b99b605 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 7%{?dist} +Release: 8%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -46,7 +46,27 @@ 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 %define mysql_conf_timestamp 20140709 @@ -210,6 +230,9 @@ fi %changelog +* Sat Jan 31 2015 Rex Dieter 1.13.0-8 +- latest 1.13 branch fixes + * Tue Jan 27 2015 Petr Machata - 1.13.0-7 - Rebuild for boost 1.57.0 From e63d049f4904552a77ad3a66c442f37a2c7c6cf9 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 4 Feb 2015 14:09:07 +0100 Subject: [PATCH 29/78] Bump for rebuild. --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index b99b605..e81020d 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 8%{?dist} +Release: 9%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -230,6 +230,9 @@ fi %changelog +* Wed Feb 04 2015 Petr Machata - 1.13.0-9 +- Bump for rebuild. + * Sat Jan 31 2015 Rex Dieter 1.13.0-8 - latest 1.13 branch fixes From 8b319ccb4c25dcca5f4ee8e1dc4032a8f9a09882 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Wed, 18 Feb 2015 09:02:17 -0600 Subject: [PATCH 30/78] rebuild (gcc5) --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index e81020d..2f0433f 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,7 +18,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 9%{?dist} +Release: 10%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -230,6 +230,9 @@ fi %changelog +* Wed Feb 18 2015 Rex Dieter 1.13.0-10 +- rebuild (gcc5) + * Wed Feb 04 2015 Petr Machata - 1.13.0-9 - Bump for rebuild. From a4f5b0576ff8c8bd0532f97a8e5bd433be6efd1a Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sun, 8 Mar 2015 14:17:39 -0500 Subject: [PATCH 31/78] explicitly Requires: mariadb(-sesrver) only as needed (#1199797) --- akonadi.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 2f0433f..af397c6 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -11,14 +11,15 @@ %endif %global mysql mysql -%if 0%{?fedora} || 0%{?rhel} > 6 +%if 0%{?rhel} > 6 +# el7 mariadb pkgs don't have compat Provides: mysql (apparently?) %global mysql mariadb %endif Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 10%{?dist} +Release: 11%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -230,6 +231,9 @@ fi %changelog +* Sun Mar 08 2015 Rex Dieter 1.13.0-11 +- explicitly Requires: mariadb(-sesrver) only as needed (#1199797) + * Wed Feb 18 2015 Rex Dieter 1.13.0-10 - rebuild (gcc5) From c9783c242fae0208d09e9e45b1c73c3d4369a574 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sun, 8 Mar 2015 15:00:57 -0500 Subject: [PATCH 32/78] 1.13.0-12 - explicit BuildRequires: mariadb-server - -mysql: Recommends: mariadb-server (f22+, #1199797) --- akonadi.spec | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index af397c6..fd254ea 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 11%{?dist} +Release: 12%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -86,7 +86,7 @@ BuildRequires: pkgconfig(sqlite3) >= 3.6.23 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: mariadb-server BuildRequires: postgresql-server %{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} @@ -108,6 +108,9 @@ Summary: Akonadi MySQL backend support Obsoletes: akonadi < 1.7.90-2 Requires: %{name}%{?_isa} = %{version}-%{release} Requires: %{mysql}-server +%if "%{?mysql}" != "mariadb" && 0%{?fedora} > 21 +Recommends: mariadb-server +%endif Requires: qt4-mysql%{?_isa} Requires(post): %{_sbindir}/update-alternatives Requires(postun): %{_sbindir}/update-alternatives @@ -231,6 +234,10 @@ fi %changelog +* Sun Mar 08 2015 Rex Dieter - 1.13.0-12 +- explicit BuildRequires: mariadb-server +- -mysql: Recommends: mariadb-server (f22+, #1199797) + * Sun Mar 08 2015 Rex Dieter 1.13.0-11 - explicitly Requires: mariadb(-sesrver) only as needed (#1199797) From 3ceac1733cf1ccb9c9cc92e71ffa5355bc227adb Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sat, 11 Apr 2015 18:36:53 -0500 Subject: [PATCH 33/78] use Recommends for f21 too --- akonadi.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index fd254ea..5704924 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -108,7 +108,7 @@ Summary: Akonadi MySQL backend support Obsoletes: akonadi < 1.7.90-2 Requires: %{name}%{?_isa} = %{version}-%{release} Requires: %{mysql}-server -%if "%{?mysql}" != "mariadb" && 0%{?fedora} > 21 +%if "%{?mysql}" != "mariadb" && 0%{?fedora} > 20 Recommends: mariadb-server %endif Requires: qt4-mysql%{?_isa} @@ -236,7 +236,7 @@ fi %changelog * Sun Mar 08 2015 Rex Dieter - 1.13.0-12 - explicit BuildRequires: mariadb-server -- -mysql: Recommends: mariadb-server (f22+, #1199797) +- -mysql: Recommends: mariadb-server (f21+, #1199797) * Sun Mar 08 2015 Rex Dieter 1.13.0-11 - explicitly Requires: mariadb(-sesrver) only as needed (#1199797) From 3845bc69ad8e7de3915f58a544d9a13530824a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 27 Apr 2015 15:04:52 +0200 Subject: [PATCH 34/78] Rebuild for boost --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 5704924..4a28d93 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 12%{?dist} +Release: 13%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -234,6 +234,9 @@ fi %changelog +* Mon Apr 27 2015 Daniel Vrátil - 1.13.0-13 +- Rebuild for boost + * Sun Mar 08 2015 Rex Dieter - 1.13.0-12 - explicit BuildRequires: mariadb-server - -mysql: Recommends: mariadb-server (f21+, #1199797) From bc3af2d35c7ff0351d7ab1b09acc139c274ce969 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 7 May 2015 13:47:35 -0500 Subject: [PATCH 35/78] %build: explicitly set -DCMAKE_BUILD_TYPE="Release" (-DQT_NO_DEBUG was used already) --- akonadi.spec | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 4a28d93..f931d85 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 13%{?dist} +Release: 14%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -129,14 +129,14 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf %build -mkdir -p %{_target_platform} +mkdir %{_target_platform} pushd %{_target_platform} -%{cmake} \ +%{cmake} .. \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ -DCONFIG_INSTALL_DIR=%{_sysconfdir} \ %{?database_backend:-DDATABASE_BACKEND=%{database_backend}} \ -DINSTALL_QSQLITE_IN_QT_PREFIX:BOOL=ON \ - -DWITH_SOPRANO:BOOL=%{?soprano:ON}%{!?soprano:OFF} \ - .. + -DWITH_SOPRANO:BOOL=%{?soprano:ON}%{!?soprano:OFF} popd make %{?_smp_mflags} -C %{_target_platform} @@ -234,6 +234,9 @@ fi %changelog +* Thu May 07 2015 Rex Dieter 1.13.0-14 +- %%build: explicitly set -DCMAKE_BUILD_TYPE="Release" (-DQT_NO_DEBUG was used already) + * Mon Apr 27 2015 Daniel Vrátil - 1.13.0-13 - Rebuild for boost From 8942b654a72d710ee3346a47543e3a7441324cb1 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Tue, 16 Jun 2015 23:44:25 +0000 Subject: [PATCH 36/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index f931d85..06b50fe 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 14%{?dist} +Release: 15%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -234,6 +234,9 @@ fi %changelog +* Tue Jun 16 2015 Fedora Release Engineering - 1.13.0-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + * Thu May 07 2015 Rex Dieter 1.13.0-14 - %%build: explicitly set -DCMAKE_BUILD_TYPE="Release" (-DQT_NO_DEBUG was used already) From 62a3cba7d77670388fc66bcbc5535eb219186bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 29 Jun 2015 23:03:19 +0200 Subject: [PATCH 37/78] Pull upstream fix for KDE#341884 --- 0031-Don-t-leak-external-payload-files.patch | 134 +++++++++++++++++++ akonadi.spec | 6 +- 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 0031-Don-t-leak-external-payload-files.patch diff --git a/0031-Don-t-leak-external-payload-files.patch b/0031-Don-t-leak-external-payload-files.patch new file mode 100644 index 0000000..e62fea7 --- /dev/null +++ b/0031-Don-t-leak-external-payload-files.patch @@ -0,0 +1,134 @@ +commit 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 +Author: Dan Vrátil +Date: Mon Jun 29 22:45:11 2015 +0200 + + 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 + +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("expectedPartSize"); + QTest::addColumn("expectedChanged"); + QTest::addColumn("isExternal"); ++ QTest::addColumn("version"); + QTest::addColumn("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 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(); + 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)); diff --git a/akonadi.spec b/akonadi.spec index 06b50fe..0db6a21 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 15%{?dist} +Release: 16%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -68,6 +68,7 @@ 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 +Patch31: 0031-Don-t-leak-external-payload-files.patch %define mysql_conf_timestamp 20140709 @@ -234,6 +235,9 @@ fi %changelog +* Mon Jun 29 2015 Daniel Vrátil - 1.13.0-16 +- pull upstream fix for KDE#341884 + * Tue Jun 16 2015 Fedora Release Engineering - 1.13.0-15 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild From eb7406d9324ef8c7b173df8cab1ab635139fec5e Mon Sep 17 00:00:00 2001 From: David Tardon Date: Wed, 22 Jul 2015 18:03:59 +0200 Subject: [PATCH 38/78] rebuild for Boost 1.58 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 0db6a21..a7d3478 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 16%{?dist} +Release: 17%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -235,6 +235,9 @@ fi %changelog +* Wed Jul 22 2015 David Tardon - 1.13.0-17 +- rebuild for Boost 1.58 + * Mon Jun 29 2015 Daniel Vrátil - 1.13.0-16 - pull upstream fix for KDE#341884 From 6505b31d4490f56c48b03d44c6f26858cb4c98bc Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 29 Jul 2015 11:57:10 -0500 Subject: [PATCH 39/78] - Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index a7d3478..cc275ac 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 17%{?dist} +Release: 18%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -235,6 +235,9 @@ fi %changelog +* Wed Jul 29 2015 Fedora Release Engineering - 1.13.0-18 +- Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159 + * Wed Jul 22 2015 David Tardon - 1.13.0-17 - rebuild for Boost 1.58 From 8b4426d5d343a4ca3df1fd0baa934c1bddb408f3 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sun, 2 Aug 2015 13:38:23 -0500 Subject: [PATCH 40/78] pull in latest 1.13 branch fixes --- 0031-Less-C-11-fixes-build-with-clang.patch | 52 ++++++++++++++++ ...ption-when-MOVE-handler-finds-no-ite.patch | 60 +++++++++++++++++++ ...on-t-leak-old-external-payload-files.patch | 44 ++++++++------ ...q-to-match-kdelibs4-and-enable-newer.patch | 25 ++++++++ akonadi.spec | 12 +++- 5 files changed, 171 insertions(+), 22 deletions(-) create mode 100644 0031-Less-C-11-fixes-build-with-clang.patch create mode 100644 0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch rename 0031-Don-t-leak-external-payload-files.patch => 0033-Don-t-leak-old-external-payload-files.patch (85%) create mode 100644 0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch diff --git a/0031-Less-C-11-fixes-build-with-clang.patch b/0031-Less-C-11-fixes-build-with-clang.patch new file mode 100644 index 0000000..f915ce2 --- /dev/null +++ b/0031-Less-C-11-fixes-build-with-clang.patch @@ -0,0 +1,52 @@ +From c23607679fa1451f0c6890bd4a5656c07d519853 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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("mimetypes"); + QTest::addColumn>("expectedResults"); + +- QTest::newRow("") << QVector({ 0 }) +- << QStringList({ QLatin1String("text/plain") }) +- << QVector({ col4.id(), col5.id(), col7.id() }); +- QTest::newRow("") << QVector({ 0 }) +- << QStringList({ QLatin1String("application/octet-stream") }) +- << QVector({ col2.id(), col3.id(), col6.id(), col8.id() }); +- QTest::newRow("") << QVector({ col1.id() }) +- << QStringList({ QLatin1String("text/plain") }) +- << QVector({ col4.id() }); +- QTest::newRow("") << QVector({ col1.id() }) +- << QStringList({ QLatin1String("unique/mime-type") }) ++ QTest::newRow("") << (QVector() << 0) ++ << (QStringList() << QLatin1String("text/plain")) ++ << (QVector() << col4.id() << col5.id() << col7.id()); ++ QTest::newRow("") << (QVector() << 0) ++ << (QStringList() << QLatin1String("application/octet-stream")) ++ << (QVector() << col2.id() << col3.id() << col6.id() << col8.id()); ++ QTest::newRow("") << (QVector() << col1.id()) ++ << (QStringList() << QLatin1String("text/plain")) ++ << (QVector() << col4.id()); ++ QTest::newRow("") << (QVector() << col1.id()) ++ << (QStringList() << QLatin1String("unique/mime-type")) + << QVector(); +- QTest::newRow("") << QVector({ col2.id(), col7.id() }) +- << QStringList({ QLatin1String("application/octet-stream") }) +- << QVector({ col3.id(), col8.id() }); ++ QTest::newRow("") << (QVector() << col2.id() << col7.id()) ++ << (QStringList() << QLatin1String("application/octet-stream")) ++ << (QVector() << col3.id() << col8.id()); + } + + void testSearchHelperCollectionListing() +-- +2.4.3 + diff --git a/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch b/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch new file mode 100644 index 0000000..62a7637 --- /dev/null +++ b/0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch @@ -0,0 +1,60 @@ +From abe71f46c3b2e657db25ac16c43a4c76b2212a9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +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 items = qb.result(); + if ( items.isEmpty() ) { +- throw HandlerException( "No items found" ); ++ return successResponse( "MOVE complete" ); + } + + // Split the list by source collection +-- +2.4.3 + diff --git a/0031-Don-t-leak-external-payload-files.patch b/0033-Don-t-leak-old-external-payload-files.patch similarity index 85% rename from 0031-Don-t-leak-external-payload-files.patch rename to 0033-Don-t-leak-old-external-payload-files.patch index e62fea7..2325de0 100644 --- a/0031-Don-t-leak-external-payload-files.patch +++ b/0033-Don-t-leak-old-external-payload-files.patch @@ -1,23 +1,26 @@ -commit 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 -Author: Dan Vrátil -Date: Mon Jun 29 22:45:11 2015 +0200 +From 9c0dc6b3f0826d32eac310b2e7ecd858ca3df681 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Vr=C3=A1til?= +Date: Mon, 29 Jun 2015 22:45:11 +0200 +Subject: [PATCH 33/34] Don't leak old external payload files - 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 +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 @@ -132,3 +135,6 @@ index 05e3a8a..669bbbc 100644 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 + diff --git a/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch b/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch new file mode 100644 index 0000000..9d6411f --- /dev/null +++ b/0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch @@ -0,0 +1,25 @@ +From 18ed37d89b8185ac15a8bfe245de8a88d17f2c64 Mon Sep 17 00:00:00 2001 +From: David Faure +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 + diff --git a/akonadi.spec b/akonadi.spec index cc275ac..11760eb 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 18%{?dist} +Release: 19%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -37,7 +37,7 @@ Source10: akonadiserverrc.mysql ## upstreamable patches -## upstream patches +## 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 @@ -68,7 +68,10 @@ 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 -Patch31: 0031-Don-t-leak-external-payload-files.patch +Patch31: 0031-Less-C-11-fixes-build-with-clang.patch +Patch32: 0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch +Patch33: 0033-Don-t-leak-old-external-payload-files.patch +Patch34: 0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch %define mysql_conf_timestamp 20140709 @@ -235,6 +238,9 @@ fi %changelog +* Fri Jul 31 2015 Rex Dieter 1.13.0-19 +- pull in latest 1.13 branch fixes + * Wed Jul 29 2015 Fedora Release Engineering - 1.13.0-18 - Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159 From 258eaca3ec4b59c716c94a278e389bd81c680481 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 5 Aug 2015 19:35:04 +0100 Subject: [PATCH 41/78] Rebuilt for Boost 1.58 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 11760eb..b17b48c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 19%{?dist} +Release: 20%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -238,6 +238,9 @@ fi %changelog +* Wed Aug 05 2015 Jonathan Wakely 1.13.0-20 +- Rebuilt for Boost 1.58 + * Fri Jul 31 2015 Rex Dieter 1.13.0-19 - pull in latest 1.13 branch fixes From 36b47e46bece0006d7b11ac85463aed17851d5fe Mon Sep 17 00:00:00 2001 From: Than Ngo Date: Thu, 20 Aug 2015 22:28:40 +0200 Subject: [PATCH 42/78] 15.08.0 --- .gitignore | 1 + akonadi.spec | 7 +++++-- sources | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d95e0b8..0eca4e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /akonadi-1.13.0.tar.bz2 +/akonadi-15.08.0.tar.xz diff --git a/akonadi.spec b/akonadi.spec index b17b48c..0b08f74 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,8 +18,8 @@ Summary: PIM Storage Service Name: akonadi -Version: 1.13.0 -Release: 20%{?dist} +Version: 15.08.0 +Release: 1%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -238,6 +238,9 @@ fi %changelog +* Thu Aug 20 2015 Than Ngo - 15.08.0-1 +- 15.08.0 + * Wed Aug 05 2015 Jonathan Wakely 1.13.0-20 - Rebuilt for Boost 1.58 diff --git a/sources b/sources index 4957149..0bdc4f0 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -84eb2e471bd6bdfe54a2a2f1d858c07d akonadi-1.13.0.tar.bz2 +e6d025189fc54594c84ca52f2a478b62 akonadi-15.08.0.tar.xz From 7e082edf724bd7486e3043bec5adffe8e9b79afb Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Sun, 30 Aug 2015 11:30:12 -0500 Subject: [PATCH 43/78] Revert "15.08.0" This reverts commit 36b47e46bece0006d7b11ac85463aed17851d5fe. moving to qt5 pim stack will be a lot more work, not ready till at least sometime next week. --- .gitignore | 1 - akonadi.spec | 7 ++----- sources | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 0eca4e7..d95e0b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /akonadi-1.13.0.tar.bz2 -/akonadi-15.08.0.tar.xz diff --git a/akonadi.spec b/akonadi.spec index 0b08f74..b17b48c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -18,8 +18,8 @@ Summary: PIM Storage Service Name: akonadi -Version: 15.08.0 -Release: 1%{?dist} +Version: 1.13.0 +Release: 20%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -238,9 +238,6 @@ fi %changelog -* Thu Aug 20 2015 Than Ngo - 15.08.0-1 -- 15.08.0 - * Wed Aug 05 2015 Jonathan Wakely 1.13.0-20 - Rebuilt for Boost 1.58 diff --git a/sources b/sources index 0bdc4f0..4957149 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -e6d025189fc54594c84ca52f2a478b62 akonadi-15.08.0.tar.xz +84eb2e471bd6bdfe54a2a2f1d858c07d akonadi-1.13.0.tar.bz2 From b97ca23711ba24a0fad57fe14f61e87910984a2d Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 27 Aug 2015 21:17:20 +0100 Subject: [PATCH 44/78] Rebuilt for Boost 1.59 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index b17b48c..404740f 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 20%{?dist} +Release: 21%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -238,6 +238,9 @@ fi %changelog +* Sun Aug 30 2015 Jonathan Wakely 1.13.0-21 +- Rebuilt for Boost 1.59 + * Wed Aug 05 2015 Jonathan Wakely 1.13.0-20 - Rebuilt for Boost 1.58 From 21ec943481459517733ef2cb693f934edb494ebb Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 12 Nov 2015 10:29:52 -0600 Subject: [PATCH 45/78] Recommends: akonadi-mysql --- akonadi.spec | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 404740f..0e597b8 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -19,7 +19,7 @@ Summary: PIM Storage Service Name: akonadi Version: 1.13.0 -Release: 21%{?dist} +Release: 22%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -97,6 +97,10 @@ BuildRequires: postgresql-server Requires(postun): /sbin/ldconfig +%if 0%{?fedora} > 21 +Recommends: %{name}-mysql = %{version}-%{release} +%endif + %description %{summary}. @@ -238,6 +242,9 @@ fi %changelog +* Thu Nov 12 2015 Rex Dieter 1.13.0-22 +- Recommends: akonadi-mysql + * Sun Aug 30 2015 Jonathan Wakely 1.13.0-21 - Rebuilt for Boost 1.59 From 04ca9f646e051af28fd8079aeab222231e8299aa Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Fri, 11 Dec 2015 08:38:19 -0600 Subject: [PATCH 46/78] for kf5 kdepim world, build libakonadi bits only (omit server and related files) --- akonadi-1.13.0-libs_only.patch | 33 +++++++ akonadi.spec | 152 +++------------------------------ 2 files changed, 45 insertions(+), 140 deletions(-) create mode 100644 akonadi-1.13.0-libs_only.patch diff --git a/akonadi-1.13.0-libs_only.patch b/akonadi-1.13.0-libs_only.patch new file mode 100644 index 0000000..73da4ca --- /dev/null +++ b/akonadi-1.13.0-libs_only.patch @@ -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() + diff --git a/akonadi.spec b/akonadi.spec index 0e597b8..9408bae 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -5,35 +5,17 @@ # trim changelog included in binary rpms %global _changelog_trimtime %(date +%s -d "1 year ago") -# legacy nepomuk/soprano support (ie, kde < 4.13) -%if 0%{?fedora} < 20 -%define soprano 1 -%endif - -%global mysql mysql -%if 0%{?rhel} > 6 -# el7 mariadb pkgs don't have compat Provides: mysql (apparently?) -%global mysql mariadb -%endif - -Summary: PIM Storage Service +Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 22%{?dist} +Release: 100%{?dist} License: LGPLv2+ 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}.tar.bz2 -%endif -## mysql config -Source10: akonadiserverrc.mysql +## downstream patches +Patch100: akonadi-1.13.0-libs_only.patch ## upstreamable patches @@ -68,12 +50,6 @@ 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 -Patch31: 0031-Less-C-11-fixes-build-with-clang.patch -Patch32: 0032-Don-t-throw-exception-when-MOVE-handler-finds-no-ite.patch -Patch33: 0033-Don-t-leak-old-external-payload-files.patch -Patch34: 0034-set-cmake_min_req-to-match-kdelibs4-and-enable-newer.patch - -%define mysql_conf_timestamp 20140709 BuildRequires: automoc4 BuildRequires: boost-devel @@ -82,24 +58,6 @@ BuildRequires: cmake >= 2.8.8 BuildRequires: libxslt BuildRequires: pkgconfig(QtDBus) pkgconfig(QtSql) pkgconfig(QtXml) BuildRequires: pkgconfig(shared-mime-info) -%if 0%{?soprano} -BuildRequires: pkgconfig(soprano) -%endif -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: mariadb-server -BuildRequires: postgresql-server - -%{?_qt4_version:Requires: qt4%{?_isa} >= %{_qt4_version}} - -Requires(postun): /sbin/ldconfig - -%if 0%{?fedora} > 21 -Recommends: %{name}-mysql = %{version}-%{release} -%endif %description %{summary}. @@ -110,27 +68,6 @@ 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 -%if "%{?mysql}" != "mariadb" && 0%{?fedora} > 20 -Recommends: mariadb-server -%endif -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 %autosetup -p1 -n akonadi-%{version} @@ -140,11 +77,7 @@ See also: %{_sysconfdir}/akonadi/mysql-global.conf mkdir %{_target_platform} pushd %{_target_platform} %{cmake} .. \ - -DCMAKE_BUILD_TYPE:STRING="Release" \ - -DCONFIG_INSTALL_DIR=%{_sysconfdir} \ - %{?database_backend:-DDATABASE_BACKEND=%{database_backend}} \ - -DINSTALL_QSQLITE_IN_QT_PREFIX:BOOL=ON \ - -DWITH_SOPRANO:BOOL=%{?soprano:ON}%{!?soprano:OFF} + -DCMAKE_BUILD_TYPE:STRING="Release" popd make %{?_smp_mflags} -C %{_target_platform} @@ -153,95 +86,34 @@ make %{?_smp_mflags} -C %{_target_platform} %install make install/fast DESTDIR=$RPM_BUILD_ROOT -C %{_target_platform} -install -p -m644 -D %{SOURCE10} %{buildroot}%{_sysconfdir}/xdg/akonadi/akonadiserverrc.mysql - -mkdir -p %{buildroot}%{_datadir}/akonadi/agents - -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 -# omit mysql-global-mobile.conf -rm -fv %{buildroot}%{_sysconfdir}/akonadi/mysql-global-mobile.conf +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} ||: -%post -/sbin/ldconfig -touch --no-create %{_datadir}/mime/packages &> /dev/null || : - -%posttrans -update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null || : - -%postun -/sbin/ldconfig ||: -if [ $1 -eq 0 ] ; then - touch --no-create %{_datadir}/mime/packages &> /dev/null || : - update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null ||: -fi +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig %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/ +%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 - %changelog +* Fri Dec 11 2015 Rex Dieter 1.13.0-100 +- for kf5 kdepim world, build libakonadi bits only (omit server and related files) + * Thu Nov 12 2015 Rex Dieter 1.13.0-22 - Recommends: akonadi-mysql From 80bf6d98ef25288058618e07429fe5d3be56955a Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Thu, 17 Dec 2015 13:45:30 -0600 Subject: [PATCH 47/78] devel: re-enable dbus-1/interfaces, Conflicts: kf5-akonadi-server-devel --- akonadi-1.13.0-libs_only.patch | 2 +- akonadi.spec | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/akonadi-1.13.0-libs_only.patch b/akonadi-1.13.0-libs_only.patch index 73da4ca..96f48cd 100644 --- a/akonadi-1.13.0-libs_only.patch +++ b/akonadi-1.13.0-libs_only.patch @@ -6,7 +6,7 @@ diff -up akonadi-1.13.0/CMakeLists.txt.opt akonadi-1.13.0/CMakeLists.txt include_directories(${Akonadi_SOURCE_DIR} ${Akonadi_BINARY_DIR} ${QT_INCLUDES} ${Boost_INCLUDE_DIR}) -add_subdirectory(interfaces) -+#add_subdirectory(interfaces) ++add_subdirectory(interfaces) add_subdirectory(libs) set(AKONADI_PROTOCOLINTERNALS_LIBS ${akonadiprotocolinternals_LIB_DEPENDS} akonadiprotocolinternals) diff --git a/akonadi.spec b/akonadi.spec index 9408bae..7ebe0a5 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 100%{?dist} +Release: 101%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -64,6 +64,7 @@ BuildRequires: pkgconfig(shared-mime-info) %package devel Summary: Developer files for %{name} +Conflicts: kf5-akonadi-server-devel Requires: %{name}%{?_isa} = %{version}-%{release} %description devel %{summary}. @@ -108,9 +109,13 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %{_libdir}/pkgconfig/akonadi.pc %{_libdir}/libakonadiprotocolinternals.so %{_libdir}/cmake/Akonadi/ +%{_datadir}/dbus-1/interfaces/org.freedesktop.Akonadi.*.xml %changelog +* Thu Dec 17 2015 Rex Dieter 1.13.0-101 +- -devel: re-enable dbus-1/interfaces, Conflicts: kf5-akonadi-server-devel + * Fri Dec 11 2015 Rex Dieter 1.13.0-100 - for kf5 kdepim world, build libakonadi bits only (omit server and related files) From 2981736d0263f66ecd211c92b9d5c790f08b5f30 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 3 Feb 2016 16:04:06 +0000 Subject: [PATCH 48/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 7ebe0a5..0d179fa 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 101%{?dist} +Release: 102%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Feb 03 2016 Fedora Release Engineering - 1.13.0-102 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + * Thu Dec 17 2015 Rex Dieter 1.13.0-101 - -devel: re-enable dbus-1/interfaces, Conflicts: kf5-akonadi-server-devel From cba6ce5ec119728df38e76968c4fe1e118bf3296 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 10 Feb 2017 05:50:28 +0000 Subject: [PATCH 49/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 0d179fa..eb05055 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 102%{?dist} +Release: 103%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Fri Feb 10 2017 Fedora Release Engineering - 1.13.0-103 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + * Wed Feb 03 2016 Fedora Release Engineering - 1.13.0-102 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild From 13aaf3464f08dc5484728848af60dfdc09224c1f Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 18 Jul 2017 12:13:23 +0100 Subject: [PATCH 50/78] Rebuilt for Boost 1.64 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index eb05055..5f4e542 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 103%{?dist} +Release: 104%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Tue Jul 18 2017 Jonathan Wakely - 1.13.0-104 +- Rebuilt for Boost 1.64 + * Fri Feb 10 2017 Fedora Release Engineering - 1.13.0-103 - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild From b8f85d80a12f2f501283dd2eb6a8e7b91781461b Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 26 Jul 2017 02:42:05 +0000 Subject: [PATCH 51/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 5f4e542..256955c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 104%{?dist} +Release: 105%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 26 2017 Fedora Release Engineering - 1.13.0-105 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + * Tue Jul 18 2017 Jonathan Wakely - 1.13.0-104 - Rebuilt for Boost 1.64 From 80b6168b8d18acdff37d9883c04a109a71f1d732 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 2 Aug 2017 17:28:23 +0000 Subject: [PATCH 52/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 256955c..0ea3ae6 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 105%{?dist} +Release: 106%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Aug 02 2017 Fedora Release Engineering - 1.13.0-106 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + * Wed Jul 26 2017 Fedora Release Engineering - 1.13.0-105 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild From 99916b63be63faa85a07886010532eb03fa149bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sun, 6 Aug 2017 11:28:59 +0200 Subject: [PATCH 53/78] Rebuilt for AutoReq cmake-filesystem --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 0ea3ae6..46a6d20 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 106%{?dist} +Release: 107%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Sun Aug 06 2017 Björn Esser - 1.13.0-107 +- Rebuilt for AutoReq cmake-filesystem + * Wed Aug 02 2017 Fedora Release Engineering - 1.13.0-106 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild From e0386f5e78a2d8341ec3b1a10ef5d75f9ce9a5df Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 23 Jan 2018 12:01:43 +0000 Subject: [PATCH 54/78] Rebuilt for Boost 1.66 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 46a6d20..126fa2a 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 107%{?dist} +Release: 108%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Tue Jan 23 2018 Jonathan Wakely - 1.13.0-108 +- Rebuilt for Boost 1.66 + * Sun Aug 06 2017 Björn Esser - 1.13.0-107 - Rebuilt for AutoReq cmake-filesystem From 070820a23d2d0392b3824df71c4169e5431c0dae Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 7 Feb 2018 02:02:35 +0000 Subject: [PATCH 55/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 126fa2a..019909b 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 108%{?dist} +Release: 109%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Feb 07 2018 Fedora Release Engineering - 1.13.0-109 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + * Tue Jan 23 2018 Jonathan Wakely - 1.13.0-108 - Rebuilt for Boost 1.66 From c183a6e34552f72fd4d4c5265a7e6b95e1059c84 Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Tue, 20 Feb 2018 15:47:13 -0600 Subject: [PATCH 56/78] BR: gcc-c++, use %ldconfig_scriptlets --- akonadi.spec | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 019909b..e789158 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 109%{?dist} +Release: 110%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -54,6 +54,7 @@ Patch30: 0030-Preallocate-a-capacity-of-16-for-the-returned-list.patch BuildRequires: automoc4 BuildRequires: boost-devel BuildRequires: cmake >= 2.8.8 +BuildRequires: gcc-c++ # for xsltproc BuildRequires: libxslt BuildRequires: pkgconfig(QtDBus) pkgconfig(QtSql) pkgconfig(QtXml) @@ -96,11 +97,10 @@ export PKG_CONFIG_PATH=%{buildroot}%{_datadir}/pkgconfig:%{buildroot}%{_libdir}/ test "$(pkg-config --modversion akonadi)" = "%{version}" -%post -p /sbin/ldconfig -%postun -p /sbin/ldconfig +%ldconfig_scriptlets %files -%doc AUTHORS lgpl-license +%doc AUTHORS %license lgpl-license %{_libdir}/libakonadiprotocolinternals.so.1* @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Tue Feb 20 2018 Rex Dieter - 1.13.0-110 +- BR: gcc-c++, use %%ldconfig_scriptlets + * Wed Feb 07 2018 Fedora Release Engineering - 1.13.0-109 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild From 442c71f0ec47b0d295706bfbe483d913a0cfa99d Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 12 Jul 2018 20:05:38 +0000 Subject: [PATCH 57/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index e789158..7f807d3 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 110%{?dist} +Release: 111%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Thu Jul 12 2018 Fedora Release Engineering - 1.13.0-111 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + * Tue Feb 20 2018 Rex Dieter - 1.13.0-110 - BR: gcc-c++, use %%ldconfig_scriptlets From e63e99f1ef9ca0fabc57efc93447612234e2f99f Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 25 Jan 2019 08:13:03 +0000 Subject: [PATCH 58/78] Rebuilt for Boost 1.69 --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 7f807d3..8bc283f 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 111%{?dist} +Release: 112%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Fri Jan 25 2019 Jonathan Wakely - 1.13.0-112 +- Rebuilt for Boost 1.69 + * Thu Jul 12 2018 Fedora Release Engineering - 1.13.0-111 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild From 5f516d08a433f182c0d215b6c514e94f5ec763f5 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 31 Jan 2019 13:07:51 +0000 Subject: [PATCH 59/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 8bc283f..04deed4 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 112%{?dist} +Release: 113%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Thu Jan 31 2019 Fedora Release Engineering - 1.13.0-113 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + * Fri Jan 25 2019 Jonathan Wakely - 1.13.0-112 - Rebuilt for Boost 1.69 From 5a7644bcb23f2f6aa3b25f108177d139d22c8a6e Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 24 Jul 2019 17:42:27 +0000 Subject: [PATCH 60/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 04deed4..d1da2cb 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 113%{?dist} +Release: 114%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 24 2019 Fedora Release Engineering - 1.13.0-114 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + * Thu Jan 31 2019 Fedora Release Engineering - 1.13.0-113 - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild From d4cb7121285ecb5a38c75de9dfa3aa1fe19d4ac7 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Tue, 28 Jan 2020 11:21:56 +0000 Subject: [PATCH 61/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index d1da2cb..5510807 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -8,7 +8,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 114%{?dist} +Release: 115%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -113,6 +113,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Tue Jan 28 2020 Fedora Release Engineering - 1.13.0-115 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + * Wed Jul 24 2019 Fedora Release Engineering - 1.13.0-114 - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild From 71184c15cba18fff766016906b091ca622bd0377 Mon Sep 17 00:00:00 2001 From: Neal Gompa Date: Sun, 5 Jul 2020 14:46:26 -0400 Subject: [PATCH 62/78] Update to new out-of-source build mechanism --- akonadi.spec | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index 5510807..317756e 100644 --- a/akonadi.spec +++ b/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 @@ -76,17 +78,12 @@ Requires: %{name}%{?_isa} = %{version}-%{release} %build -mkdir %{_target_platform} -pushd %{_target_platform} -%{cmake} .. \ - -DCMAKE_BUILD_TYPE:STRING="Release" -popd - -make %{?_smp_mflags} -C %{_target_platform} +%cmake -DCMAKE_BUILD_TYPE:STRING="Release" +%cmake_build %install -make install/fast DESTDIR=$RPM_BUILD_ROOT -C %{_target_platform} +%cmake_install ## unpackaged files rm -fv %{buildroot}%{_datadir}/mime/packages/akonadi-mime.xml From b53992e7f46241bde8cfc4e6e3accfa41039fd0f Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 27 Jul 2020 11:52:34 +0000 Subject: [PATCH 63/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 317756e..4012d2a 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 115%{?dist} +Release: 116%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Mon Jul 27 2020 Fedora Release Engineering - 1.13.0-116 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + * Tue Jan 28 2020 Fedora Release Engineering - 1.13.0-115 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild From e0c683030d8f8687f2f61749be4df85a3d825903 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 31 Jul 2020 23:53:20 +0000 Subject: [PATCH 64/78] - Second attempt - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 4012d2a..95a5d90 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 116%{?dist} +Release: 117%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,10 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Fri Jul 31 2020 Fedora Release Engineering - 1.13.0-117 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + * Mon Jul 27 2020 Fedora Release Engineering - 1.13.0-116 - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild From 1d79ef60cff02fd007dd51be398d5392ae1f6a0d Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 25 Jan 2021 23:56:12 +0000 Subject: [PATCH 65/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 95a5d90..065ee0e 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 117%{?dist} +Release: 118%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Mon Jan 25 2021 Fedora Release Engineering - 1.13.0-118 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + * Fri Jul 31 2020 Fedora Release Engineering - 1.13.0-117 - Second attempt - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild From 1c611726ac91c60466f96ddc5e456fcd47baab38 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 21 Jul 2021 12:28:20 +0000 Subject: [PATCH 66/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild Signed-off-by: Fedora Release Engineering From e513ef09a921c9b584d88592dde653faf9790375 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 21 Jul 2021 17:23:36 +0000 Subject: [PATCH 67/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 065ee0e..a63bcd2 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 118%{?dist} +Release: 119%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 21 2021 Fedora Release Engineering - 1.13.0-119 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + * Mon Jan 25 2021 Fedora Release Engineering - 1.13.0-118 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild From 24bd5578bf448a9ba65dee8e70909394a8a38d14 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 19 Jan 2022 21:04:02 +0000 Subject: [PATCH 68/78] - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index a63bcd2..ab84ad3 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 119%{?dist} +Release: 120%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jan 19 2022 Fedora Release Engineering - 1.13.0-120 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + * Wed Jul 21 2021 Fedora Release Engineering - 1.13.0-119 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild From bad5a7962ce1a802e3288b8a7c946301b57e4010 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 20 Jul 2022 20:35:13 +0000 Subject: [PATCH 69/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index ab84ad3..e3f2eb2 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 120%{?dist} +Release: 121%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 20 2022 Fedora Release Engineering - 1.13.0-121 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + * Wed Jan 19 2022 Fedora Release Engineering - 1.13.0-120 - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild From 3e10cdbabedc3116e881d6932d85bdd694a61c2b Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 18 Jan 2023 21:32:30 +0000 Subject: [PATCH 70/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index e3f2eb2..c225638 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 121%{?dist} +Release: 122%{?dist} License: LGPLv2+ URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jan 18 2023 Fedora Release Engineering - 1.13.0-122 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + * Wed Jul 20 2022 Fedora Release Engineering - 1.13.0-121 - Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild From 8e0da1155dc1bf1eef87ac21e567f12cef57e4c5 Mon Sep 17 00:00:00 2001 From: Than Ngo Date: Mon, 12 Jun 2023 12:12:13 +0200 Subject: [PATCH 71/78] migrated to SPDX license --- akonadi.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/akonadi.spec b/akonadi.spec index c225638..44b0477 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,9 +10,9 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 122%{?dist} +Release: 123%{?dist} -License: LGPLv2+ +License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi Source0: http://download.kde.org/stable/akonadi/src/akonadi-%{version}.tar.bz2 @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Mon Jun 12 2023 Than Ngo - 1.13.0-123 +- migrated to SPDX license + * Wed Jan 18 2023 Fedora Release Engineering - 1.13.0-122 - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild From aefd5236d85058d0c5f50ac09884c25c79f99574 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 19 Jul 2023 13:03:56 +0000 Subject: [PATCH 72/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 44b0477..4cb1f58 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 123%{?dist} +Release: 124%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 19 2023 Fedora Release Engineering - 1.13.0-124 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + * Mon Jun 12 2023 Than Ngo - 1.13.0-123 - migrated to SPDX license From 2bcdc9e18b150a85e8d32f00f177e3646aa45c54 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 19 Jan 2024 12:29:49 +0000 Subject: [PATCH 73/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 4cb1f58..e6915fe 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 124%{?dist} +Release: 125%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Fri Jan 19 2024 Fedora Release Engineering - 1.13.0-125 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Wed Jul 19 2023 Fedora Release Engineering - 1.13.0-124 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild From 547cef98b921b2238c917ae87317e88e054cba3f Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 22 Jan 2024 22:49:46 +0000 Subject: [PATCH 74/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index e6915fe..48bb7d9 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 125%{?dist} +Release: 126%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Mon Jan 22 2024 Fedora Release Engineering - 1.13.0-126 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Fri Jan 19 2024 Fedora Release Engineering - 1.13.0-125 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From b94e5a0e93b21bc80518518816ee68fb871c9bb4 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 17 Jul 2024 16:46:09 +0000 Subject: [PATCH 75/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 48bb7d9..bdf118c 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 126%{?dist} +Release: 127%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 17 2024 Fedora Release Engineering - 1.13.0-127 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + * Mon Jan 22 2024 Fedora Release Engineering - 1.13.0-126 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From d5433cf6e85683b6145f35ba3cdc54a782b9b3dd Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 16 Jan 2025 10:46:32 +0000 Subject: [PATCH 76/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index bdf118c..0a8daa7 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 127%{?dist} +Release: 128%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Thu Jan 16 2025 Fedora Release Engineering - 1.13.0-128 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + * Wed Jul 17 2024 Fedora Release Engineering - 1.13.0-127 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild From 04d7652051e4579f9fa8817654dddec620393c21 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 23 Jul 2025 16:51:21 +0000 Subject: [PATCH 77/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 0a8daa7..79e70d7 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 128%{?dist} +Release: 129%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Wed Jul 23 2025 Fedora Release Engineering - 1.13.0-129 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + * Thu Jan 16 2025 Fedora Release Engineering - 1.13.0-128 - Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From f93f842792304170af34c29100d24a75b29570db Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 16 Jan 2026 03:32:46 +0000 Subject: [PATCH 78/78] Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild --- akonadi.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akonadi.spec b/akonadi.spec index 79e70d7..e616592 100644 --- a/akonadi.spec +++ b/akonadi.spec @@ -10,7 +10,7 @@ Summary: PIM Storage Service Libraries Name: akonadi Version: 1.13.0 -Release: 129%{?dist} +Release: 130%{?dist} License: LGPL-2.0-or-later URL: http://community.kde.org/KDE_PIM/Akonadi @@ -110,6 +110,9 @@ test "$(pkg-config --modversion akonadi)" = "%{version}" %changelog +* Fri Jan 16 2026 Fedora Release Engineering - 1.13.0-130 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + * Wed Jul 23 2025 Fedora Release Engineering - 1.13.0-129 - Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild