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