Commit 55d4c729 authored by Konrad Czapla's avatar Konrad Czapla Committed by Sandro Knauß
Browse files

Add support for subquery as table expression

parent 733aff90
Pipeline #60878 passed with stage
in 10 minutes and 38 seconds
......@@ -19,6 +19,8 @@
#include <QElapsedTimer>
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlField>
#include <QSqlDriver>
using namespace Akonadi::Server;
......@@ -97,6 +99,26 @@ QueryBuilder::QueryBuilder(const QString &table, QueryBuilder::QueryType type)
, mType(type)
, mIdentificationColumn()
, mLimit(-1)
, mOffset(-1)
, mDistinct(false)
{
static const QString defaultIdColumn = QStringLiteral("id");
mIdentificationColumn = defaultIdColumn;
}
QueryBuilder::QueryBuilder(const QSqlQuery &tableQuery, const QString &tableQueryAlias)
: mTable(tableQueryAlias)
, mTableSubQuery(tableQuery)
#ifndef QUERYBUILDER_UNITTEST
, mDatabaseType(DbType::type(DataStore::self()->database()))
, mQuery(DataStore::self()->database())
#else
, mDatabaseType(DbType::Unknown)
#endif
, mType(QueryType::Select)
, mIdentificationColumn()
, mLimit(-1)
, mOffset(-1)
, mDistinct(false)
{
static const QString defaultIdColumn = QStringLiteral("id");
......@@ -202,7 +224,8 @@ void QueryBuilder::buildQuery(QString *statement)
Q_ASSERT_X(mColumns.count() > 0, "QueryBuilder::exec()", "No columns specified");
appendJoined(statement, mColumns);
*statement += QLatin1String(" FROM ");
*statement += mTable;
*statement += mTableSubQuery.isValid()
? getTableQuery(mTableSubQuery, mTable) : mTable;
for (const QString &joinedTable : qAsConst(mJoinedTables)) {
const auto &[joinType, joinCond] = mJoins.value(joinedTable);
switch (joinType) {
......@@ -321,6 +344,9 @@ void QueryBuilder::buildQuery(QString *statement)
if (mLimit > 0) {
*statement += QLatin1String(" LIMIT ") + QString::number(mLimit);
if (mOffset > 0) {
*statement += QLatin1String(" OFFSET ") + QString::number(mOffset);
}
}
if (mType == Select && mForUpdate) {
......@@ -580,9 +606,10 @@ void QueryBuilder::setDistinct(bool distinct)
mDistinct = distinct;
}
void QueryBuilder::setLimit(int limit)
void QueryBuilder::setLimit(int limit, int offset)
{
mLimit = limit;
mOffset = offset;
}
void QueryBuilder::setIdentificationColumn(const QString &column)
......@@ -617,3 +644,43 @@ void QueryBuilder::setForUpdate(bool forUpdate)
{
mForUpdate = forUpdate;
}
QString QueryBuilder::getTable() const
{
return mTable;
}
QString QueryBuilder::getTableQuery(const QSqlQuery& query, const QString &alias)
{
Q_ASSERT_X(query.isValid() && query.isSelect(), "QueryBuilder::getTableQuery", "Table subquery use only for valid SELECT queries");
QString tableQuery = query.lastQuery();
if (tableQuery.isEmpty()) {
qCWarning(AKONADISERVER_LOG) << "Table subquery is empty";
return tableQuery;
}
tableQuery.prepend(QLatin1String("( "));
const auto boundValues = query.boundValues();
for (int pos = boundValues.size() - 1; pos >= 0; --pos) {
const QString key(QLatin1Char(':') + QString::number(pos));
const auto value = boundValues.value(key);
QSqlField field(QLatin1String(""), value.type());
if (value.isNull()) {
field.clear();
}
else {
field.setValue(value);
}
const QString formattedValue = query.driver()->formatValue(field);
tableQuery.replace(key, formattedValue);
}
tableQuery.append(QLatin1String(" ) AS %1").arg(alias));
return tableQuery;
}
......@@ -70,6 +70,13 @@ public:
*/
explicit QueryBuilder(const QString &table, QueryType type = Select);
/**
Creates a new query builder with subquery in FROM cluase for SELECT queries.
@param tableQuery must be a valid select query
@param tableQueryAlias alias name for table query
*/
explicit QueryBuilder(const QSqlQuery &tableQuery, const QString &tableQueryAlias);
/**
Sets the database which should execute the query. Unfortunately the SQL "standard"
is not interpreted in the same way everywhere...
......@@ -204,9 +211,11 @@ public:
/**
* Limits the amount of retrieved rows.
* @param limit the maximum number of rows to retrieve.
* @param offset offset of the first row to retrieve.
* The default value for @p offset is -1, indicating no offset.
* @note This has no effect on anything but SELECT queries.
*/
void setLimit(int limit);
void setLimit(int limit, int offset=-1);
/**
* Sets the column used for identification in an INSERT statement.
......@@ -244,11 +253,17 @@ public:
*/
void setForUpdate(bool forUpdate = true);
/**
Returns the name of the main table or subquery.
*/
QString getTable() const;
private:
void buildQuery(QString *query);
void bindValue(QString *query, const QVariant &value);
void buildWhereCondition(QString *query, const Query::Condition &cond);
void buildCaseStatement(QString *query, const Query::Case &caseStmt);
QString getTableQuery(const QSqlQuery &query, const QString &alias);
/**
* SQLite does not support JOINs with UPDATE, so we have to convert it into
......@@ -258,6 +273,7 @@ private:
private:
QString mTable;
QSqlQuery mTableSubQuery;
DbType::Type mDatabaseType;
Query::Condition mRootCondition[NUM_CONDITIONS];
QSqlQuery mQuery;
......@@ -274,6 +290,7 @@ private:
QStringList mJoinedTables;
QMap<QString, QPair<JoinType, Query::Condition>> mJoins;
int mLimit;
int mOffset;
bool mDistinct;
bool mForUpdate = false;
#ifdef QUERYBUILDER_UNITTEST
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment