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