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