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