|
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 |
|