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