SQL-Queries are now stored in a resource. The bashscript
[quassel.git] / src / core / abstractsqlstorage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by the Quassel IRC Team                         *
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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "abstractsqlstorage.h"
22
23 #include <QSqlError>
24 #include <QSqlQuery>
25
26 AbstractSqlStorage::AbstractSqlStorage(QObject *parent)
27   : Storage(parent),
28     _schemaVersion(0)
29 {
30 }
31
32 AbstractSqlStorage::~AbstractSqlStorage() {
33   {
34     QSqlDatabase db = QSqlDatabase::database("quassel_connection");
35     db.commit();
36     db.close();
37   }
38   QSqlDatabase::removeDatabase("quassel_connection");  
39 }
40
41 QSqlDatabase AbstractSqlStorage::logDb() {
42   QSqlDatabase db = QSqlDatabase::database("quassel_connection");
43   if(db.isValid() && db.isOpen())
44     return db;
45
46   if(!openDb()) {
47     qWarning() << "Unable to Open Database" << engineName();
48     qWarning() << " -" << db.lastError().text();
49   }
50
51   return QSqlDatabase::database("quassel_connection");
52 }
53
54 bool AbstractSqlStorage::openDb() {
55   QSqlDatabase db = QSqlDatabase::database("quassel_connection");
56   if(db.isValid() && !db.isOpen())
57     return db.open();
58
59   db = QSqlDatabase::addDatabase(driverName(), "quassel_connection");
60   db.setDatabaseName(databaseName());
61
62   if(!hostName().isEmpty())
63     db.setHostName(hostName());
64
65   if(!userName().isEmpty()) {
66     db.setUserName(userName());
67     db.setPassword(password());
68   }
69
70   return db.open();
71 }
72
73 bool AbstractSqlStorage::init(const QVariantMap &settings) {
74   Q_UNUSED(settings)
75   QSqlDatabase db = logDb();
76   if(!db.isValid() || !db.isOpen())
77     return false;
78
79   if(installedSchemaVersion() == -1) {
80     qDebug() << "Storage Schema is missing!";
81     return false;
82   }
83
84   if(installedSchemaVersion() > schemaVersion()) {
85     qWarning() << "Installed Schema is newer then any known Version.";
86     return false;
87   }
88   
89   if(installedSchemaVersion() < schemaVersion()) {
90     qWarning() << "Installed Schema is not up to date. Upgrading...";
91     if(!upgradeDb())
92       return false;
93   }
94   
95   qDebug() << "Storage Backend is ready. Quassel Schema Version:" << installedSchemaVersion();
96   return true;
97 }
98
99 QString AbstractSqlStorage::queryString(const QString &queryName, int version) {
100   if(version == 0)
101     version = schemaVersion();
102     
103   QFileInfo queryInfo(QString(":/SQL/%1/%2/%3.sql").arg(engineName()).arg(version).arg(queryName));
104   if(!queryInfo.exists() || !queryInfo.isFile() || !queryInfo.isReadable()) {
105     qWarning() << "Unable to read SQL-Query" << queryName << "for Engine" << engineName();
106     return QString();
107   }
108
109   QFile queryFile(queryInfo.filePath());
110   if(!queryFile.open(QIODevice::ReadOnly | QIODevice::Text))
111     return QString();
112   QString query = QTextStream(&queryFile).readAll();
113   queryFile.close();
114   
115   return query.trimmed();
116 }
117
118 QString AbstractSqlStorage::queryString(const QString &queryName) {
119   return queryString(queryName, 0);
120 }
121
122 QSqlQuery *AbstractSqlStorage::cachedQuery(const QString &queryName, int version) {
123   QPair<QString, int> queryId = qMakePair(queryName, version);
124   if(!_queryCache.contains(queryId)) {
125     QSqlQuery *query = new QSqlQuery(logDb());
126     query->prepare(queryString(queryName, version));
127     _queryCache[queryId] = query;
128   }
129
130   return _queryCache[queryId];
131 }
132
133 QSqlQuery *AbstractSqlStorage::cachedQuery(const QString &queryName) {
134   return cachedQuery(queryName, 0);
135 }
136
137 QStringList AbstractSqlStorage::setupQueries() {
138   QStringList queries;
139   QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(schemaVersion()));
140   foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "setup*", QDir::NoFilter, QDir::Name)) {
141     queries << queryString(fileInfo.baseName());
142   }
143   return queries;
144 }
145
146 bool AbstractSqlStorage::setup(const QVariantMap &settings) {
147   Q_UNUSED(settings)
148   QSqlDatabase db = logDb();
149   if(!db.isOpen()) {
150     qWarning() << "Unable to setup Logging Backend!";
151     return false;
152   }
153
154   foreach(QString queryString, setupQueries()) {
155     QSqlQuery query = db.exec(queryString);
156     if(!watchQuery(&query)) {
157       qWarning() << "Unable to setup Logging Backend!";
158       return false;
159     }
160   }
161   return true;
162 }
163
164 QStringList AbstractSqlStorage::upgradeQueries(int version) {
165   QStringList queries;
166   QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(version));
167   foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "upgrade*", QDir::NoFilter, QDir::Name)) {
168     qDebug() << queryString(fileInfo.baseName());
169     queries << queryString(fileInfo.baseName());
170   }
171   return queries;
172 }
173
174 bool AbstractSqlStorage::upgradeDb() {
175   if(schemaVersion() <= installedSchemaVersion())
176     return true;
177
178   QSqlDatabase db = logDb();
179
180   for(int ver = installedSchemaVersion() + 1; ver <= schemaVersion(); ver++) {
181     foreach(QString queryString, upgradeQueries(ver)) {
182       QSqlQuery query = db.exec(queryString);
183       if(!watchQuery(&query)) {
184         qWarning() << "Unable to upgrade Logging Backend!";
185         return false;
186       }
187     }
188   }
189   return true;
190 }
191
192
193 int AbstractSqlStorage::schemaVersion() {
194   // returns the newest Schema Version!
195   // not the currently used one! (though it can be the same)
196   if(_schemaVersion > 0)
197     return _schemaVersion;
198
199   int version;
200   bool ok;
201   QDir dir = QDir(":/SQL/" + engineName());
202   foreach(QFileInfo fileInfo, dir.entryInfoList()) {
203     if(!fileInfo.isDir())
204       continue;
205
206     version = fileInfo.fileName().toInt(&ok);
207     if(!ok)
208       continue;
209
210     if(version > _schemaVersion)
211       _schemaVersion = version;
212   }
213   return _schemaVersion;
214 }
215
216 bool AbstractSqlStorage::watchQuery(QSqlQuery *query) {
217   if(query->lastError().isValid()) {
218     qWarning() << "unhandled Error in QSqlQuery!";
219     qWarning() << "                  last Query:" << query->lastQuery();
220     qWarning() << "              executed Query:" << query->executedQuery();
221     qWarning() << "                bound Values:" << query->boundValues();
222     qWarning() << "                Error Number:" << query->lastError().number();
223     qWarning() << "               Error Message:" << query->lastError().text();
224     qWarning() << "              Driver Message:" << query->lastError().driverText();
225     qWarning() << "                  DB Message:" << query->lastError().databaseText();
226     
227     return false;
228   }
229   return true;
230 }