Rex Dieter adf30af
From 63f49d233ca8a4fdd3e8937ea1c80d5e57a1cbdc Mon Sep 17 00:00:00 2001
Rex Dieter adf30af
From: Milian Wolff <mail@milianw.de>
Rex Dieter adf30af
Date: Tue, 25 Nov 2014 20:16:41 +0100
Rex Dieter adf30af
Subject: [PATCH 12/30] Optimize: Reduce the amount of allocations required to
Rex Dieter adf30af
 build a query.
Rex Dieter adf30af
Rex Dieter adf30af
The initial implementation of the QueryBuilder was quite naive, when
Rex Dieter adf30af
you look at the amount of string allocations it does to build the
Rex Dieter adf30af
final query we sent to the SQL server.
Rex Dieter adf30af
Rex Dieter adf30af
This was found with Linux perf (no, not even heaptrack!). It
Rex Dieter adf30af
showed a huge number of cycles spent in malloc/free, all called
Rex Dieter adf30af
eventually by the QueryBuilder.
Rex Dieter adf30af
Rex Dieter adf30af
This patch removes most of these allocations. It can further be
Rex Dieter adf30af
improved in the future, I bet. Also, the amount of queries we create
Rex Dieter adf30af
is pretty large. I guess using stored procedures or something similar
Rex Dieter adf30af
might also help the performance. At least, we should try to "remember"
Rex Dieter adf30af
some of our queries, and make it possible to reuse them in the
Rex Dieter adf30af
functions that run often.
Rex Dieter adf30af
Rex Dieter adf30af
The added benchmark shows that the cost is not as big as I'd initially
Rex Dieter adf30af
assumed. There are simply many more allocation occurrences in Akonadi
Rex Dieter adf30af
currently. Still, I think it's worth it, as it also decreases the
Rex Dieter adf30af
memory fragmentation and improves cache locality:
Rex Dieter adf30af
Rex Dieter adf30af
Before:
Rex Dieter adf30af
RESULT : QueryBuilderTest::benchQueryBuilder():
Rex Dieter adf30af
     0.0115 msecs per iteration (total: 116, iterations: 10000)
Rex Dieter adf30af
Rex Dieter adf30af
113.10MB bytes allocated in total (ignoring deallocations)
Rex Dieter adf30af
over 1203089 calls to allocation functions.
Rex Dieter adf30af
peak heap memory consumption: 254.46KB
Rex Dieter adf30af
Rex Dieter adf30af
After:
Rex Dieter adf30af
RESULT : QueryBuilderTest::benchQueryBuilder():
Rex Dieter adf30af
     0.0065 msecs per iteration (total: 66, iterations: 10000)
Rex Dieter adf30af
Rex Dieter adf30af
62.42MB bytes allocated in total (ignoring deallocations)
Rex Dieter adf30af
over 343089 calls to allocation functions.
Rex Dieter adf30af
peak heap memory consumption: 254.96KB
Rex Dieter adf30af
Rex Dieter adf30af
So before, we had approx. 60 allocations per query build in the
Rex Dieter adf30af
benchmark (note that Qt for some reason executes the loop twice,
Rex Dieter adf30af
so while the time is measured for 10k iterations, heaptrack will
Rex Dieter adf30af
see 20k). With this patch applied, we only need ~20 allocations
Rex Dieter adf30af
per query we build up.
Rex Dieter adf30af
Rex Dieter adf30af
The remaining allocations are the various append operations to
Rex Dieter adf30af
the QList/QVectors mostly, as well as QueryBuilder::addAggregation.
Rex Dieter adf30af
Rex Dieter adf30af
REVIEW: 121247
Rex Dieter adf30af
---
Rex Dieter adf30af
 server/src/storage/querybuilder.cpp        | 210 ++++++++++++++++-------------
Rex Dieter adf30af
 server/src/storage/querybuilder.h          |  14 +-
Rex Dieter adf30af
 server/tests/unittest/querybuildertest.cpp |  58 ++++++--
Rex Dieter adf30af
 server/tests/unittest/querybuildertest.h   |   2 +
Rex Dieter adf30af
 4 files changed, 173 insertions(+), 111 deletions(-)
Rex Dieter adf30af
Rex Dieter adf30af
diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp
Rex Dieter adf30af
index c079059..3017867 100644
Rex Dieter adf30af
--- a/server/src/storage/querybuilder.cpp
Rex Dieter adf30af
+++ b/server/src/storage/querybuilder.cpp
Rex Dieter adf30af
@@ -31,7 +31,7 @@
Rex Dieter adf30af
 
Rex Dieter adf30af
 using namespace Akonadi::Server;
Rex Dieter adf30af
 
Rex Dieter adf30af
-static QString compareOperatorToString( Query::CompareOperator op )
Rex Dieter adf30af
+static QLatin1String compareOperatorToString( Query::CompareOperator op )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   switch ( op ) {
Rex Dieter adf30af
   case Query::Equals:
Rex Dieter adf30af
@@ -58,10 +58,10 @@ static QString compareOperatorToString( Query::CompareOperator op )
Rex Dieter adf30af
     return QLatin1String( " LIKE " );
Rex Dieter adf30af
   }
Rex Dieter adf30af
   Q_ASSERT_X( false, "QueryBuilder::compareOperatorToString()", "Unknown compare operator." );
Rex Dieter adf30af
-  return QString();
Rex Dieter adf30af
+  return QLatin1String("");
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-static QString logicOperatorToString( Query::LogicOperator op )
Rex Dieter adf30af
+static QLatin1String logicOperatorToString( Query::LogicOperator op )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   switch ( op ) {
Rex Dieter adf30af
   case Query::And:
Rex Dieter adf30af
@@ -70,10 +70,10 @@ static QString logicOperatorToString( Query::LogicOperator op )
Rex Dieter adf30af
     return QLatin1String( " OR " );
Rex Dieter adf30af
   }
Rex Dieter adf30af
   Q_ASSERT_X( false, "QueryBuilder::logicOperatorToString()", "Unknown logic operator." );
Rex Dieter adf30af
-  return QString();
Rex Dieter adf30af
+  return QLatin1String("");
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-static QString sortOrderToString( Query::SortOrder order )
Rex Dieter adf30af
+static QLatin1String sortOrderToString( Query::SortOrder order )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   switch ( order ) {
Rex Dieter adf30af
   case Query::Ascending:
Rex Dieter adf30af
@@ -82,7 +82,17 @@ static QString sortOrderToString( Query::SortOrder order )
Rex Dieter adf30af
     return QLatin1String( " DESC" );
Rex Dieter adf30af
   }
Rex Dieter adf30af
   Q_ASSERT_X( false, "QueryBuilder::sortOrderToString()", "Unknown sort order." );
Rex Dieter adf30af
-  return QString();
Rex Dieter adf30af
+  return QLatin1String("");
Rex Dieter adf30af
+}
Rex Dieter adf30af
+
Rex Dieter adf30af
+static void appendJoined( QString *statement, const QStringList &strings, const QLatin1String &glue = QLatin1String( ", " ) )
Rex Dieter adf30af
+{
Rex Dieter adf30af
+  for (int i = 0, c = strings.size(); i < c; ++i) {
Rex Dieter adf30af
+    *statement += strings.at( i );
Rex Dieter adf30af
+    if (i + 1 < c) {
Rex Dieter adf30af
+      *statement += glue;
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+  }
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 QueryBuilder::QueryBuilder( const QString &table, QueryBuilder::QueryType type )
Rex Dieter adf30af
@@ -94,10 +104,12 @@ QueryBuilder::QueryBuilder( const QString &table, QueryBuilder::QueryType type )
Rex Dieter adf30af
    , mDatabaseType( DbType::Unknown )
Rex Dieter adf30af
 #endif
Rex Dieter adf30af
    , mType( type )
Rex Dieter adf30af
-   , mIdentificationColumn( QLatin1String( "id" ) )
Rex Dieter adf30af
+   , mIdentificationColumn(  )
Rex Dieter adf30af
    , mLimit( -1 )
Rex Dieter adf30af
    , mDistinct( false )
Rex Dieter adf30af
 {
Rex Dieter adf30af
+  static const QString defaultIdColumn = QLatin1String( "id" );
Rex Dieter adf30af
+  mIdentificationColumn = defaultIdColumn;
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 void QueryBuilder::setDatabaseType( DbType::Type type )
Rex Dieter adf30af
@@ -175,60 +187,65 @@ void QueryBuilder::sqliteAdaptUpdateJoin( Query::Condition &condition )
Rex Dieter adf30af
   qb.addCondition( joinCondition.second );
Rex Dieter adf30af
 
Rex Dieter adf30af
   // Convert the subquery to string
Rex Dieter adf30af
-  condition.mColumn = QLatin1String( "( " ) + qb.buildQuery() + QLatin1String( " )" );
Rex Dieter adf30af
+  condition.mColumn.reserve(1024);
Rex Dieter adf30af
+  condition.mColumn.resize(0);
Rex Dieter adf30af
+  condition.mColumn += QLatin1String( "( " );
Rex Dieter adf30af
+  qb.buildQuery(&condition.mColumn);
Rex Dieter adf30af
+  condition.mColumn += QLatin1String( " )" );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-
Rex Dieter adf30af
-QString QueryBuilder::buildQuery()
Rex Dieter adf30af
+void QueryBuilder::buildQuery(QString *statement)
Rex Dieter adf30af
 {
Rex Dieter adf30af
-  QString statement;
Rex Dieter adf30af
-
Rex Dieter adf30af
   // we add the ON conditions of Inner Joins in a Update query here
Rex Dieter adf30af
   // but don't want to change the mRootCondition on each exec().
Rex Dieter adf30af
   Query::Condition whereCondition = mRootCondition[WhereCondition];
Rex Dieter adf30af
 
Rex Dieter adf30af
   switch ( mType ) {
Rex Dieter adf30af
   case Select:
Rex Dieter adf30af
-    statement += QLatin1String( "SELECT " );
Rex Dieter adf30af
+    *statement += QLatin1String( "SELECT " );
Rex Dieter adf30af
     if ( mDistinct ) {
Rex Dieter adf30af
-      statement += QLatin1String( "DISTINCT " );
Rex Dieter adf30af
+      *statement += QLatin1String( "DISTINCT " );
Rex Dieter adf30af
     }
Rex Dieter adf30af
     Q_ASSERT_X( mColumns.count() > 0, "QueryBuilder::exec()", "No columns specified" );
Rex Dieter adf30af
-    statement += mColumns.join( QLatin1String( ", " ) );
Rex Dieter adf30af
-    statement += QLatin1String( " FROM " );
Rex Dieter adf30af
-    statement += mTable;
Rex Dieter adf30af
+    appendJoined( statement, mColumns );
Rex Dieter adf30af
+    *statement += QLatin1String( " FROM " );
Rex Dieter adf30af
+    *statement += mTable;
Rex Dieter adf30af
     Q_FOREACH ( const QString &joinedTable, mJoinedTables ) {
Rex Dieter adf30af
       const QPair<JoinType, Query::Condition> &join = mJoins.value( joinedTable );
Rex Dieter adf30af
       switch ( join.first ) {
Rex Dieter adf30af
       case LeftJoin:
Rex Dieter adf30af
-        statement += QLatin1String( " LEFT JOIN " );
Rex Dieter adf30af
+        *statement += QLatin1String( " LEFT JOIN " );
Rex Dieter adf30af
         break;
Rex Dieter adf30af
       case InnerJoin:
Rex Dieter adf30af
-        statement += QLatin1String( " INNER JOIN " );
Rex Dieter adf30af
+        *statement += QLatin1String( " INNER JOIN " );
Rex Dieter adf30af
         break;
Rex Dieter adf30af
       }
Rex Dieter adf30af
-      statement += joinedTable;
Rex Dieter adf30af
-      statement += QLatin1String( " ON " );
Rex Dieter adf30af
-      statement += buildWhereCondition( join.second );
Rex Dieter adf30af
+      *statement += joinedTable;
Rex Dieter adf30af
+      *statement += QLatin1String( " ON " );
Rex Dieter adf30af
+      buildWhereCondition( statement, join.second );
Rex Dieter adf30af
     }
Rex Dieter adf30af
     break;
Rex Dieter adf30af
   case Insert:
Rex Dieter adf30af
   {
Rex Dieter adf30af
-    statement += QLatin1String( "INSERT INTO " );
Rex Dieter adf30af
-    statement += mTable;
Rex Dieter adf30af
-    statement += QLatin1String( " (" );
Rex Dieter adf30af
-    typedef QPair<QString,QVariant> StringVariantPair;
Rex Dieter adf30af
-    QStringList cols, vals;
Rex Dieter adf30af
-    Q_FOREACH ( const StringVariantPair &p, mColumnValues ) {
Rex Dieter adf30af
-      cols.append( p.first );
Rex Dieter adf30af
-      vals.append( bindValue( p.second ) );
Rex Dieter adf30af
+    *statement += QLatin1String( "INSERT INTO " );
Rex Dieter adf30af
+    *statement += mTable;
Rex Dieter adf30af
+    *statement += QLatin1String( " (" );
Rex Dieter adf30af
+    for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
Rex Dieter adf30af
+      *statement += mColumnValues.at(i).first;
Rex Dieter adf30af
+      if (i + 1 < c) {
Rex Dieter adf30af
+        *statement += QLatin1String( ", " );
Rex Dieter adf30af
+      }
Rex Dieter adf30af
+    }
Rex Dieter adf30af
+    *statement += QLatin1String( ") VALUES (" );
Rex Dieter adf30af
+    for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
Rex Dieter adf30af
+      bindValue( statement, mColumnValues.at(i).second );
Rex Dieter adf30af
+      if (i + 1 < c) {
Rex Dieter adf30af
+        *statement += QLatin1String( ", " );
Rex Dieter adf30af
+      }
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    statement += cols.join( QLatin1String( ", " ) );
Rex Dieter adf30af
-    statement += QLatin1String( ") VALUES (" );
Rex Dieter adf30af
-    statement += vals.join( QLatin1String( ", " ) );
Rex Dieter adf30af
-    statement += QLatin1Char( ')' );
Rex Dieter adf30af
+    *statement += QLatin1Char( ')' );
Rex Dieter adf30af
     if ( mDatabaseType == DbType::PostgreSQL && !mIdentificationColumn.isEmpty() ) {
Rex Dieter adf30af
-      statement += QLatin1String( " RETURNING " ) + mIdentificationColumn;
Rex Dieter adf30af
+      *statement += QLatin1String( " RETURNING " ) + mIdentificationColumn;
Rex Dieter adf30af
     }
Rex Dieter adf30af
     break;
Rex Dieter adf30af
   }
Rex Dieter adf30af
@@ -246,78 +263,75 @@ QString QueryBuilder::buildQuery()
Rex Dieter adf30af
       sqliteAdaptUpdateJoin( whereCondition );
Rex Dieter adf30af
     }
Rex Dieter adf30af
 
Rex Dieter adf30af
-    statement += QLatin1String( "UPDATE " );
Rex Dieter adf30af
-    statement += mTable;
Rex Dieter adf30af
+    *statement += QLatin1String( "UPDATE " );
Rex Dieter adf30af
+    *statement += mTable;
Rex Dieter adf30af
 
Rex Dieter adf30af
     if ( mDatabaseType == DbType::MySQL && !mJoinedTables.isEmpty() ) {
Rex Dieter adf30af
       // for mysql we list all tables directly
Rex Dieter adf30af
-      statement += QLatin1String( ", " );
Rex Dieter adf30af
-      statement += mJoinedTables.join( QLatin1String( ", " ) );
Rex Dieter adf30af
+      *statement += QLatin1String( ", " );
Rex Dieter adf30af
+      appendJoined( statement, mJoinedTables );
Rex Dieter adf30af
     }
Rex Dieter adf30af
 
Rex Dieter adf30af
-    statement += QLatin1String( " SET " );
Rex Dieter adf30af
+    *statement += QLatin1String( " SET " );
Rex Dieter adf30af
     Q_ASSERT_X( mColumnValues.count() >= 1, "QueryBuilder::exec()", "At least one column needs to be changed" );
Rex Dieter adf30af
-    typedef QPair<QString,QVariant> StringVariantPair;
Rex Dieter adf30af
-    QStringList updStmts;
Rex Dieter adf30af
-    Q_FOREACH ( const StringVariantPair &p, mColumnValues ) {
Rex Dieter adf30af
-      QString updStmt = p.first;
Rex Dieter adf30af
-      updStmt += QLatin1String( " = " );
Rex Dieter adf30af
-      updStmt += bindValue( p.second );
Rex Dieter adf30af
-      updStmts << updStmt;
Rex Dieter adf30af
+    for (int i = 0, c = mColumnValues.size(); i < c; ++i) {
Rex Dieter adf30af
+      const QPair<QString, QVariant>& p = mColumnValues.at( i );
Rex Dieter adf30af
+      *statement += p.first;
Rex Dieter adf30af
+      *statement += QLatin1String( " = " );
Rex Dieter adf30af
+      bindValue( statement, p.second );
Rex Dieter adf30af
+      if (i + 1 < c) {
Rex Dieter adf30af
+        *statement += QLatin1String( ", " );
Rex Dieter adf30af
+      }
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    statement += updStmts.join( QLatin1String( ", " ) );
Rex Dieter adf30af
 
Rex Dieter adf30af
     if ( mDatabaseType == DbType::PostgreSQL && !mJoinedTables.isEmpty() ) {
Rex Dieter adf30af
       // PSQL have this syntax
Rex Dieter adf30af
       // FROM t1 JOIN t2 JOIN ...
Rex Dieter adf30af
-      statement += QLatin1String( " FROM " );
Rex Dieter adf30af
-      statement += mJoinedTables.join( QLatin1String( " JOIN " ) );
Rex Dieter adf30af
+      *statement += QLatin1String( " FROM " );
Rex Dieter adf30af
+      appendJoined( statement, mJoinedTables, QLatin1String( " JOIN " ) );
Rex Dieter adf30af
     }
Rex Dieter adf30af
 
Rex Dieter adf30af
     break;
Rex Dieter adf30af
   }
Rex Dieter adf30af
   case Delete:
Rex Dieter adf30af
-    statement += QLatin1String( "DELETE FROM " );
Rex Dieter adf30af
-    statement += mTable;
Rex Dieter adf30af
+    *statement += QLatin1String( "DELETE FROM " );
Rex Dieter adf30af
+    *statement += mTable;
Rex Dieter adf30af
     break;
Rex Dieter adf30af
   default:
Rex Dieter adf30af
     Q_ASSERT_X( false, "QueryBuilder::exec()", "Unknown enum value" );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( !whereCondition.isEmpty() ) {
Rex Dieter adf30af
-    statement += QLatin1String( " WHERE " );
Rex Dieter adf30af
-    statement += buildWhereCondition( whereCondition );
Rex Dieter adf30af
+    *statement += QLatin1String( " WHERE " );
Rex Dieter adf30af
+    buildWhereCondition( statement, whereCondition );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( !mGroupColumns.isEmpty() ) {
Rex Dieter adf30af
-    statement += QLatin1String( " GROUP BY " );
Rex Dieter adf30af
-    statement += mGroupColumns.join( QLatin1String( ", " ) );
Rex Dieter adf30af
+    *statement += QLatin1String( " GROUP BY " );
Rex Dieter adf30af
+    appendJoined( statement, mGroupColumns );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( !mRootCondition[HavingCondition].isEmpty() ) {
Rex Dieter adf30af
-    statement += QLatin1String( " HAVING " );
Rex Dieter adf30af
-    statement += buildWhereCondition( mRootCondition[HavingCondition] );
Rex Dieter adf30af
+    *statement += QLatin1String( " HAVING " );
Rex Dieter adf30af
+    buildWhereCondition( statement, mRootCondition[HavingCondition] );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( !mSortColumns.isEmpty() ) {
Rex Dieter adf30af
     Q_ASSERT_X( mType == Select, "QueryBuilder::exec()", "Order statements are only valid for SELECT queries" );
Rex Dieter adf30af
-    QStringList orderStmts;
Rex Dieter adf30af
-    typedef QPair<QString, Query::SortOrder> SortColumnInfo;
Rex Dieter adf30af
-    Q_FOREACH ( const SortColumnInfo &order, mSortColumns ) {
Rex Dieter adf30af
-      QString orderStmt;
Rex Dieter adf30af
-      orderStmt += order.first;
Rex Dieter adf30af
-      orderStmt += sortOrderToString( order.second );
Rex Dieter adf30af
-      orderStmts << orderStmt;
Rex Dieter adf30af
+    *statement += QLatin1String( " ORDER BY " );
Rex Dieter adf30af
+    for (int i = 0, c = mSortColumns.size(); i < c; ++i) {
Rex Dieter adf30af
+      const QPair<QString, Query::SortOrder>& order = mSortColumns.at( i );
Rex Dieter adf30af
+      *statement += order.first;
Rex Dieter adf30af
+      *statement += sortOrderToString( order.second );
Rex Dieter adf30af
+      if (i + 1 < c) {
Rex Dieter adf30af
+        *statement += QLatin1String( ", " );
Rex Dieter adf30af
+      }
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    statement += QLatin1String( " ORDER BY " );
Rex Dieter adf30af
-    statement += orderStmts.join( QLatin1String( ", " ) );
Rex Dieter adf30af
   }
Rex Dieter adf30af
 
Rex Dieter adf30af
   if ( mLimit > 0 ) {
Rex Dieter adf30af
-    statement += QLatin1Literal( " LIMIT " ) + QString::number( mLimit );
Rex Dieter adf30af
+    *statement += QLatin1Literal( " LIMIT " ) + QString::number( mLimit );
Rex Dieter adf30af
   }
Rex Dieter adf30af
-
Rex Dieter adf30af
-  return statement;
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
 bool QueryBuilder::retryLastTransaction( bool rollback )
Rex Dieter adf30af
@@ -334,7 +348,9 @@ bool QueryBuilder::retryLastTransaction( bool rollback )
Rex Dieter adf30af
 
Rex Dieter adf30af
 bool QueryBuilder::exec()
Rex Dieter adf30af
 {
Rex Dieter adf30af
-  const QString statement = buildQuery();
Rex Dieter adf30af
+  QString statement;
Rex Dieter adf30af
+  statement.reserve(1024);
Rex Dieter adf30af
+  buildQuery(&statement);
Rex Dieter adf30af
 
Rex Dieter adf30af
 #ifndef QUERYBUILDER_UNITTEST
Rex Dieter adf30af
   if ( QueryCache::contains( statement ) ) {
Rex Dieter adf30af
@@ -443,52 +459,54 @@ void QueryBuilder::addColumn( const QString &col )
Rex Dieter adf30af
 
Rex Dieter adf30af
 void QueryBuilder::addAggregation( const QString &col, const QString &aggregate )
Rex Dieter adf30af
 {
Rex Dieter adf30af
-  QString s( aggregate );
Rex Dieter adf30af
-  s += QLatin1Char( '(' );
Rex Dieter adf30af
-  s += col;
Rex Dieter adf30af
-  s += QLatin1Char( ')' );
Rex Dieter adf30af
-  mColumns.append( s );
Rex Dieter adf30af
+  mColumns.append( aggregate + QLatin1Char( '(' ) + col + QLatin1Char( ')' ) );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-QString QueryBuilder::bindValue( const QVariant &value )
Rex Dieter adf30af
+void QueryBuilder::bindValue( QString *query, const QVariant &value )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   mBindValues << value;
Rex Dieter adf30af
-  return QLatin1Char( ':' ) + QString::number( mBindValues.count() - 1 );
Rex Dieter adf30af
+  *query += QLatin1Char( ':' ) + QString::number( mBindValues.count() - 1 );
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
-QString QueryBuilder::buildWhereCondition( const Query::Condition &cond )
Rex Dieter adf30af
+void QueryBuilder::buildWhereCondition( QString *query, const Query::Condition &cond )
Rex Dieter adf30af
 {
Rex Dieter adf30af
   if ( !cond.isEmpty() ) {
Rex Dieter adf30af
-    QStringList conds;
Rex Dieter adf30af
-    Q_FOREACH ( const Query::Condition &c, cond.subConditions() ) {
Rex Dieter adf30af
-      conds << buildWhereCondition( c );
Rex Dieter adf30af
+    *query += QLatin1String( "( " );
Rex Dieter adf30af
+    const QLatin1String glue = logicOperatorToString( cond.mCombineOp );
Rex Dieter adf30af
+    const Query::Condition::List& subConditions = cond.subConditions();
Rex Dieter adf30af
+    for (int i = 0, c = subConditions.size(); i < c; ++i) {
Rex Dieter adf30af
+      buildWhereCondition(query, subConditions.at(i));
Rex Dieter adf30af
+      if (i + 1 < c) {
Rex Dieter adf30af
+        *query += glue;
Rex Dieter adf30af
+      }
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    return QLatin1String( "( " ) + conds.join( logicOperatorToString( cond.mCombineOp ) ) + QLatin1String( " )" );
Rex Dieter adf30af
+    *query += QLatin1String( " )" );
Rex Dieter adf30af
   } else {
Rex Dieter adf30af
-    QString stmt = cond.mColumn;
Rex Dieter adf30af
-    stmt += compareOperatorToString( cond.mCompareOp );
Rex Dieter adf30af
+    *query += cond.mColumn;
Rex Dieter adf30af
+    *query += compareOperatorToString( cond.mCompareOp );
Rex Dieter adf30af
     if ( cond.mComparedColumn.isEmpty() ) {
Rex Dieter adf30af
       if ( cond.mComparedValue.isValid() ) {
Rex Dieter adf30af
         if ( cond.mComparedValue.canConvert( QVariant::List ) ) {
Rex Dieter adf30af
-          stmt += QLatin1String( "( " );
Rex Dieter adf30af
-          QStringList entries;
Rex Dieter adf30af
-          Q_ASSERT_X( !cond.mComparedValue.toList().isEmpty(),
Rex Dieter adf30af
+          *query += QLatin1String( "( " );
Rex Dieter adf30af
+          const QVariantList& entries = cond.mComparedValue.toList();
Rex Dieter adf30af
+          Q_ASSERT_X( !entries.isEmpty(),
Rex Dieter adf30af
                       "QueryBuilder::buildWhereCondition()", "No values given for IN condition." );
Rex Dieter adf30af
-          Q_FOREACH ( const QVariant &entry, cond.mComparedValue.toList() ) {
Rex Dieter adf30af
-            entries << bindValue( entry );
Rex Dieter adf30af
+          for (int i = 0, c = entries.size(); i < c; ++i) {
Rex Dieter adf30af
+            bindValue( query, entries.at(i) );
Rex Dieter adf30af
+            if (i + 1 < c) {
Rex Dieter adf30af
+              *query += QLatin1String( ", " );
Rex Dieter adf30af
+            }
Rex Dieter adf30af
           }
Rex Dieter adf30af
-          stmt += entries.join( QLatin1String( ", " ) );
Rex Dieter adf30af
-          stmt += QLatin1String( " )" );
Rex Dieter adf30af
+          *query += QLatin1String( " )" );
Rex Dieter adf30af
         } else {
Rex Dieter adf30af
-          stmt += bindValue( cond.mComparedValue );
Rex Dieter adf30af
+          bindValue( query, cond.mComparedValue );
Rex Dieter adf30af
         }
Rex Dieter adf30af
       } else {
Rex Dieter adf30af
-        stmt += QLatin1String( "NULL" );
Rex Dieter adf30af
+        *query += QLatin1String( "NULL" );
Rex Dieter adf30af
       }
Rex Dieter adf30af
     } else {
Rex Dieter adf30af
-      stmt += cond.mComparedColumn;
Rex Dieter adf30af
+      *query += cond.mComparedColumn;
Rex Dieter adf30af
     }
Rex Dieter adf30af
-    return stmt;
Rex Dieter adf30af
   }
Rex Dieter adf30af
 }
Rex Dieter adf30af
 
Rex Dieter adf30af
diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h
Rex Dieter adf30af
index b380f93..df7c362 100644
Rex Dieter adf30af
--- a/server/src/storage/querybuilder.h
Rex Dieter adf30af
+++ b/server/src/storage/querybuilder.h
Rex Dieter adf30af
@@ -70,7 +70,9 @@ class QueryBuilder
Rex Dieter adf30af
       WhereCondition,
Rex Dieter adf30af
       /// add condition to HAVING part of the query
Rex Dieter adf30af
       /// NOTE: only supported for SELECT queries
Rex Dieter adf30af
-      HavingCondition
Rex Dieter adf30af
+      HavingCondition,
Rex Dieter adf30af
+
Rex Dieter adf30af
+      NUM_CONDITIONS
Rex Dieter adf30af
     };
Rex Dieter adf30af
 
Rex Dieter adf30af
     /**
Rex Dieter adf30af
@@ -234,9 +236,9 @@ class QueryBuilder
Rex Dieter adf30af
     qint64 insertId();
Rex Dieter adf30af
 
Rex Dieter adf30af
   private:
Rex Dieter adf30af
-    QString buildQuery();
Rex Dieter adf30af
-    QString bindValue( const QVariant &value );
Rex Dieter adf30af
-    QString buildWhereCondition( const Query::Condition &cond );
Rex Dieter adf30af
+    void buildQuery( QString *query );
Rex Dieter adf30af
+    void bindValue( QString *query, const QVariant &value );
Rex Dieter adf30af
+    void buildWhereCondition( QString *query, const Query::Condition &cond );
Rex Dieter adf30af
 
Rex Dieter adf30af
     /**
Rex Dieter adf30af
      * SQLite does not support JOINs with UPDATE, so we have to convert it into
Rex Dieter adf30af
@@ -249,11 +251,11 @@ class QueryBuilder
Rex Dieter adf30af
   private:
Rex Dieter adf30af
     QString mTable;
Rex Dieter adf30af
     DbType::Type mDatabaseType;
Rex Dieter adf30af
-    QHash<ConditionType, Query::Condition> mRootCondition;
Rex Dieter adf30af
+    Query::Condition mRootCondition[NUM_CONDITIONS];
Rex Dieter adf30af
     QSqlQuery mQuery;
Rex Dieter adf30af
     QueryType mType;
Rex Dieter adf30af
     QStringList mColumns;
Rex Dieter adf30af
-    QList<QVariant> mBindValues;
Rex Dieter adf30af
+    QVector<QVariant> mBindValues;
Rex Dieter adf30af
     QVector<QPair<QString, Query::SortOrder> > mSortColumns;
Rex Dieter adf30af
     QStringList mGroupColumns;
Rex Dieter adf30af
     QVector<QPair<QString, QVariant> > mColumnValues;
Rex Dieter adf30af
diff --git a/server/tests/unittest/querybuildertest.cpp b/server/tests/unittest/querybuildertest.cpp
Rex Dieter adf30af
index 0aba8a1..92df2a2 100644
Rex Dieter adf30af
--- a/server/tests/unittest/querybuildertest.cpp
Rex Dieter adf30af
+++ b/server/tests/unittest/querybuildertest.cpp
Rex Dieter adf30af
@@ -29,26 +29,29 @@
Rex Dieter adf30af
 
Rex Dieter adf30af
 QTEST_MAIN( QueryBuilderTest )
Rex Dieter adf30af
 
Rex Dieter adf30af
+Q_DECLARE_METATYPE(QVector<QVariant>)
Rex Dieter adf30af
+
Rex Dieter adf30af
 using namespace Akonadi::Server;
Rex Dieter adf30af
 
Rex Dieter adf30af
 void QueryBuilderTest::testQueryBuilder_data()
Rex Dieter adf30af
 {
Rex Dieter adf30af
+  qRegisterMetaType<QVector<QVariant> >();
Rex Dieter adf30af
   mBuilders.clear();
Rex Dieter adf30af
   QTest::addColumn<int>( "qbId" );
Rex Dieter adf30af
   QTest::addColumn<QString>( "sql" );
Rex Dieter adf30af
-  QTest::addColumn<QList<QVariant> >( "bindValues" );
Rex Dieter adf30af
+  QTest::addColumn<QVector<QVariant> >( "bindValues" );
Rex Dieter adf30af
 
Rex Dieter adf30af
   QueryBuilder qb( "table", QueryBuilder::Select );
Rex Dieter adf30af
   qb.addColumn( "col1" );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "simple select" ) << mBuilders.count() << QString( "SELECT col1 FROM table" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb.addColumn( "col2" );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "simple select 2" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb.addValueCondition( "col1", Query::Equals, QVariant( 5 ) );
Rex Dieter adf30af
-  QList<QVariant> bindVals;
Rex Dieter adf30af
+  QVector<QVariant> bindVals;
Rex Dieter adf30af
   bindVals << QVariant( 5 );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
   QTest::newRow( "single where" ) << mBuilders.count() << QString( "SELECT col1, col2 FROM table WHERE ( col1 = :0 )" ) << bindVals;
Rex Dieter adf30af
@@ -71,17 +74,17 @@ void QueryBuilderTest::testQueryBuilder_data()
Rex Dieter adf30af
   qb = QueryBuilder( "table" );
Rex Dieter adf30af
   qb.addAggregation( "col1", "count" );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "single aggregation" ) << mBuilders.count() << QString( "SELECT count(col1) FROM table" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "single aggregation" ) << mBuilders.count() << QString( "SELECT count(col1) FROM table" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb = QueryBuilder( "table" );
Rex Dieter adf30af
   qb.addColumn( "col1" );
Rex Dieter adf30af
   qb.addSortColumn( "col1" );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "single order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "single order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb.addSortColumn( "col2", Query::Descending );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "multiple order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC, col2 DESC" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "multiple order by" ) << mBuilders.count() << QString( "SELECT col1 FROM table ORDER BY col1 ASC, col2 DESC" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb = QueryBuilder( "table" );
Rex Dieter adf30af
   qb.addColumn( "col1" );
Rex Dieter adf30af
@@ -98,7 +101,7 @@ void QueryBuilderTest::testQueryBuilder_data()
Rex Dieter adf30af
   qb.addColumn( "col1" );
Rex Dieter adf30af
   qb.setLimit( 1 );
Rex Dieter adf30af
   mBuilders << qb;
Rex Dieter adf30af
-  QTest::newRow( "SELECT with LIMIT" ) << mBuilders.count() << QString( "SELECT col1 FROM table LIMIT 1" ) << QList<QVariant>();
Rex Dieter adf30af
+  QTest::newRow( "SELECT with LIMIT" ) << mBuilders.count() << QString( "SELECT col1 FROM table LIMIT 1" ) << QVector<QVariant>();
Rex Dieter adf30af
 
Rex Dieter adf30af
   qb = QueryBuilder( "table", QueryBuilder::Update );
Rex Dieter adf30af
   qb.setColumnValue( "col1", QString( "bla" ) );
Rex Dieter adf30af
@@ -263,7 +266,7 @@ void QueryBuilderTest::testQueryBuilder()
Rex Dieter adf30af
 {
Rex Dieter adf30af
   QFETCH( int, qbId );
Rex Dieter adf30af
   QFETCH( QString, sql );
Rex Dieter adf30af
-  QFETCH( QList<QVariant>, bindValues );
Rex Dieter adf30af
+  QFETCH( QVector<QVariant>, bindValues );
Rex Dieter adf30af
 
Rex Dieter adf30af
   --qbId;
Rex Dieter adf30af
 
Rex Dieter adf30af
@@ -271,3 +274,40 @@ void QueryBuilderTest::testQueryBuilder()
Rex Dieter adf30af
   QCOMPARE( mBuilders[qbId].mStatement, sql );
Rex Dieter adf30af
   QCOMPARE( mBuilders[qbId].mBindValues, bindValues );
Rex Dieter adf30af
 }
Rex Dieter adf30af
+
Rex Dieter adf30af
+void QueryBuilderTest::benchQueryBuilder()
Rex Dieter adf30af
+{
Rex Dieter adf30af
+  const QString table1 = QLatin1String("Table1");
Rex Dieter adf30af
+  const QString table2 = QLatin1String("Table2");
Rex Dieter adf30af
+  const QString table3 = QLatin1String("Table3");
Rex Dieter adf30af
+  const QString table1_id = QLatin1String("Table1.id");
Rex Dieter adf30af
+  const QString table2_id = QLatin1String("Table2.id");
Rex Dieter adf30af
+  const QString table3_id = QLatin1String("Table3.id");
Rex Dieter adf30af
+  const QString aggregate = QLatin1String("COUNT");
Rex Dieter adf30af
+  const QVariant value = QVariant::fromValue(QString("asdf"));
Rex Dieter adf30af
+
Rex Dieter adf30af
+  const QStringList columns = QStringList()
Rex Dieter adf30af
+    << QLatin1String("Table1.id")
Rex Dieter adf30af
+    << QLatin1String("Table1.fooAsdf")
Rex Dieter adf30af
+    << QLatin1String("Table2.barLala")
Rex Dieter adf30af
+    << QLatin1String("Table3.xyzFsd");
Rex Dieter adf30af
+
Rex Dieter adf30af
+  bool executed = true;
Rex Dieter adf30af
+
Rex Dieter adf30af
+  QBENCHMARK {
Rex Dieter adf30af
+    QueryBuilder builder( table1, QueryBuilder::Select );
Rex Dieter adf30af
+    builder.setDatabaseType( DbType::MySQL );
Rex Dieter adf30af
+    builder.addColumns( columns );
Rex Dieter adf30af
+    builder.addJoin( QueryBuilder::InnerJoin, table2, table2_id, table1_id );
Rex Dieter adf30af
+    builder.addJoin( QueryBuilder::LeftJoin, table3, table1_id, table3_id );
Rex Dieter adf30af
+    builder.addAggregation( columns.first(), aggregate );
Rex Dieter adf30af
+    builder.addColumnCondition( columns.at(1), Query::LessOrEqual, columns.last() );
Rex Dieter adf30af
+    builder.addValueCondition( columns.at(3), Query::Equals, value );
Rex Dieter adf30af
+    builder.addSortColumn( columns.at(2) );
Rex Dieter adf30af
+    builder.setLimit( 10 );
Rex Dieter adf30af
+    builder.addGroupColumn( columns.at(3) );
Rex Dieter adf30af
+    executed = executed && builder.exec();
Rex Dieter adf30af
+  }
Rex Dieter adf30af
+
Rex Dieter adf30af
+  QVERIFY(executed);
Rex Dieter adf30af
+}
Rex Dieter adf30af
\ No newline at end of file
Rex Dieter adf30af
diff --git a/server/tests/unittest/querybuildertest.h b/server/tests/unittest/querybuildertest.h
Rex Dieter adf30af
index 3bb6b22..1bca2cc 100644
Rex Dieter adf30af
--- a/server/tests/unittest/querybuildertest.h
Rex Dieter adf30af
+++ b/server/tests/unittest/querybuildertest.h
Rex Dieter adf30af
@@ -37,6 +37,8 @@ class QueryBuilderTest : public QObject
Rex Dieter adf30af
     void testQueryBuilder_data();
Rex Dieter adf30af
     void testQueryBuilder();
Rex Dieter adf30af
 
Rex Dieter adf30af
+    void benchQueryBuilder();
Rex Dieter adf30af
+
Rex Dieter adf30af
   private:
Rex Dieter adf30af
     QList< Akonadi::Server::QueryBuilder > mBuilders;
Rex Dieter adf30af
 };
Rex Dieter adf30af
-- 
Rex Dieter adf30af
2.1.0
Rex Dieter adf30af