1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "settingsdatabase.h"
35 #include <QtCore/QDir>
36 #include <QtCore/QMap>
37 #include <QtCore/QString>
38 #include <QtCore/QStringList>
39 #include <QtCore/QVariant>
41 #include <QtSql/QSqlDatabase>
42 #include <QtSql/QSqlError>
43 #include <QtSql/QSqlQuery>
44 #include <QtCore/QDebug>
47 \class Core::SettingsDatabase
48 \brief An alternative to the application-wide QSettings that is more
49 suitable for storing large amounts of data.
51 The settings database is SQLite based, and lazily retrieves data when it
52 is asked for. It also does incremental updates of the database rather than
53 rewriting the whole file each time one of the settings change.
55 The SettingsDatabase API mimics that of QSettings.
59 using namespace Core::Internal;
61 enum { debug_settings = 0 };
66 typedef QMap<QString, QVariant> SettingsMap;
68 class SettingsDatabasePrivate
71 QString effectiveGroup() const
73 return m_groups.join(QString(QLatin1Char('/')));
76 QString effectiveKey(const QString &key) const
78 QString g = effectiveGroup();
79 if (!g.isEmpty() && !key.isEmpty())
80 g += QLatin1Char('/');
85 SettingsMap m_settings;
88 QStringList m_dirtyKeys;
93 } // namespace Internal
96 SettingsDatabase::SettingsDatabase(const QString &path,
97 const QString &application,
100 , d(new SettingsDatabasePrivate)
102 const QLatin1Char slash('/');
104 // TODO: Don't rely on a path, but determine automatically
106 if (!pathDir.exists())
107 pathDir.mkpath(pathDir.absolutePath());
109 QString fileName = path;
110 if (!fileName.endsWith(slash))
112 fileName += application;
113 fileName += QLatin1String(".db");
115 d->m_db = QSqlDatabase::addDatabase("QSQLITE", QLatin1String("settings"));
116 d->m_db.setDatabaseName(fileName);
117 if (!d->m_db.open()) {
118 qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
119 << d->m_db.lastError().driverText() << ")";
121 // Create the settings table if it doesn't exist yet
122 QSqlQuery query(d->m_db);
123 query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
124 "key PRIMARY KEY ON CONFLICT REPLACE, "
127 qWarning().nospace() << "Warning: Failed to prepare settings database! ("
128 << query.lastError().driverText() << ")";
130 // Retrieve all available keys (values are retrieved lazily)
131 if (query.exec(QLatin1String("SELECT key FROM settings"))) {
132 while (query.next()) {
133 d->m_settings.insert(query.value(0).toString(), QVariant());
139 SettingsDatabase::~SettingsDatabase()
144 QSqlDatabase::removeDatabase(QLatin1String("settings"));
147 void SettingsDatabase::setValue(const QString &key, const QVariant &value)
149 const QString effectiveKey = d->effectiveKey(key);
152 d->m_settings.insert(effectiveKey, value);
154 if (!d->m_db.isOpen())
157 // Instant apply (TODO: Delay writing out settings)
158 QSqlQuery query(d->m_db);
159 query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
160 query.addBindValue(effectiveKey);
161 query.addBindValue(value);
165 qDebug() << "Stored:" << effectiveKey << "=" << value;
168 QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const
170 const QString effectiveKey = d->effectiveKey(key);
171 QVariant value = defaultValue;
173 SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
174 if (i != d->m_settings.constEnd() && i.value().isValid()) {
176 } else if (d->m_db.isOpen()) {
177 // Try to read the value from the database
178 QSqlQuery query(d->m_db);
179 query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
180 query.addBindValue(effectiveKey);
183 value = query.value(0);
186 qDebug() << "Retrieved:" << effectiveKey << "=" << value;
190 d->m_settings.insert(effectiveKey, value);
196 bool SettingsDatabase::contains(const QString &key) const
198 return d->m_settings.contains(d->effectiveKey(key));
201 void SettingsDatabase::remove(const QString &key)
203 const QString effectiveKey = d->effectiveKey(key);
205 // Remove keys from the cache
206 foreach (const QString &k, d->m_settings.keys()) {
207 // Either it's an exact match, or it matches up to a /
208 if (k.startsWith(effectiveKey)
209 && (k.length() == effectiveKey.length()
210 || k.at(effectiveKey.length()) == QLatin1Char('/')))
212 d->m_settings.remove(k);
216 if (!d->m_db.isOpen())
219 // Delete keys from the database
220 QSqlQuery query(d->m_db);
221 query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
222 query.addBindValue(effectiveKey);
223 query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
227 void SettingsDatabase::beginGroup(const QString &prefix)
229 d->m_groups.append(prefix);
232 void SettingsDatabase::endGroup()
234 d->m_groups.removeLast();
237 QString SettingsDatabase::group() const
239 return d->effectiveGroup();
242 QStringList SettingsDatabase::childKeys() const
244 QStringList children;
246 const QString g = group();
247 QMapIterator<QString, QVariant> i(d->m_settings);
248 while (i.hasNext()) {
249 const QString &key = i.next().key();
250 if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1) {
251 children.append(key.mid(g.length() + 1));
258 void SettingsDatabase::sync()
260 // TODO: Delay writing of dirty keys and save them here