Akonadi

dbconfigsqlite.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "dbconfigsqlite.h"
8#include "akonadiserver_debug.h"
9#include "utils.h"
10
11#include "private/standarddirs_p.h"
12
13#include <QDir>
14#include <QSqlDriver>
15#include <QSqlError>
16#include <QSqlQuery>
17
18using namespace Akonadi;
19using namespace Akonadi::Server;
20
21static QString dataDir(const QString &dbPathOverride = {})
22{
23 QString akonadiHomeDir = dbPathOverride.isEmpty() ? StandardDirs::saveDir("data") : dbPathOverride;
24 if (!QDir(akonadiHomeDir).exists()) {
25 if (!QDir().mkpath(akonadiHomeDir)) {
26 qCCritical(AKONADISERVER_LOG) << "Unable to create" << akonadiHomeDir << "during database initialization";
27 return QString();
28 }
29 }
30
31 akonadiHomeDir += QStringLiteral("/");
32
33 return akonadiHomeDir;
34}
35
36static QString sqliteDataFile(const QString &dbPathOverride = {})
37{
38 const QString dir = dataDir(dbPathOverride);
39 if (dir.isEmpty()) {
40 return QString();
41 }
42 const QString akonadiPath = dir + QLatin1StringView("akonadi.db");
43 if (!QFile::exists(akonadiPath)) {
44 QFile file(akonadiPath);
45 if (!file.open(QIODevice::WriteOnly)) {
46 qCCritical(AKONADISERVER_LOG) << "Unable to create file" << akonadiPath << "during database initialization.";
47 return QString();
48 }
49 file.close();
50 }
51
52 return akonadiPath;
53}
54
55DbConfigSqlite::DbConfigSqlite(const QString &configFile)
56 : DbConfig(configFile)
57{
58}
59
60QString DbConfigSqlite::driverName() const
61{
62 return QStringLiteral("QSQLITE");
63}
64
65QString DbConfigSqlite::databaseName() const
66{
67 return mDatabaseName;
68}
69
70QString DbConfigSqlite::databasePath() const
71{
72 return mDatabaseName;
73}
74
75void DbConfigSqlite::setDatabasePath(const QString &path, QSettings &settings)
76{
77 mDatabaseName = path;
78 settings.beginGroup(driverName());
79 settings.setValue(QStringLiteral("Name"), mDatabaseName);
80 settings.endGroup();
81}
82
83bool DbConfigSqlite::init(QSettings &settings, bool storeSettings, const QString &dbPathOverride)
84{
85 // determine default settings depending on the driver
86 const QString defaultDbName = sqliteDataFile(dbPathOverride);
87 if (defaultDbName.isEmpty()) {
88 return false;
89 }
90
91 // read settings for current driver
92 settings.beginGroup(driverName());
93 mDatabaseName = settings.value(QStringLiteral("Name"), defaultDbName).toString();
94 mHostName = settings.value(QStringLiteral("Host")).toString();
95 mUserName = settings.value(QStringLiteral("User")).toString();
96 mPassword = settings.value(QStringLiteral("Password")).toString();
97 mConnectionOptions = settings.value(QStringLiteral("Options")).toString();
98 settings.endGroup();
99
100 if (storeSettings) {
101 // store back the default values
102 settings.beginGroup(driverName());
103 settings.setValue(QStringLiteral("Name"), mDatabaseName);
104 settings.endGroup();
105 settings.sync();
106 }
107
108 return true;
109}
110
111bool DbConfigSqlite::isAvailable(QSettings &settings)
112{
113 if (!QSqlDatabase::drivers().contains(driverName())) {
114 return false;
115 }
116
117 if (!init(settings, false)) {
118 return false;
119 }
120
121 return true;
122}
123
124void DbConfigSqlite::apply(QSqlDatabase &database)
125{
126 if (!mDatabaseName.isEmpty()) {
127 database.setDatabaseName(mDatabaseName);
128 }
129 if (!mHostName.isEmpty()) {
130 database.setHostName(mHostName);
131 }
132 if (!mUserName.isEmpty()) {
133 database.setUserName(mUserName);
134 }
135 if (!mPassword.isEmpty()) {
136 database.setPassword(mPassword);
137 }
138
139 database.setConnectOptions(mConnectionOptions);
140
141 // can we check that during init() already?
142 Q_ASSERT(database.driver()->hasFeature(QSqlDriver::LastInsertId));
143}
144
145bool DbConfigSqlite::useInternalServer() const
146{
147 return false;
148}
149
150bool DbConfigSqlite::setPragma(QSqlDatabase &db, QSqlQuery &query, const QString &pragma)
151{
152 if (!query.exec(QStringLiteral("PRAGMA %1").arg(pragma))) {
153 qCCritical(AKONADISERVER_LOG) << "Could not set sqlite PRAGMA " << pragma;
154 qCCritical(AKONADISERVER_LOG) << "Database: " << mDatabaseName;
155 qCCritical(AKONADISERVER_LOG) << "Query error: " << query.lastError().text();
156 qCCritical(AKONADISERVER_LOG) << "Database error: " << db.lastError().text();
157 return false;
158 }
159 return true;
160}
161
162void DbConfigSqlite::setup()
163{
164 const QLatin1StringView connectionName("initConnectionSqlite");
165
166 {
167 QSqlDatabase db = QSqlDatabase::addDatabase(driverName(), connectionName);
168
169 if (!db.isValid()) {
170 qCCritical(AKONADISERVER_LOG) << "Invalid database for" << mDatabaseName << "with driver" << driverName();
171 return;
172 }
173
174 QFileInfo finfo(mDatabaseName);
175 if (!finfo.dir().exists()) {
176 QDir dir;
177 dir.mkpath(finfo.path());
178 }
179
180#ifdef Q_OS_LINUX
181 QFile dbFile(mDatabaseName);
182 // It is recommended to disable CoW feature when running on Btrfs to improve
183 // database performance. It does not have any effect on non-empty files, so
184 // we check, whether the database has not yet been initialized.
185 if (dbFile.size() == 0) {
186 if (Utils::getDirectoryFileSystem(mDatabaseName) == QLatin1StringView("btrfs")) {
187 Utils::disableCoW(mDatabaseName);
188 }
189 }
190#endif
191
192 db.setDatabaseName(mDatabaseName);
193 if (!db.open()) {
194 qCCritical(AKONADISERVER_LOG) << "Could not open sqlite database" << mDatabaseName << "with driver" << driverName() << "for initialization";
195 db.close();
196 return;
197 }
198
199 apply(db);
200
201 QSqlQuery query(db);
202 if (!query.exec(QStringLiteral("SELECT sqlite_version()"))) {
203 qCCritical(AKONADISERVER_LOG) << "Could not query sqlite version";
204 qCCritical(AKONADISERVER_LOG) << "Database: " << mDatabaseName;
205 qCCritical(AKONADISERVER_LOG) << "Query error: " << query.lastError().text();
206 qCCritical(AKONADISERVER_LOG) << "Database error: " << db.lastError().text();
207 db.close();
208 return;
209 }
210
211 if (!query.next()) { // should never occur
212 qCCritical(AKONADISERVER_LOG) << "Could not query sqlite version";
213 qCCritical(AKONADISERVER_LOG) << "Database: " << mDatabaseName;
214 qCCritical(AKONADISERVER_LOG) << "Query error: " << query.lastError().text();
215 qCCritical(AKONADISERVER_LOG) << "Database error: " << db.lastError().text();
216 db.close();
217 return;
218 }
219
220 const QString sqliteVersion = query.value(0).toString();
221 qCDebug(AKONADISERVER_LOG) << "sqlite version is " << sqliteVersion;
222
223 const QStringList list = sqliteVersion.split(QLatin1Char('.'));
224 const int sqliteVersionMajor = list[0].toInt();
225 const int sqliteVersionMinor = list[1].toInt();
226
227 // set synchronous mode to NORMAL; see http://www.sqlite.org/pragma.html#pragma_synchronous
228 if (!setPragma(db, query, QStringLiteral("synchronous=1"))) {
229 db.close();
230 return;
231 }
232
233 if (sqliteVersionMajor < 3 && sqliteVersionMinor < 7) {
234 // wal mode is only supported with >= sqlite 3.7.0
235 db.close();
236 return;
237 }
238
239 // set write-ahead-log mode; see http://www.sqlite.org/wal.html
240 if (!setPragma(db, query, QStringLiteral("journal_mode=wal"))) {
241 db.close();
242 return;
243 }
244
245 if (!query.next()) { // should never occur
246 qCCritical(AKONADISERVER_LOG) << "Could not query sqlite journal mode";
247 qCCritical(AKONADISERVER_LOG) << "Database: " << mDatabaseName;
248 qCCritical(AKONADISERVER_LOG) << "Query error: " << query.lastError().text();
249 qCCritical(AKONADISERVER_LOG) << "Database error: " << db.lastError().text();
250 db.close();
251 return;
252 }
253
254 const QString journalMode = query.value(0).toString();
255 qCDebug(AKONADISERVER_LOG) << "sqlite journal mode is " << journalMode;
256
257 // as of sqlite 3.12 this is default, previously was 1024.
258 if (!setPragma(db, query, QStringLiteral("page_size=4096"))) {
259 db.close();
260 return;
261 }
262
263 // set cache_size to 100000 pages; see https://www.sqlite.org/pragma.html#pragma_cache_size
264 if (!setPragma(db, query, QStringLiteral("cache_size=100000"))) {
265 db.close();
266 return;
267 }
268
269 // construct temporary tables in memory; see https://www.sqlite.org/pragma.html#pragma_temp_store
270 if (!setPragma(db, query, QStringLiteral("temp_store=MEMORY"))) {
271 db.close();
272 return;
273 }
274
275 // enable foreign key support; see https://www.sqlite.org/pragma.html#pragma_foreign_keys
276 if (!setPragma(db, query, QStringLiteral("foreign_keys=ON"))) {
277 db.close();
278 return;
279 }
280
281 db.close();
282 }
283
284 QSqlDatabase::removeDatabase(connectionName);
285}
286
287bool DbConfigSqlite::disableConstraintChecks(const QSqlDatabase &db)
288{
289 QSqlQuery query(db);
290 return query.exec(QStringLiteral("PRAGMA ignore_check_constraints=ON"));
291}
292
293bool DbConfigSqlite::enableConstraintChecks(const QSqlDatabase &db)
294{
295 QSqlQuery query(db);
296 return query.exec(QStringLiteral("PRAGMA ignore_check_constraints=OFF"));
297}
A base class that provides an unique access layer to configuration and initialization of different da...
Definition dbconfig.h:21
Helper integration between Akonadi and Qt.
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool exists() const const
T value(qsizetype i) const const
void beginGroup(QAnyStringView prefix)
void endGroup()
void setValue(QAnyStringView key, const QVariant &value)
void sync()
QVariant value(QAnyStringView key) const const
QSqlDatabase addDatabase(QSqlDriver *driver, const QString &connectionName)
QSqlDriver * driver() const const
QStringList drivers()
bool isValid() const const
QSqlError lastError() const const
void removeDatabase(const QString &connectionName)
void setConnectOptions(const QString &options)
void setDatabaseName(const QString &name)
void setHostName(const QString &host)
void setPassword(const QString &password)
void setUserName(const QString &name)
virtual bool hasFeature(DriverFeature feature) const const=0
QString text() const const
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.