core: Replace QList by std::vector in the storage API
[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 #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         NetworkId networkid;
277         IdentityId identityid;
278         int messagerateburstsize;
279         int messageratedelay;
280         int autoreconnectinterval;
281         int autoreconnectretries;
282         bool rejoinchannels;
283         bool userandomserver;
284         bool useautoidentify;
285         bool usesasl;
286         bool useautoreconnect;
287         bool unlimitedconnectretries;
288         bool usecustommessagerate;
289         bool unlimitedmessagerate;
290         bool connected;
291     };
292
293     struct BufferMO
294     {
295         BufferId bufferid;
296         UserId userid;
297         int groupid;
298         NetworkId networkid;
299         QString buffername;
300         QString buffercname;
301         int buffertype;
302         qint64 lastmsgid;
303         qint64 lastseenmsgid;
304         qint64 markerlinemsgid;
305         int bufferactivity;
306         int highlightcount;
307         QString key;
308         bool joined;
309         QString cipher;
310     };
311
312     struct BacklogMO
313     {
314         MsgId messageid;
315         QDateTime time;  // has to be in UTC!
316         BufferId bufferid;
317         int type;
318         int flags;
319         qint64 senderid;
320         QString senderprefixes;
321         QString message;
322     };
323
324     struct IrcServerMO
325     {
326         int serverid;
327         UserId userid;
328         NetworkId networkid;
329         QString hostname;
330         int port;
331         QString password;
332         bool ssl;
333         bool sslverify;  /// If true, validate SSL certificates
334         int sslversion;
335         bool useproxy;
336         int proxytype;
337         QString proxyhost;
338         int proxyport;
339         QString proxyuser;
340         QString proxypass;
341     };
342
343     struct UserSettingMO
344     {
345         UserId userid;
346         QString settingname;
347         QByteArray settingvalue;
348     };
349
350     struct CoreStateMO
351     {
352         QString key;
353         QByteArray value;
354     };
355
356     enum MigrationObject
357     {
358         QuasselUser,
359         Sender,
360         Identity,
361         IdentityNick,
362         Network,
363         Buffer,
364         Backlog,
365         IrcServer,
366         UserSetting,
367         CoreState
368     };
369
370     virtual ~AbstractSqlMigrator() = default;
371
372     static QString migrationObject(MigrationObject moType);
373
374 protected:
375     void newQuery(const QString& query, QSqlDatabase db);
376     virtual void resetQuery();
377     virtual bool prepareQuery(MigrationObject mo) = 0;
378     bool exec();
379     inline bool next() { return _query->next(); }
380     inline QVariant value(int index) { return _query->value(index); }
381     inline void bindValue(const QString& placeholder, const QVariant& val) { _query->bindValue(placeholder, val); }
382     inline void bindValue(int pos, const QVariant& val) { _query->bindValue(pos, val); }
383
384     inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
385     void dumpStatus();
386     inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
387     inline QVariantList boundValues();
388
389     virtual bool transaction() = 0;
390     virtual void rollback() = 0;
391     virtual bool commit() = 0;
392
393 private:
394     QSqlQuery* _query{nullptr};
395 };
396
397 class AbstractSqlMigrationReader : public AbstractSqlMigrator
398 {
399 public:
400     AbstractSqlMigrationReader();
401
402     virtual bool readMo(QuasselUserMO& user) = 0;
403     virtual bool readMo(IdentityMO& identity) = 0;
404     virtual bool readMo(IdentityNickMO& identityNick) = 0;
405     virtual bool readMo(NetworkMO& network) = 0;
406     virtual bool readMo(BufferMO& buffer) = 0;
407     virtual bool readMo(SenderMO& sender) = 0;
408     virtual bool readMo(BacklogMO& backlog) = 0;
409     virtual bool readMo(IrcServerMO& ircserver) = 0;
410     virtual bool readMo(UserSettingMO& userSetting) = 0;
411     virtual bool readMo(CoreStateMO& coreState) = 0;
412
413     bool migrateTo(AbstractSqlMigrationWriter* writer);
414
415 private:
416     void abortMigration(const QString& errorMsg = QString());
417     bool finalizeMigration();
418
419     template<typename T>
420     bool transferMo(MigrationObject moType, T& mo);
421
422     AbstractSqlMigrationWriter* _writer{nullptr};
423 };
424
425 class AbstractSqlMigrationWriter : public AbstractSqlMigrator
426 {
427 public:
428     virtual bool writeMo(const QuasselUserMO& user) = 0;
429     virtual bool writeMo(const IdentityMO& identity) = 0;
430     virtual bool writeMo(const IdentityNickMO& identityNick) = 0;
431     virtual bool writeMo(const NetworkMO& network) = 0;
432     virtual bool writeMo(const BufferMO& buffer) = 0;
433     virtual bool writeMo(const SenderMO& sender) = 0;
434     virtual bool writeMo(const BacklogMO& backlog) = 0;
435     virtual bool writeMo(const IrcServerMO& ircserver) = 0;
436     virtual bool writeMo(const UserSettingMO& userSetting) = 0;
437     virtual bool writeMo(const CoreStateMO& coreState) = 0;
438
439     inline bool migrateFrom(AbstractSqlMigrationReader* reader) { return reader->migrateTo(this); }
440
441     // called after migration process
442     virtual inline bool postProcess() { return true; }
443     friend class AbstractSqlMigrationReader;
444 };