2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2009-05-19 19:20:53 +02:00
|
|
|
|
|
|
|
|
#include "settingsdatabase.h"
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDir>
|
|
|
|
|
#include <QMap>
|
|
|
|
|
#include <QString>
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
#include <QVariant>
|
2009-10-01 16:38:08 +02:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QSqlDatabase>
|
|
|
|
|
#include <QSqlError>
|
|
|
|
|
#include <QSqlQuery>
|
|
|
|
|
#include <QDebug>
|
2023-09-14 17:00:11 +02:00
|
|
|
#include <QCoreApplication>
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2009-05-20 10:55:27 +02:00
|
|
|
/*!
|
2023-09-14 17:00:11 +02:00
|
|
|
\namespace Core::SettingsDatabase
|
2020-06-12 16:04:30 +02:00
|
|
|
\inheaderfile coreplugin/settingsdatabase.h
|
2020-03-18 13:32:02 +01:00
|
|
|
\inmodule QtCreator
|
2020-06-12 16:04:30 +02:00
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
\brief The SettingsDatabase namespace offers an alternative to the
|
2013-06-05 14:29:24 +02:00
|
|
|
application-wide QSettings that is more
|
2009-05-20 10:55:27 +02:00
|
|
|
suitable for storing large amounts of data.
|
|
|
|
|
|
|
|
|
|
The settings database is SQLite based, and lazily retrieves data when it
|
|
|
|
|
is asked for. It also does incremental updates of the database rather than
|
|
|
|
|
rewriting the whole file each time one of the settings change.
|
|
|
|
|
|
|
|
|
|
The SettingsDatabase API mimics that of QSettings.
|
2023-09-14 17:00:11 +02:00
|
|
|
|
|
|
|
|
\sa settings()
|
2009-05-20 10:55:27 +02:00
|
|
|
*/
|
|
|
|
|
|
2009-05-19 19:20:53 +02:00
|
|
|
using namespace Core;
|
2023-09-14 17:00:11 +02:00
|
|
|
using namespace ExtensionSystem;
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2009-05-20 17:15:59 +02:00
|
|
|
enum { debug_settings = 0 };
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
namespace Core::SettingsDatabase {
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
using SettingsMap = QMap<QString, QVariant>;
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
class SettingsDatabaseImpl
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
QString effectiveGroup() const
|
|
|
|
|
{
|
2010-02-01 12:43:56 +01:00
|
|
|
return m_groups.join(QString(QLatin1Char('/')));
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString effectiveKey(const QString &key) const
|
|
|
|
|
{
|
|
|
|
|
QString g = effectiveGroup();
|
2009-05-20 17:15:59 +02:00
|
|
|
if (!g.isEmpty() && !key.isEmpty())
|
2009-05-19 19:20:53 +02:00
|
|
|
g += QLatin1Char('/');
|
|
|
|
|
g += key;
|
|
|
|
|
return g;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SettingsMap m_settings;
|
|
|
|
|
|
|
|
|
|
QStringList m_groups;
|
|
|
|
|
QStringList m_dirtyKeys;
|
|
|
|
|
|
|
|
|
|
QSqlDatabase m_db;
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
static SettingsDatabaseImpl *d;
|
2009-05-19 19:20:53 +02:00
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void ensureImpl()
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
if (d)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
d = new SettingsDatabaseImpl;
|
|
|
|
|
|
|
|
|
|
const QString path = QFileInfo(PluginManager::settings()->fileName()).path();
|
|
|
|
|
const QString application = QCoreApplication::applicationName();
|
2009-05-19 19:20:53 +02:00
|
|
|
const QLatin1Char slash('/');
|
|
|
|
|
|
|
|
|
|
// TODO: Don't rely on a path, but determine automatically
|
2009-06-12 14:51:01 +02:00
|
|
|
QDir pathDir(path);
|
|
|
|
|
if (!pathDir.exists())
|
|
|
|
|
pathDir.mkpath(pathDir.absolutePath());
|
|
|
|
|
|
2009-05-19 19:20:53 +02:00
|
|
|
QString fileName = path;
|
|
|
|
|
if (!fileName.endsWith(slash))
|
|
|
|
|
fileName += slash;
|
|
|
|
|
fileName += application;
|
|
|
|
|
fileName += QLatin1String(".db");
|
|
|
|
|
|
2011-12-22 14:44:14 +01:00
|
|
|
d->m_db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("settings"));
|
2009-05-19 19:20:53 +02:00
|
|
|
d->m_db.setDatabaseName(fileName);
|
2009-06-05 12:40:00 +02:00
|
|
|
if (!d->m_db.open()) {
|
|
|
|
|
qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
|
|
|
|
|
<< d->m_db.lastError().driverText() << ")";
|
|
|
|
|
} else {
|
|
|
|
|
// Create the settings table if it doesn't exist yet
|
|
|
|
|
QSqlQuery query(d->m_db);
|
|
|
|
|
query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
|
|
|
|
|
"key PRIMARY KEY ON CONFLICT REPLACE, "
|
|
|
|
|
"value)"));
|
|
|
|
|
if (!query.exec())
|
|
|
|
|
qWarning().nospace() << "Warning: Failed to prepare settings database! ("
|
|
|
|
|
<< query.lastError().driverText() << ")";
|
|
|
|
|
|
|
|
|
|
// Retrieve all available keys (values are retrieved lazily)
|
|
|
|
|
if (query.exec(QLatin1String("SELECT key FROM settings"))) {
|
|
|
|
|
while (query.next()) {
|
|
|
|
|
d->m_settings.insert(query.value(0).toString(), QVariant());
|
|
|
|
|
}
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
2014-02-21 16:34:54 +01:00
|
|
|
|
|
|
|
|
// syncing can be slow, especially on Linux and Windows
|
|
|
|
|
d->m_db.exec(QLatin1String("PRAGMA synchronous = OFF;"));
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void destroy()
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
if (!d)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// TODO: Delay writing of dirty keys and save them here
|
2009-05-19 19:20:53 +02:00
|
|
|
|
|
|
|
|
delete d;
|
2023-09-14 17:00:11 +02:00
|
|
|
d = nullptr;
|
2009-05-19 19:20:53 +02:00
|
|
|
QSqlDatabase::removeDatabase(QLatin1String("settings"));
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void setValue(const QString &key, const QVariant &value)
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-19 19:20:53 +02:00
|
|
|
const QString effectiveKey = d->effectiveKey(key);
|
|
|
|
|
|
|
|
|
|
// Add to cache
|
|
|
|
|
d->m_settings.insert(effectiveKey, value);
|
|
|
|
|
|
2009-06-05 12:40:00 +02:00
|
|
|
if (!d->m_db.isOpen())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-05-19 19:20:53 +02:00
|
|
|
// Instant apply (TODO: Delay writing out settings)
|
|
|
|
|
QSqlQuery query(d->m_db);
|
|
|
|
|
query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
|
|
|
|
|
query.addBindValue(effectiveKey);
|
|
|
|
|
query.addBindValue(value);
|
|
|
|
|
query.exec();
|
|
|
|
|
|
|
|
|
|
if (debug_settings)
|
|
|
|
|
qDebug() << "Stored:" << effectiveKey << "=" << value;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
QVariant value(const QString &key, const QVariant &defaultValue)
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-19 19:20:53 +02:00
|
|
|
const QString effectiveKey = d->effectiveKey(key);
|
|
|
|
|
QVariant value = defaultValue;
|
|
|
|
|
|
|
|
|
|
SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
|
|
|
|
|
if (i != d->m_settings.constEnd() && i.value().isValid()) {
|
|
|
|
|
value = i.value();
|
2009-06-05 12:40:00 +02:00
|
|
|
} else if (d->m_db.isOpen()) {
|
2009-05-19 19:20:53 +02:00
|
|
|
// Try to read the value from the database
|
|
|
|
|
QSqlQuery query(d->m_db);
|
|
|
|
|
query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
|
|
|
|
|
query.addBindValue(effectiveKey);
|
|
|
|
|
query.exec();
|
|
|
|
|
if (query.next()) {
|
|
|
|
|
value = query.value(0);
|
|
|
|
|
|
|
|
|
|
if (debug_settings)
|
|
|
|
|
qDebug() << "Retrieved:" << effectiveKey << "=" << value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cache the result
|
|
|
|
|
d->m_settings.insert(effectiveKey, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
bool contains(const QString &key)
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2021-01-26 16:31:12 +01:00
|
|
|
// check exact key
|
|
|
|
|
// this already caches the value
|
|
|
|
|
if (value(key).isValid())
|
|
|
|
|
return true;
|
|
|
|
|
// check for group
|
|
|
|
|
if (d->m_db.isOpen()) {
|
|
|
|
|
const QString glob = d->effectiveKey(key) + "/?*";
|
|
|
|
|
QSqlQuery query(d->m_db);
|
|
|
|
|
query.prepare(
|
|
|
|
|
QLatin1String("SELECT value FROM settings WHERE key GLOB '%1' LIMIT 1").arg(glob));
|
|
|
|
|
query.exec();
|
|
|
|
|
if (query.next())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void remove(const QString &key)
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-20 17:15:59 +02:00
|
|
|
const QString effectiveKey = d->effectiveKey(key);
|
|
|
|
|
|
|
|
|
|
// Remove keys from the cache
|
2024-01-08 23:47:36 +01:00
|
|
|
for (auto it = d->m_settings.cbegin(); it != d->m_settings.cend(); ) {
|
2009-05-20 17:15:59 +02:00
|
|
|
// Either it's an exact match, or it matches up to a /
|
2024-01-08 23:47:36 +01:00
|
|
|
const QString k = it.key();
|
2009-05-20 17:15:59 +02:00
|
|
|
if (k.startsWith(effectiveKey)
|
|
|
|
|
&& (k.length() == effectiveKey.length()
|
2024-01-08 23:47:36 +01:00
|
|
|
|| k.at(effectiveKey.length()) == QLatin1Char('/'))) {
|
|
|
|
|
it = d->m_settings.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
++it;
|
2009-05-20 17:15:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-06-05 12:40:00 +02:00
|
|
|
|
|
|
|
|
if (!d->m_db.isOpen())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Delete keys from the database
|
|
|
|
|
QSqlQuery query(d->m_db);
|
|
|
|
|
query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
|
|
|
|
|
query.addBindValue(effectiveKey);
|
2009-06-08 15:54:05 +02:00
|
|
|
query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
|
2009-06-05 12:40:00 +02:00
|
|
|
query.exec();
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void beginGroup(const QString &prefix)
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-19 19:20:53 +02:00
|
|
|
d->m_groups.append(prefix);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void endGroup()
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-19 19:20:53 +02:00
|
|
|
d->m_groups.removeLast();
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
QString group()
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2009-05-19 19:20:53 +02:00
|
|
|
return d->effectiveGroup();
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
QStringList childKeys()
|
2009-05-19 19:20:53 +02:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2010-01-11 10:22:55 +01:00
|
|
|
QStringList children;
|
2009-05-19 19:20:53 +02:00
|
|
|
|
|
|
|
|
const QString g = group();
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto i = d->m_settings.cbegin(), end = d->m_settings.cend(); i != end; ++i) {
|
|
|
|
|
const QString &key = i.key();
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1)
|
2010-01-11 10:22:55 +01:00
|
|
|
children.append(key.mid(g.length() + 1));
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-11 10:22:55 +01:00
|
|
|
return children;
|
2009-05-19 19:20:53 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void beginTransaction()
|
2014-02-21 15:43:49 +01:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2014-02-21 15:43:49 +01:00
|
|
|
if (!d->m_db.isOpen())
|
|
|
|
|
return;
|
2015-12-15 09:51:17 +02:00
|
|
|
d->m_db.transaction();
|
2014-02-21 15:43:49 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
void endTransaction()
|
2014-02-21 15:43:49 +01:00
|
|
|
{
|
2023-09-14 17:00:11 +02:00
|
|
|
ensureImpl();
|
2014-02-21 15:43:49 +01:00
|
|
|
if (!d->m_db.isOpen())
|
|
|
|
|
return;
|
2015-12-15 09:51:17 +02:00
|
|
|
d->m_db.commit();
|
2014-02-21 15:43:49 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:00:11 +02:00
|
|
|
} // Core::SettingsDatabase
|