File indexing completed on 2025-01-05 04:47:01

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef AKONADI_QUERYBUILDER_H
0008 #define AKONADI_QUERYBUILDER_H
0009 
0010 #include "dbtype.h"
0011 #include "query.h"
0012 
0013 #include <QList>
0014 #include <QPair>
0015 #include <QSqlQuery>
0016 #include <QString>
0017 #include <QStringList>
0018 #include <QVariant>
0019 
0020 #ifdef QUERYBUILDER_UNITTEST
0021 class QueryBuilderTest;
0022 #endif
0023 
0024 namespace Akonadi
0025 {
0026 namespace Server
0027 {
0028 class DataStore;
0029 /**
0030   Helper class to construct arbitrary SQL queries.
0031 */
0032 class QueryBuilder
0033 {
0034 public:
0035     enum QueryType {
0036         Select,
0037         Insert,
0038         Update,
0039         Delete,
0040     };
0041 
0042     /**
0043      * When the same table gets joined as both, Inner- and LeftJoin,
0044      * it will be merged into a single InnerJoin since it is more
0045      * restrictive.
0046      */
0047     enum JoinType {
0048         /// NOTE: only supported for UPDATE and SELECT queries.
0049         InnerJoin,
0050         /// NOTE: only supported for SELECT queries
0051         LeftJoin
0052     };
0053 
0054     /**
0055      * Defines the place at which a condition should be evaluated.
0056      */
0057     enum ConditionType {
0058         /// add condition to WHERE part of the query
0059         WhereCondition,
0060         /// add condition to HAVING part of the query
0061         /// NOTE: only supported for SELECT queries
0062         HavingCondition,
0063 
0064         NUM_CONDITIONS
0065     };
0066 
0067     /**
0068       Creates a new query builder.
0069 
0070       @param table The main table to operate on.
0071     */
0072     explicit QueryBuilder(const QString &table, QueryType type = Select);
0073     QueryBuilder(DataStore *store, const QString &table, QueryType type = Select);
0074 
0075     /**
0076       Creates a new query builder with subquery in FROM clause for SELECT queries.
0077       @param tableQuery must be a valid select query
0078       @param tableQueryAlias alias name for table query
0079     */
0080     explicit QueryBuilder(const QSqlQuery &tableQuery, const QString &tableQueryAlias);
0081     QueryBuilder(DataStore *store, const QSqlQuery &tableQuery, const QString &tableQueryAlias);
0082 
0083     /**
0084       Sets the database which should execute the query. Unfortunately the SQL "standard"
0085       is not interpreted in the same way everywhere...
0086     */
0087     void setDatabaseType(DbType::Type type);
0088 
0089     /**
0090       Join a table to the query.
0091 
0092       NOTE: make sure the @c JoinType is supported by the current @c QueryType
0093       @param joinType The type of JOIN you want to add.
0094       @param table The table to join.
0095       @param condition the ON condition for this join.
0096     */
0097     void addJoin(JoinType joinType, const QString &table, const Query::Condition &condition);
0098 
0099     /**
0100       Join a table to the query.
0101       This is a convenience method to create simple joins like e.g. 'LEFT JOIN t ON c1 = c2'.
0102 
0103       NOTE: make sure the @c JoinType is supported by the current @c QueryType
0104       @param joinType The type of JOIN you want to add.
0105       @param table The table to join.
0106       @param col1 The first column for the ON statement.
0107       @param col2 The second column for the ON statement.
0108     */
0109     void addJoin(JoinType joinType, const QString &table, const QString &col1, const QString &col2);
0110 
0111     /**
0112       Adds the given columns to a select query.
0113       @param cols The columns you want to select.
0114     */
0115     void addColumns(const QStringList &cols);
0116 
0117     /**
0118       Adds the given column to a select query.
0119       @param col The column to add.
0120     */
0121     void addColumn(const QString &col);
0122 
0123     /**
0124      * Adds the given case statement to a select query.
0125      * @param caseStmt The case statement to add.
0126      */
0127     void addColumn(const Query::Case &caseStmt);
0128 
0129     /**
0130      * Adds an aggregation statement.
0131      * @param col The column to aggregate on
0132      * @param aggregate The aggregation function.
0133      */
0134     void addAggregation(const QString &col, const QString &aggregate);
0135 
0136     /**
0137      * Adds and aggregation statement with CASE
0138      * @param caseStmt The case statement to aggregate on
0139      * @param aggregate The aggregation function.
0140      */
0141     void addAggregation(const Query::Case &caseStmt, const QString &aggregate);
0142 
0143     /**
0144       Add a WHERE or HAVING condition which compares a column with a given value.
0145       @param column The column that should be compared.
0146       @param op The operator used for comparison
0147       @param value The value @p column is compared to.
0148       @param type Defines whether this condition should be part of the WHERE or the HAVING
0149                   part of the query. Defaults to WHERE.
0150     */
0151     void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, ConditionType type = WhereCondition);
0152 
0153     /**
0154       Add a WHERE or HAVING condition which compares a column with another column.
0155       @param column The column that should be compared.
0156       @param op The operator used for comparison.
0157       @param column2 The column @p column is compared to.
0158       @param type Defines whether this condition should be part of the WHERE or the HAVING
0159                   part of the query. Defaults to WHERE.
0160     */
0161     void addColumnCondition(const QString &column, Query::CompareOperator op, const QString &column2, ConditionType type = WhereCondition);
0162 
0163     /**
0164       Add a WHERE condition. Use this to build hierarchical conditions.
0165       @param condition The condition that the resultset should satisfy.
0166       @param type Defines whether this condition should be part of the WHERE or the HAVING
0167                   part of the query. Defaults to WHERE.
0168     */
0169     void addCondition(const Query::Condition &condition, ConditionType type = WhereCondition);
0170 
0171     /**
0172       Define how WHERE or HAVING conditions are combined.
0173       @todo Give this method a better name.
0174       @param op The logical operator that should be used to combine the conditions.
0175       @param type Defines whether the operator should be used for WHERE or for HAVING
0176                   conditions. Defaults to WHERE conditions.
0177     */
0178     void setSubQueryMode(Query::LogicOperator op, ConditionType type = WhereCondition);
0179 
0180     /**
0181       Add sort column.
0182       @param column Name of the column to sort.
0183       @param order Sort order
0184     */
0185     void addSortColumn(const QString &column, Query::SortOrder order = Query::Ascending);
0186 
0187     /**
0188       Add a GROUP BY column.
0189       NOTE: Only supported in SELECT queries.
0190       @param column Name of the column to use for grouping.
0191     */
0192     void addGroupColumn(const QString &column);
0193 
0194     /**
0195       Add list of columns to GROUP BY.
0196       NOTE: Only supported in SELECT queries.
0197       @param columns Names of columns to use for grouping.
0198     */
0199     void addGroupColumns(const QStringList &columns);
0200 
0201     /**
0202       Sets a column to the given value (only valid for INSERT and UPDATE queries).
0203       @param column Column to change.
0204       @param value The value @p column should be set to.
0205     */
0206     void setColumnValue(const QString &column, const QVariant &value);
0207 
0208     /**
0209      * Specify whether duplicates should be included in the result.
0210      * @param distinct @c true to remove duplicates, @c false is the default
0211      */
0212     void setDistinct(bool distinct);
0213 
0214     /**
0215      * Limits the amount of retrieved rows.
0216      * @param limit the maximum number of rows to retrieve.
0217      * @param offset offset of the first row to retrieve.
0218      * The default value for @p offset is -1, indicating no offset.
0219      * @note This has no effect on anything but SELECT queries.
0220      */
0221     void setLimit(int limit, int offset=-1);
0222 
0223     /**
0224      * Sets the column used for identification in an INSERT statement.
0225      * The default is "id", only change this on tables without such a column
0226      * (usually n:m helper tables).
0227      * @param column Name of the identification column, empty string to disable this.
0228      * @note This only affects PostgreSQL.
0229      * @see insertId()
0230      */
0231     void setIdentificationColumn(const QString &column);
0232 
0233     /**
0234       Returns the query, only valid after exec().
0235     */
0236     QSqlQuery &query();
0237 
0238     /**
0239       Executes the query, returns true on success.
0240     */
0241     bool exec();
0242 
0243     /**
0244       Returns the ID of the newly created record (only valid for INSERT queries)
0245       @note This will assert when being used with setIdentificationColumn() called
0246       with an empty string.
0247       @returns -1 if invalid
0248     */
0249     qint64 insertId();
0250 
0251     /**
0252       Indicate to the database to acquire an exclusive lock on the rows already during
0253       SELECT statement.
0254 
0255       Only makes sense in SELECT queries.
0256      */
0257     void setForUpdate(bool forUpdate = true);
0258 
0259     /**
0260       Returns the name of the main table or subquery.
0261     */
0262     QString getTable() const;
0263 
0264     /**
0265      * Returns concatenated table name with column name.
0266      * @param column Column name.
0267      * @note Pass only @p column that are not prefixed by table name.
0268     */
0269     QString getTableWithColumn(const QString &column) const;
0270 
0271 private:
0272     void buildQuery(QString *query);
0273     void bindValue(QString *query, const QVariant &value);
0274     void buildWhereCondition(QString *query, const Query::Condition &cond);
0275     void buildCaseStatement(QString *query, const Query::Case &caseStmt);
0276     QString getTableQuery(const QSqlQuery &query, const QString &alias);
0277 
0278     /**
0279      * SQLite does not support JOINs with UPDATE, so we have to convert it into
0280      * subqueries
0281      */
0282     void sqliteAdaptUpdateJoin(Query::Condition &cond);
0283 
0284 protected:
0285     DataStore *dataStore() const
0286     {
0287         return mDataStore;
0288     }
0289 
0290 private:
0291     QString mTable;
0292     QSqlQuery mTableSubQuery;
0293     DataStore *mDataStore = nullptr;
0294     DbType::Type mDatabaseType;
0295     Query::Condition mRootCondition[NUM_CONDITIONS];
0296     QSqlQuery mQuery;
0297     QueryType mType;
0298     QStringList mColumns;
0299     QList<QVariant> mBindValues;
0300     QList<QPair<QString, Query::SortOrder>> mSortColumns;
0301     QStringList mGroupColumns;
0302     QList<QPair<QString, QVariant>> mColumnValues;
0303     QString mIdentificationColumn;
0304 
0305     // we must make sure that the tables are joined in the correct order
0306     // QMap sorts by key which might invalidate the queries
0307     QStringList mJoinedTables;
0308     QMap<QString, QPair<JoinType, Query::Condition>> mJoins;
0309     int mLimit;
0310     int mOffset;
0311     bool mDistinct;
0312     bool mForUpdate = false;
0313 #ifdef QUERYBUILDER_UNITTEST
0314     QString mStatement;
0315     friend class ::QueryBuilderTest;
0316 #endif
0317 };
0318 
0319 } // namespace Server
0320 } // namespace Akonadi
0321 
0322 #endif