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