qa: Replace Qt module includes by class ones
[quassel.git] / src / core / abstractsqlstorage.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #pragma once
22
23 #include <memory>
24
25 #include <QHash>
26 #include <QMutex>
27 #include <QSqlDatabase>
28 #include <QSqlError>
29 #include <QSqlQuery>
30
31 #include "storage.h"
32
33 class QThread;
34
35 class AbstractSqlMigrationReader;
36 class AbstractSqlMigrationWriter;
37
38 class AbstractSqlStorage : public Storage
39 {
40     Q_OBJECT
41
42 public:
43     AbstractSqlStorage(QObject* parent = nullptr);
44     ~AbstractSqlStorage() override;
45
46     virtual std::unique_ptr<AbstractSqlMigrationReader> createMigrationReader() { return {}; }
47     virtual std::unique_ptr<AbstractSqlMigrationWriter> createMigrationWriter() { return {}; }
48
49     /**
50      * An SQL query with associated resource filename
51      */
52     struct SqlQueryResource {
53         QString queryString;   ///< SQL query string
54         QString queryFilename; ///< Path to the resource file providing this query
55
56         SqlQueryResource(const QString& queryString, const QString& queryFilename)
57             : queryString(std::move(queryString)),
58               queryFilename(std::move(queryFilename)) {}
59     };
60
61 public slots:
62     State init(const QVariantMap& settings = QVariantMap(),
63                const QProcessEnvironment& environment = {},
64                bool loadFromEnvironment = false) override;
65     bool setup(const QVariantMap& settings = QVariantMap(),
66                const QProcessEnvironment& environment = {},
67                bool loadFromEnvironment = false) override;
68
69 protected:
70     inline void sync() override{};
71
72     QSqlDatabase logDb();
73
74     /**
75      * Fetch an SQL query string by name and optional schema version
76      *
77      * Loads the named SQL query from the built-in SQL resource collection, returning it as a
78      * string.  If a version is specified, it'll be loaded from the schema version-specific folder
79      * instead.
80      *
81      * @see schemaVersion()
82      *
83      * @param[in] queryName  File name of the SQL query, minus the .sql extension
84      * @param[in] version
85      * @parblock
86      * SQL schema version; if 0, fetches from current version, otherwise loads from the specified
87      * schema version instead of the current schema files.
88      * @endparblock
89      * @return String with the requested SQL query, ready for parameter substitution
90      */
91     QString queryString(const QString& queryName, int version = 0);
92
93     /**
94      * Gets the collection of SQL setup queries and filenames to create a new database
95      *
96      * @return List of SQL query strings and filenames
97      */
98     QList<SqlQueryResource> setupQueries();
99
100     /**
101      * Gets the collection of SQL upgrade queries and filenames for a given schema version
102      *
103      * @param ver  SQL schema version
104      * @return List of SQL query strings and filenames
105      */
106     QList<SqlQueryResource> upgradeQueries(int ver);
107     bool upgradeDb();
108
109     bool watchQuery(QSqlQuery& query);
110
111     int schemaVersion();
112     virtual int installedSchemaVersion() { return -1; };
113
114     /**
115      * Update the stored schema version number, optionally clearing the record of mid-schema steps
116      *
117      * @param newVersion        New schema version number
118      * @param clearUpgradeStep  If true, clear the record of any in-progress schema upgrades
119      * @return
120      */
121     virtual bool updateSchemaVersion(int newVersion, bool clearUpgradeStep = true) = 0;
122
123     virtual bool setupSchemaVersion(int version) = 0;
124
125     /**
126      * Gets the last successful schema upgrade step, or an empty string if no upgrade is in progress
127      *
128      * @return Filename of last successful schema upgrade query, or empty string if not upgrading
129      */
130     virtual QString schemaVersionUpgradeStep();
131
132     /**
133      * Sets the last successful schema upgrade step
134      *
135      * @param upgradeQuery  The filename of the last successful schema upgrade query
136      * @return True if successfully set, otherwise false
137      */
138     virtual bool setSchemaVersionUpgradeStep(QString upgradeQuery) = 0;
139
140     virtual void setConnectionProperties(const QVariantMap& properties, const QProcessEnvironment& environment, bool loadFromEnvironment) = 0;
141     virtual QString driverName() = 0;
142     inline virtual QString hostName() { return QString(); }
143     inline virtual int port() { return -1; }
144     virtual QString databaseName() = 0;
145     inline virtual QString userName() { return QString(); }
146     inline virtual QString password() { return QString(); }
147
148     //! Initialize db specific features on connect
149     /** This is called every time a connection to a specific SQL backend is established
150      *  the default implementation does nothing.
151      *
152      *  When reimplementing this method, don't use logDB() inside this function as
153      *  this would cause as we're just about to initialize that DB connection.
154      */
155     inline virtual bool initDbSession(QSqlDatabase& /* db */) { return true; }
156
157 private slots:
158     void connectionDestroyed();
159
160 private:
161     void addConnectionToPool();
162     void dbConnect(QSqlDatabase& db);
163
164     int _schemaVersion{0};
165     bool _debug;
166
167     static int _nextConnectionId;
168     QMutex _connectionPoolMutex;
169     // we let a Connection Object manage each actual db connection
170     // those objects reside in the thread the connection belongs to
171     // which allows us thread safe termination of a connection
172     class Connection;
173     QHash<QThread*, Connection*> _connectionPool;
174 };
175
176 struct SenderData
177 {
178     QString sender;
179     QString realname;
180     QString avatarurl;
181
182     friend uint qHash(const SenderData& key);
183     friend bool operator==(const SenderData& a, const SenderData& b);
184 };
185
186 // ========================================
187 //  AbstractSqlStorage::Connection
188 // ========================================
189 class AbstractSqlStorage::Connection : public QObject
190 {
191     Q_OBJECT
192
193 public:
194     Connection(const QString& name, QObject* parent = nullptr);
195     ~Connection() override;
196
197     inline QLatin1String name() const { return QLatin1String(_name); }
198
199 private:
200     QByteArray _name;
201 };
202
203 // ========================================
204 //  AbstractSqlMigrator
205 // ========================================
206 class AbstractSqlMigrator
207 {
208 public:
209     // migration objects
210     struct QuasselUserMO
211     {
212         UserId id;
213         QString username;
214         QString password;
215         int hashversion;
216         QString authenticator;
217     };
218
219     struct SenderMO
220     {
221         qint64 senderId{0};
222         QString sender;
223         QString realname;
224         QString avatarurl;
225     };
226
227     struct IdentityMO
228     {
229         IdentityId id;
230         UserId userid;
231         QString identityname;
232         QString realname;
233         QString awayNick;
234         bool awayNickEnabled;
235         QString awayReason;
236         bool awayReasonEnabled;
237         bool autoAwayEnabled;
238         int autoAwayTime;
239         QString autoAwayReason;
240         bool autoAwayReasonEnabled;
241         bool detachAwayEnabled;
242         QString detachAwayReason;
243         bool detachAwayReasonEnabled;
244         QString ident;
245         QString kickReason;
246         QString partReason;
247         QString quitReason;
248         QByteArray sslCert;
249         QByteArray sslKey;
250     };
251
252     struct IdentityNickMO
253     {
254         int nickid;
255         IdentityId identityId;
256         QString nick;
257     };
258
259     struct NetworkMO
260     {
261         UserId userid;
262         QString networkname;
263         QString perform;
264         QString autoidentifyservice;
265         QString autoidentifypassword;
266         QString saslaccount;
267         QString saslpassword;
268         QString servercodec;
269         QString encodingcodec;
270         QString decodingcodec;
271         QString usermode;
272         QString awaymessage;
273         QString attachperform;
274         QString detachperform;
275         NetworkId networkid;
276         IdentityId identityid;
277         int messagerateburstsize;
278         int messageratedelay;
279         int autoreconnectinterval;
280         int autoreconnectretries;
281         bool rejoinchannels;
282         bool userandomserver;
283         bool useautoidentify;
284         bool usesasl;
285         bool useautoreconnect;
286         bool unlimitedconnectretries;
287         bool usecustommessagerate;
288         bool unlimitedmessagerate;
289         bool connected;
290     };
291
292     struct BufferMO
293     {
294         BufferId bufferid;
295         UserId userid;
296         int groupid;
297         NetworkId networkid;
298         QString buffername;
299         QString buffercname;
300         int buffertype;
301         qint64 lastmsgid;
302         qint64 lastseenmsgid;
303         qint64 markerlinemsgid;
304         int bufferactivity;
305         int highlightcount;
306         QString key;
307         bool joined;
308         QString cipher;
309     };
310
311     struct BacklogMO
312     {
313         MsgId messageid;
314         QDateTime time;  // has to be in UTC!
315         BufferId bufferid;
316         int type;
317         int flags;
318         qint64 senderid;
319         QString senderprefixes;
320         QString message;
321     };
322
323     struct IrcServerMO
324     {
325         int serverid;
326         UserId userid;
327         NetworkId networkid;
328         QString hostname;
329         int port;
330         QString password;
331         bool ssl;
332         bool sslverify;  /// If true, validate SSL certificates
333         int sslversion;
334         bool useproxy;
335         int proxytype;
336         QString proxyhost;
337         int proxyport;
338         QString proxyuser;
339         QString proxypass;
340     };
341
342     struct UserSettingMO
343     {
344         UserId userid;
345         QString settingname;
346         QByteArray settingvalue;
347     };
348
349     struct CoreStateMO
350     {
351         QString key;
352         QByteArray value;
353     };
354
355     enum MigrationObject
356     {
357         QuasselUser,
358         Sender,
359         Identity,
360         IdentityNick,
361         Network,
362         Buffer,
363         Backlog,
364         IrcServer,
365         UserSetting,
366         CoreState
367     };
368
369     virtual ~AbstractSqlMigrator() = default;
370
371     static QString migrationObject(MigrationObject moType);
372
373 protected:
374     void newQuery(const QString& query, QSqlDatabase db);
375     virtual void resetQuery();
376     virtual bool prepareQuery(MigrationObject mo) = 0;
377     bool exec();
378     inline bool next() { return _query->next(); }
379     inline QVariant value(int index) { return _query->value(index); }
380     inline void bindValue(const QString& placeholder, const QVariant& val) { _query->bindValue(placeholder, val); }
381     inline void bindValue(int pos, const QVariant& val) { _query->bindValue(pos, val); }
382
383     inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
384     void dumpStatus();
385     inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
386     inline QVariantList boundValues();
387
388     virtual bool transaction() = 0;
389     virtual void rollback() = 0;
390     virtual bool commit() = 0;
391
392 private:
393     QSqlQuery* _query{nullptr};
394 };
395
396 class AbstractSqlMigrationReader : public AbstractSqlMigrator
397 {
398 public:
399     AbstractSqlMigrationReader();
400
401     virtual bool readMo(QuasselUserMO& user) = 0;
402     virtual bool readMo(IdentityMO& identity) = 0;
403     virtual bool readMo(IdentityNickMO& identityNick) = 0;
404     virtual bool readMo(NetworkMO& network) = 0;
405     virtual bool readMo(BufferMO& buffer) = 0;
406     virtual bool readMo(SenderMO& sender) = 0;
407     virtual bool readMo(BacklogMO& backlog) = 0;
408     virtual bool readMo(IrcServerMO& ircserver) = 0;
409     virtual bool readMo(UserSettingMO& userSetting) = 0;
410     virtual bool readMo(CoreStateMO& coreState) = 0;
411
412     bool migrateTo(AbstractSqlMigrationWriter* writer);
413
414 private:
415     void abortMigration(const QString& errorMsg = QString());
416     bool finalizeMigration();
417
418     template<typename T>
419     bool transferMo(MigrationObject moType, T& mo);
420
421     AbstractSqlMigrationWriter* _writer{nullptr};
422 };
423
424 class AbstractSqlMigrationWriter : public AbstractSqlMigrator
425 {
426 public:
427     virtual bool writeMo(const QuasselUserMO& user) = 0;
428     virtual bool writeMo(const IdentityMO& identity) = 0;
429     virtual bool writeMo(const IdentityNickMO& identityNick) = 0;
430     virtual bool writeMo(const NetworkMO& network) = 0;
431     virtual bool writeMo(const BufferMO& buffer) = 0;
432     virtual bool writeMo(const SenderMO& sender) = 0;
433     virtual bool writeMo(const BacklogMO& backlog) = 0;
434     virtual bool writeMo(const IrcServerMO& ircserver) = 0;
435     virtual bool writeMo(const UserSettingMO& userSetting) = 0;
436     virtual bool writeMo(const CoreStateMO& coreState) = 0;
437
438     inline bool migrateFrom(AbstractSqlMigrationReader* reader) { return reader->migrateTo(this); }
439
440     // called after migration process
441     virtual inline bool postProcess() { return true; }
442     friend class AbstractSqlMigrationReader;
443 };