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