File indexing completed on 2024-06-23 05:07:05
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2006 Andreas Gungl <a.gungl@gmx.de> * 0003 * * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later * 0005 ***************************************************************************/ 0006 0007 #pragma once 0008 0009 #include <QList> 0010 #include <QMutex> 0011 #include <QObject> 0012 #include <QSqlDatabase> 0013 #include <QThreadStorage> 0014 0015 class QSqlQuery; 0016 class QTimer; 0017 0018 #include "entities.h" 0019 #include "notificationcollector.h" 0020 0021 #include <memory> 0022 0023 namespace Akonadi 0024 { 0025 namespace Server 0026 { 0027 class DbConfig; 0028 class DataStore; 0029 class DataStoreFactory 0030 { 0031 public: 0032 virtual ~DataStoreFactory() = default; 0033 virtual DataStore *createStore() = 0; 0034 0035 protected: 0036 explicit DataStoreFactory() = default; 0037 0038 private: 0039 Q_DISABLE_COPY_MOVE(DataStoreFactory) 0040 }; 0041 0042 class NotificationCollector; 0043 0044 /** 0045 This class handles all the database access. 0046 0047 <h4>Database configuration</h4> 0048 0049 You can select between various database backends during runtime using the 0050 @c $HOME/.config/akonadi/akonadiserverrc configuration file. 0051 0052 Example: 0053 @verbatim 0054 [%General] 0055 Driver=QMYSQL 0056 0057 [QMYSQL_EMBEDDED] 0058 Name=akonadi 0059 Options=SERVER_DATADIR=/home/foo/.local/share/akonadi/db_data 0060 0061 [QMYSQL] 0062 Name=akonadi 0063 Host=localhost 0064 User=foo 0065 Password=***** 0066 #Options=UNIX_SOCKET=/home/foo/.local/share/akonadi/socket-bar/mysql.socket 0067 StartServer=true 0068 ServerPath=/usr/sbin/mysqld 0069 0070 [QSQLITE] 0071 Name=/home/foo/.local/share/akonadi/akonadi.db 0072 @endverbatim 0073 0074 Use @c General/Driver to select the QSql driver to use for database 0075 access. The following drivers are currently supported, other might work 0076 but are untested: 0077 0078 - QMYSQL 0079 - QMYSQL_EMBEDDED 0080 - QSQLITE 0081 0082 The options for each driver are read from the corresponding group. 0083 The following options are supported, dependent on the driver not all of them 0084 might have an effect: 0085 0086 - Name: Database name, for sqlite that's the file name of the database. 0087 - Host: Hostname of the database server 0088 - User: Username for the database server 0089 - Password: Password for the database server 0090 - Options: Additional options, format is driver-dependent 0091 - StartServer: Start the database locally just for Akonadi instead of using an existing one 0092 - ServerPath: Path to the server executable 0093 */ 0094 class DataStore : public QObject 0095 { 0096 Q_OBJECT 0097 public: 0098 const constexpr static bool Silent = true; 0099 0100 static void setFactory(std::unique_ptr<DataStoreFactory> factory); 0101 0102 /** 0103 * Returns DataStore associated with the given database connection. 0104 */ 0105 static DataStore *dataStoreForDatabase(const QSqlDatabase &db); 0106 0107 /** 0108 Closes the database connection and destroys the DataStore object. 0109 */ 0110 ~DataStore() override; 0111 0112 /** 0113 Opens the database connection. 0114 */ 0115 virtual void open(); 0116 0117 /** 0118 Closes the database connection. 0119 */ 0120 void close(); 0121 0122 /** 0123 Initializes the database. Should be called during startup by the main thread. 0124 */ 0125 virtual bool init(); 0126 0127 /** 0128 Per thread singleton. 0129 */ 0130 static DataStore *self(); 0131 0132 /** 0133 * Returns whether per thread DataStore has been created. 0134 */ 0135 static bool hasDataStore(); 0136 0137 /* --- ItemFlags ----------------------------------------------------- */ 0138 virtual bool setItemsFlags(const PimItem::List &items, 0139 const QList<Flag> *currentFlags, 0140 const QList<Flag> &newFlags, 0141 bool *flagsChanged = nullptr, 0142 const Collection &col = Collection(), 0143 bool silent = false); 0144 virtual bool appendItemsFlags(const PimItem::List &items, 0145 const QList<Flag> &flags, 0146 bool *flagsChanged = nullptr, 0147 bool checkIfExists = true, 0148 const Collection &col = Collection(), 0149 bool silent = false); 0150 virtual bool removeItemsFlags(const PimItem::List &items, 0151 const QList<Flag> &flags, 0152 bool *tagsChanged = nullptr, 0153 const Collection &collection = Collection(), 0154 bool silent = false); 0155 0156 /* --- ItemTags ----------------------------------------------------- */ 0157 virtual bool setItemsTags(const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = nullptr, bool silent = false); 0158 virtual bool appendItemsTags(const PimItem::List &items, 0159 const Tag::List &tags, 0160 bool *tagsChanged = nullptr, 0161 bool checkIfExists = true, 0162 const Collection &col = Collection(), 0163 bool silent = false); 0164 virtual bool removeItemsTags(const PimItem::List &items, const Tag::List &tags, bool *tagsChanged = nullptr, bool silent = false); 0165 virtual bool removeTags(const Tag::List &tags, bool silent = false); 0166 0167 /* --- ItemParts ----------------------------------------------------- */ 0168 virtual bool removeItemParts(const PimItem &item, const QSet<QByteArray> &parts); 0169 0170 // removes all payload parts for this item. 0171 virtual bool invalidateItemCache(const PimItem &item); 0172 0173 /* --- Collection ------------------------------------------------------ */ 0174 virtual bool appendCollection(Collection &collection, const QStringList &mimeTypes, const QMap<QByteArray, QByteArray> &attributes); 0175 0176 /// removes the given collection and all its content 0177 virtual bool cleanupCollection(Collection &collection); 0178 0179 /// moves the collection @p collection to @p newParent. 0180 virtual bool moveCollection(Collection &collection, const Collection &newParent); 0181 0182 virtual bool appendMimeTypeForCollection(qint64 collectionId, const QStringList &mimeTypes); 0183 0184 static QString collectionDelimiter() 0185 { 0186 return QStringLiteral("/"); 0187 } 0188 0189 /** 0190 Determines the active cache policy for this Collection. 0191 The active cache policy is set in the corresponding Collection fields. 0192 */ 0193 virtual void activeCachePolicy(Collection &col); 0194 0195 /// Returns all virtual collections the @p item is linked to 0196 QList<Collection> virtualCollections(const PimItem &item); 0197 0198 QMap<Server::Entity::Id, QList<PimItem>> virtualCollections(const Akonadi::Server::PimItem::List &items); 0199 0200 /* --- PimItem ------------------------------------------------------- */ 0201 virtual bool appendPimItem(QList<Part> &parts, 0202 const QList<Flag> &flags, 0203 const MimeType &mimetype, 0204 const Collection &collection, 0205 const QDateTime &dateTime, 0206 const QString &remote_id, 0207 const QString &remoteRevision, 0208 const QString &gid, 0209 PimItem &pimItem); 0210 /** 0211 * Removes the pim item and all referenced data ( e.g. flags ) 0212 */ 0213 virtual bool cleanupPimItems(const PimItem::List &items, bool silent = false); 0214 0215 /** 0216 * Unhides the specified PimItem. Emits the itemAdded() notification as 0217 * the hidden flag is assumed to have been set by appendPimItem() before 0218 * pushing the item to the preprocessor chain. The hidden item had his 0219 * notifications disabled until now (so for the clients the "unhide" operation 0220 * is actually a new item arrival). 0221 * 0222 * This function does NOT verify if the item was *really* hidden: this is 0223 * responsibility of the caller. 0224 */ 0225 virtual bool unhidePimItem(PimItem &pimItem); 0226 0227 /** 0228 * Unhides all the items which have the "hidden" flag set. 0229 * This function doesn't emit any notification about the items 0230 * being unhidden so it's meant to be called only in rare circumstances. 0231 * The most notable call to this function is at server startup 0232 * when we attempt to restore a clean state of the database. 0233 */ 0234 virtual bool unhideAllPimItems(); 0235 0236 /* --- Collection attributes ------------------------------------------ */ 0237 virtual bool addCollectionAttribute(const Collection &col, const QByteArray &key, const QByteArray &value, bool silent = false); 0238 /** 0239 * Removes the given collection attribute for @p col. 0240 * @throws HandlerException on database errors 0241 * @returns @c true if the attribute existed, @c false otherwise 0242 */ 0243 virtual bool removeCollectionAttribute(const Collection &col, const QByteArray &key); 0244 0245 /* --- Helper functions ---------------------------------------------- */ 0246 0247 /** 0248 Begins a transaction. No changes will be written to the database and 0249 no notification signal will be emitted unless you call commitTransaction(). 0250 @return @c true if successful. 0251 */ 0252 virtual bool beginTransaction(const QString &name); 0253 0254 /** 0255 Reverts all changes within the current transaction. 0256 */ 0257 virtual bool rollbackTransaction(); 0258 0259 /** 0260 Commits all changes within the current transaction and emits all 0261 collected notification signals. If committing fails, the transaction 0262 will be rolled back. 0263 */ 0264 virtual bool commitTransaction(); 0265 0266 /** 0267 Returns true if there is a transaction in progress. 0268 */ 0269 bool inTransaction() const; 0270 0271 /** 0272 Returns the notification collector of this DataStore object. 0273 Use this to listen to change notification signals. 0274 */ 0275 NotificationCollector *notificationCollector(); 0276 0277 /** 0278 Returns the QSqlDatabase object. Use this for generating queries yourself. 0279 0280 Will [re-]open the database, if it is closed. 0281 */ 0282 QSqlDatabase database(); 0283 0284 /** 0285 Sets the current session id. 0286 */ 0287 void setSessionId(const QByteArray &sessionId) 0288 { 0289 mSessionId = sessionId; 0290 } 0291 0292 /** 0293 Returns if the database is currently open 0294 */ 0295 bool isOpened() const 0296 { 0297 return m_dbOpened; 0298 } 0299 0300 bool doRollback(); 0301 void transactionKilledByDB(); 0302 0303 Q_SIGNALS: 0304 /** 0305 Emitted if a transaction has been successfully committed. 0306 */ 0307 void transactionCommitted(); 0308 /** 0309 Emitted if a transaction has been aborted. 0310 */ 0311 void transactionRolledBack(); 0312 0313 protected: 0314 /** 0315 Creates a new DataStore object and opens it. 0316 */ 0317 DataStore(AkonadiServer *akonadi, DbConfig *dbConfig); 0318 explicit DataStore(DbConfig *config); 0319 0320 void debugLastDbError(QStringView actionDescription) const; 0321 void debugLastQueryError(const QSqlQuery &query, const char *actionDescription) const; 0322 0323 private: 0324 bool doAppendItemsFlag(const PimItem::List &items, const Flag &flag, const QSet<PimItem::Id> &existing, const Collection &col, bool silent); 0325 0326 bool doAppendItemsTag(const PimItem::List &items, const Tag &tag, const QSet<Entity::Id> &existing, const Collection &col, bool silent); 0327 0328 /** Converts the given date/time to the database format, i.e. 0329 "YYYY-MM-DD HH:MM:SS". 0330 @param dateTime the date/time in UTC 0331 @return the date/time in database format 0332 @see dateTimeToQDateTime 0333 */ 0334 static QString dateTimeFromQDateTime(const QDateTime &dateTime); 0335 0336 /** Converts the given date/time from database format to QDateTime. 0337 @param dateTime the date/time in database format 0338 @return the date/time as QDateTime 0339 @see dateTimeFromQDateTime 0340 */ 0341 static QDateTime dateTimeToQDateTime(const QByteArray &dateTime); 0342 0343 private Q_SLOTS: 0344 void sendKeepAliveQuery(); 0345 0346 protected: 0347 static std::unique_ptr<DataStoreFactory> sFactory; 0348 std::unique_ptr<NotificationCollector> mNotificationCollector; 0349 AkonadiServer *const m_akonadi = nullptr; 0350 DbConfig *const m_dbConfig = nullptr; 0351 0352 private: 0353 Q_DISABLE_COPY_MOVE(DataStore) 0354 0355 void cleanupAfterRollback(); 0356 QString m_connectionName; 0357 QSqlDatabase m_database; 0358 bool m_dbOpened; 0359 bool m_transactionKilledByDB = false; 0360 uint m_transactionLevel; 0361 struct TransactionQuery { 0362 QString query; 0363 QList<QVariant> boundValues; 0364 bool isBatch; 0365 }; 0366 QByteArray mSessionId; 0367 QTimer *m_keepAliveTimer = nullptr; 0368 0369 friend class DataStoreFactory; 0370 }; 0371 0372 } // namespace Server 0373 } // namespace Akonadi