datastore.h 13.1 KB
Newer Older
Till Adam's avatar
Till Adam committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/***************************************************************************
 *   Copyright (C) 2006 by Andreas Gungl <a.gungl@gmx.de>                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#ifndef DATASTORE_H
#define DATASTORE_H

Daniel Vrátil's avatar
Daniel Vrátil committed
23 24 25 26 27
#include <QObject>
#include <QList>
#include <QVector>
#include <QThreadStorage>
#include <QSqlDatabase>
Till Adam's avatar
Till Adam committed
28 29

class QSqlQuery;
30
class QTimer;
Till Adam's avatar
Till Adam committed
31

32
#include "entities.h"
33
#include "notificationcollector.h"
Till Adam's avatar
Till Adam committed
34

Laurent Montel's avatar
Laurent Montel committed
35 36 37 38
namespace Akonadi
{
namespace Server
{
Till Adam's avatar
Till Adam committed
39

40 41
class NotificationCollector;

42 43
/**
  This class handles all the database access.
44 45 46 47

  <h4>Database configuration</h4>

  You can select between various database backends during runtime using the
48
  @c $HOME/.config/akonadi/akonadiserverrc configuration file.
49 50 51 52 53 54 55 56

  Example:
@verbatim
[%General]
Driver=QMYSQL

[QMYSQL_EMBEDDED]
Name=akonadi
57
Options=SERVER_DATADIR=/home/foo/.local/share/akonadi/db_data
58 59 60 61 62 63

[QMYSQL]
Name=akonadi
Host=localhost
User=foo
Password=*****
64
#Options=UNIX_SOCKET=/home/foo/.local/share/akonadi/socket-bar/mysql.socket
65 66
StartServer=true
ServerPath=/usr/sbin/mysqld
67 68

[QSQLITE]
69
Name=/home/foo/.local/share/akonadi/akonadi.db
70 71
@endverbatim

72
  Use @c General/Driver to select the QSql driver to use for database
73 74 75 76 77 78 79 80
  access. The following drivers are currently supported, other might work
  but are untested:

  - QMYSQL
  - QMYSQL_EMBEDDED
  - QSQLITE

  The options for each driver are read from the corresponding group.
Volker Krause's avatar
Volker Krause committed
81
  The following options are supported, dependent on the driver not all of them
82 83 84 85 86 87 88
  might have an effect:

  - Name: Database name, for sqlite that's the file name of the database.
  - Host: Hostname of the database server
  - User: Username for the database server
  - Password: Password for the database server
  - Options: Additional options, format is driver-dependent
89 90
  - StartServer: Start the database locally just for Akonadi instead of using an existing one
  - ServerPath: Path to the server executable
91
*/
92
class DataStore : public QObject
Till Adam's avatar
Till Adam committed
93
{
94
    Q_OBJECT
95
public:
96 97 98
    /**
      Closes the database connection and destroys the DataStore object.
    */
Till Adam's avatar
Till Adam committed
99 100
    virtual ~DataStore();

101 102 103
    /**
      Opens the database connection.
    */
104
    virtual void open();
105 106

    /**
107
      Closes the database connection.
108
    */
109
    virtual void close();
110 111 112 113

    /**
      Initializes the database. Should be called during startup by the main thread.
    */
114
    virtual bool init();
115

116
    /**
Volker Krause's avatar
Volker Krause committed
117
      Per thread singleton.
118
    */
Guy Maurel's avatar
Guy Maurel committed
119
    static DataStore *self();
120

121 122 123 124 125
    /**
     * Returns whether per thread DataStore has been created.
     */
    static bool hasDataStore();

Till Adam's avatar
Till Adam committed
126
    /* --- ItemFlags ----------------------------------------------------- */
127
    virtual bool setItemsFlags(const PimItem::List &items, const QVector<Flag> &flags,
Laurent Montel's avatar
Laurent Montel committed
128
                               bool *flagsChanged = nullptr, const Collection &col = Collection(), bool silent = false);
Laurent Montel's avatar
Laurent Montel committed
129
    virtual bool appendItemsFlags(const PimItem::List &items, const QVector<Flag> &flags, bool *flagsChanged = nullptr,
Daniel Vrátil's avatar
Daniel Vrátil committed
130
                                  bool checkIfExists = true, const Collection &col = Collection(), bool silent = false);
Laurent Montel's avatar
Laurent Montel committed
131
    virtual bool removeItemsFlags(const PimItem::List &items, const QVector<Flag> &flags, bool *tagsChanged = nullptr,
Daniel Vrátil's avatar
Daniel Vrátil committed
132
                                  const Collection &collection = Collection(), bool silent = false);
Till Adam's avatar
Till Adam committed
133

134
    /* --- ItemTags ----------------------------------------------------- */
135
    virtual bool setItemsTags(const PimItem::List &items, const Tag::List &tags,
Laurent Montel's avatar
Laurent Montel committed
136
                              bool *tagsChanged = nullptr, bool silent = false);
137
    virtual bool appendItemsTags(const PimItem::List &items, const Tag::List &tags,
Laurent Montel's avatar
Laurent Montel committed
138
                                 bool *tagsChanged = nullptr, bool checkIfExists = true,
139 140
                                 const Collection &col = Collection(), bool silent = false);
    virtual bool removeItemsTags(const PimItem::List &items, const Tag::List &tags,
Laurent Montel's avatar
Laurent Montel committed
141
                                 bool *tagsChanged = nullptr, bool silent = false);
Laurent Montel's avatar
Laurent Montel committed
142
    virtual bool removeTags(const Tag::List &tags, bool silent = false);
143

144
    /* --- ItemParts ----------------------------------------------------- */
145
    virtual bool removeItemParts(const PimItem &item, const QSet<QByteArray> &parts);
146

147
    // removes all payload parts for this item.
148
    virtual bool invalidateItemCache(const PimItem &item);
149

150
    /* --- Collection ------------------------------------------------------ */
151
    virtual bool appendCollection(Collection &collection);
152

153
    /// removes the given collection and all its content
154
    virtual bool cleanupCollection(Collection &collection);
155
    /// same as the above but for database backends without working referential actions on foreign keys
156
    virtual bool cleanupCollection_slow(Collection &collection);
157

158
    /// moves the collection @p collection to @p newParent.
159
    virtual bool moveCollection(Collection &collection, const Collection &newParent);
Till Adam's avatar
Till Adam committed
160

161
    virtual bool appendMimeTypeForCollection(qint64 collectionId, const QStringList &mimeTypes);
Till Adam's avatar
Till Adam committed
162

163 164
    static QString collectionDelimiter()
    {
Laurent Montel's avatar
Laurent Montel committed
165
        return QStringLiteral("/");
166
    }
167

Volker Krause's avatar
Volker Krause committed
168
    /**
169 170
      Determines the active cache policy for this Collection.
      The active cache policy is set in the corresponding Collection fields.
Volker Krause's avatar
Volker Krause committed
171
    */
172
    virtual void activeCachePolicy(Collection &col);
Volker Krause's avatar
Volker Krause committed
173

174
    /// Returns all virtual collections the @p item is linked to
175
    QVector<Collection> virtualCollections(const PimItem &item);
176

177
    QMap< Server::Entity::Id, QList< PimItem > > virtualCollections(const Akonadi::Server::PimItem::List &items);
178

Till Adam's avatar
Till Adam committed
179
    /* --- PimItem ------------------------------------------------------- */
180
    virtual bool appendPimItem(QVector<Part> &parts,
181
                               const QVector<Flag> &flags,
182 183 184 185 186 187 188
                               const MimeType &mimetype,
                               const Collection &collection,
                               const QDateTime &dateTime,
                               const QString &remote_id,
                               const QString &remoteRevision,
                               const QString &gid,
                               PimItem &pimItem);
Till Adam's avatar
Till Adam committed
189 190 191
    /**
     * Removes the pim item and all referenced data ( e.g. flags )
     */
192
    virtual bool cleanupPimItems(const PimItem::List &items);
Till Adam's avatar
Till Adam committed
193

194
    /**
195
     * Unhides the specified PimItem. Emits the itemAdded() notification as
196 197 198 199 200 201
     * the hidden flag is assumed to have been set by appendPimItem() before
     * pushing the item to the preprocessor chain. The hidden item had his
     * notifications disabled until now (so for the clients the "unhide" operation
     * is actually a new item arrival).
     *
     * This function does NOT verify if the item was *really* hidden: this is
202
     * responsibility of the caller.
203
     */
204
    virtual bool unhidePimItem(PimItem &pimItem);
205 206 207 208 209 210 211 212

    /**
     * Unhides all the items which have the "hidden" flag set.
     * This function doesn't emit any notification about the items
     * being unhidden so it's meant to be called only in rare circumstances.
     * The most notable call to this function is at server startup
     * when we attempt to restore a clean state of the database.
     */
213
    virtual bool unhideAllPimItems();
214

Volker Krause's avatar
Volker Krause committed
215
    /* --- Collection attributes ------------------------------------------ */
216 217
    virtual bool addCollectionAttribute(const Collection &col, const QByteArray &key,
                                        const QByteArray &value);
218 219 220 221 222
    /**
     * Removes the given collection attribute for @p col.
     * @throws HandlerException on database errors
     * @returns @c true if the attribute existed, @c false otherwise
     */
223
    virtual bool removeCollectionAttribute(const Collection &col, const QByteArray &key);
224

Till Adam's avatar
Till Adam committed
225 226
    /* --- Helper functions ---------------------------------------------- */

227 228 229
    /**
      Begins a transaction. No changes will be written to the database and
      no notification signal will be emitted unless you call commitTransaction().
Volker Krause's avatar
Volker Krause committed
230
      @return @c true if successful.
231
    */
232
    virtual bool beginTransaction(const QString &name);
233 234 235 236

    /**
      Reverts all changes within the current transaction.
    */
237
    virtual bool rollbackTransaction();
238 239 240 241 242 243

    /**
      Commits all changes within the current transaction and emits all
      collected notfication signals. If committing fails, the transaction
      will be rolled back.
    */
244
    virtual bool commitTransaction();
245 246 247 248

    /**
      Returns true if there is a transaction in progress.
    */
249
    virtual bool inTransaction() const;
250 251 252 253 254

    /**
      Returns the notification collector of this DataStore object.
      Use this to listen to change notification signals.
    */
255
    virtual NotificationCollector *notificationCollector();
256

257 258
    /**
      Returns the QSqlDatabase object. Use this for generating queries yourself.
259 260

      Will [re-]open the database, if it is closed.
261
    */
262
    QSqlDatabase database();
263

264 265 266
    /**
      Sets the current session id.
    */
267 268 269 270
    void setSessionId(const QByteArray &sessionId)
    {
        mSessionId = sessionId;
    }
271

272 273 274
    /**
      Returns if the database is currently open
      */
Laurent Montel's avatar
Laurent Montel committed
275 276 277 278
    bool isOpened() const
    {
        return m_dbOpened;
    }
279

280
Q_SIGNALS:
281 282 283 284 285 286 287 288
    /**
      Emitted if a transaction has been successfully committed.
    */
    void transactionCommitted();
    /**
      Emitted if a transaction has been aborted.
    */
    void transactionRolledBack();
289

Till Adam's avatar
Till Adam committed
290
protected:
291 292 293 294 295
    /**
      Creates a new DataStore object and opens it.
    */
    DataStore();

296 297
    void debugLastDbError(const char *actionDescription) const;
    void debugLastQueryError(const QSqlQuery &query, const char *actionDescription) const;
Till Adam's avatar
Till Adam committed
298

299 300 301 302
private:
    bool doAppendItemsFlag(const PimItem::List &items, const Flag &flag,
                           const QSet<PimItem::Id> &existing, const Collection &col,
                           bool silent);
303

304
    bool doAppendItemsTag(const PimItem::List &items, const Tag &tag,
305
                          const QSet<Entity::Id> &existing, const Collection &col,
306
                          bool silent);
307

Till Adam's avatar
Till Adam committed
308 309 310 311 312 313
    /** Converts the given date/time to the database format, i.e.
        "YYYY-MM-DD HH:MM:SS".
        @param dateTime the date/time in UTC
        @return the date/time in database format
        @see dateTimeToQDateTime
     */
314
    static QString dateTimeFromQDateTime(const QDateTime &dateTime);
Till Adam's avatar
Till Adam committed
315 316 317 318 319 320

    /** Converts the given date/time from database format to QDateTime.
        @param dateTime the date/time in database format
        @return the date/time as QDateTime
        @see dateTimeFromQDateTime
     */
321
    static QDateTime dateTimeToQDateTime(const QByteArray &dateTime);
322 323 324 325 326 327 328 329 330 331 332 333

    /**
     * Adds the @p query to current transaction, so that it can be replayed in
     * case the transaction deadlocks or timeouts.
     *
     * When DataStore is not in transaction or SQLite is configured, this method
     * does nothing.
     *
     * All queries will automatically be removed when transaction is committed.
     *
     * This method should only be used by QueryBuilder.
     */
334
    void addQueryToTransaction(const QSqlQuery &query, bool isBatch);
335 336 337 338 339 340 341 342 343 344 345

    /**
     * Tries to execute all queries from last transaction again. If any of the
     * queries fails, the entire transaction is rolled back and fails.
     *
     * This method can only be used by QueryBuilder when database rolls back
     * transaction due to deadlock or timeout.
     *
     * @return Returns an invalid query when error occurs, or the last replayed
     *         query on success.
     */
346
    QSqlQuery retryLastTransaction(bool rollbackFirst);
347

348
private Q_SLOTS:
349 350
    void sendKeepAliveQuery();

351
protected:
352
    static QThreadStorage<DataStore *> sInstances;
353

354
    QString m_connectionName;
Till Adam's avatar
Till Adam committed
355 356
    QSqlDatabase m_database;
    bool m_dbOpened;
357
    uint m_transactionLevel;
358
    QVector<QPair<QSqlQuery, bool /* isBatch */> > m_transactionQueries;
359
    QByteArray mSessionId;
Guy Maurel's avatar
Guy Maurel committed
360
    NotificationCollector *mNotificationCollector;
361
    QTimer *m_keepAliveTimer;
362
    static bool s_hasForeignKeyConstraints;
363 364 365 366

    // Gives QueryBuilder access to addQueryToTransaction() and retryLastTransaction()
    friend class QueryBuilder;

Till Adam's avatar
Till Adam committed
367 368
};

369 370 371
} // namespace Server
} // namespace Akonadi

Till Adam's avatar
Till Adam committed
372
#endif