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