Plasma5Support

storagethread.cpp
1/*
2 SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "storagethread_p.h"
8
9#include <QCoreApplication>
10#include <QDataStream>
11#include <QDir>
12#include <QSqlDriver>
13#include <QSqlError>
14#include <QSqlField>
15#include <QSqlQuery>
16#include <QSqlRecord>
17
18#include "debug_p.h"
19#include <QDebug>
20#include <QStandardPaths>
21
22namespace Plasma5Support
23{
24class StorageThreadSingleton
25{
26public:
27 StorageThreadSingleton()
28 {
29 }
30
31 StorageThread self;
32};
33
34Q_GLOBAL_STATIC(StorageThreadSingleton, privateStorageThreadSelf)
35
36static void closeConnection()
37{
38 StorageThread::self()->closeDb();
39 StorageThread::self()->quit();
40}
41
42StorageThread::StorageThread(QObject *parent)
43 : QThread(parent)
44{
45 qAddPostRoutine(closeConnection);
46}
47
48StorageThread::~StorageThread()
49{
50}
51
52Plasma5Support::StorageThread *StorageThread::self()
53{
54 return &privateStorageThreadSelf()->self;
55}
56
57void StorageThread::closeDb()
58{
59 QString name = m_db.connectionName();
61 m_db = QSqlDatabase();
62}
63
64void StorageThread::initializeDb(StorageJob *caller)
65{
66 if (!m_db.open()) {
67 m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("plasma-storage-%1").arg((quintptr)this));
69 QDir().mkpath(storageDir);
70 m_db.setDatabaseName(storageDir + QLatin1Char('/') + QStringLiteral("plasma-storage2.db"));
71 }
72
73 if (!m_db.open()) {
74 qCWarning(LOG_PLASMA5SUPPORT) << "Unable to open the plasma storage cache database: " << m_db.lastError();
75 } else if (!m_db.tables().contains(caller->clientName())) {
76 QSqlQuery query(m_db);
77 query.prepare(QStringLiteral("create table ") + caller->clientName()
78 + QStringLiteral(" (valueGroup varchar(256), id varchar(256), txt TEXT, int INTEGER, float REAL, binary BLOB, creationTime datetime, "
79 "accessTime datetime, primary key (valueGroup, id))"));
80 if (!query.exec()) {
81 qCWarning(LOG_PLASMA5SUPPORT) << "Unable to create table for" << caller->clientName();
82 m_db.close();
83 }
84 }
85 m_db.transaction();
86}
87
88void StorageThread::save(QPointer<StorageJob> wcaller, const QVariantMap &params)
89{
90 StorageJob *caller = wcaller.data();
91 if (!caller) {
92 return;
93 }
94
95 initializeDb(caller);
96 QString valueGroup = params[QStringLiteral("group")].toString();
97 if (valueGroup.isEmpty()) {
98 valueGroup = QStringLiteral("default");
99 }
100 QSqlQuery query(m_db);
101
102 QVariantMap data = caller->data();
103 if (params.value(QStringLiteral("key")).toString().isNull()) {
104 data.insert(params.value(QStringLiteral("key")).toString(), params.value(QStringLiteral("data")));
105 }
106 caller->setData(data);
107
108 QMapIterator<QString, QVariant> it(caller->data());
109
110 QString ids;
111 while (it.hasNext()) {
112 it.next();
113 QSqlField field(QStringLiteral(":id"), QMetaType(QMetaType::QString));
114 field.setValue(it.key());
115 if (!ids.isEmpty()) {
116 ids.append(QStringLiteral(", "));
117 }
118 ids.append(m_db.driver()->formatValue(field));
119 }
120
121 query.prepare(QStringLiteral("delete from ") + caller->clientName() + QStringLiteral(" where valueGroup = :valueGroup and id in (") + ids
122 + QStringLiteral(");"));
123 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
124
125 if (!query.exec()) {
126 m_db.commit();
127 Q_EMIT newResult(caller, false);
128 return;
129 }
130
131 query.prepare(QStringLiteral("insert into ") + caller->clientName()
132 + QStringLiteral(" values(:valueGroup, :id, :txt, :int, :float, :binary, date('now'), date('now'))"));
133 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
134 query.bindValue(QStringLiteral(":txt"), QVariant());
135 query.bindValue(QStringLiteral(":int"), QVariant());
136 query.bindValue(QStringLiteral(":float"), QVariant());
137 query.bindValue(QStringLiteral(":binary"), QVariant());
138
139 const QString key = params.value(QStringLiteral("key")).toString();
140 if (!key.isEmpty()) {
141 QVariantMap data = caller->data();
142 data.insert(key, params[QStringLiteral("data")]);
143 caller->setData(data);
144 }
145
146 it.toFront();
147 while (it.hasNext()) {
148 it.next();
149 // qCDebug(LOG_PLASMA) << "going to insert" << valueGroup << it.key();
150 query.bindValue(QStringLiteral(":id"), it.key());
151
152 QString field;
153 bool binary = false;
154 switch (it.value().userType()) {
156 field = QStringLiteral(":txt");
157 break;
158 case QMetaType::Int:
159 field = QStringLiteral(":int");
160 break;
162 field = QStringLiteral(":float");
163 break;
165 binary = true;
166 field = QStringLiteral(":binary");
167 break;
168 default:
169 continue;
170 }
171
172 if (binary) {
173 QByteArray b;
175 ds << it.value();
176 query.bindValue(field, b);
177 } else {
178 query.bindValue(field, it.value());
179 }
180
181 if (!query.exec()) {
182 // qCDebug(LOG_PLASMA) << "query failed:" << query.lastQuery() << query.lastError().text();
183 m_db.commit();
184 Q_EMIT newResult(caller, false);
185 return;
186 }
187
188 query.bindValue(field, QVariant());
189 }
190 m_db.commit();
191
192 Q_EMIT newResult(caller, true);
193}
194
195void StorageThread::retrieve(QPointer<StorageJob> wcaller, const QVariantMap &params)
196{
197 StorageJob *caller = wcaller.data();
198 if (!caller) {
199 return;
200 }
201
202 const QString clientName = caller->clientName();
203 initializeDb(caller);
204 QString valueGroup = params[QStringLiteral("group")].toString();
205 if (valueGroup.isEmpty()) {
206 valueGroup = QStringLiteral("default");
207 }
208
209 QSqlQuery query(m_db);
210
211 // a bit redundant but should be the faster way with less string concatenation as possible
212 if (params[QStringLiteral("key")].toString().isEmpty()) {
213 // update modification time
214 query.prepare(QStringLiteral("update ") + clientName + QStringLiteral(" set accessTime=date('now') where valueGroup=:valueGroup"));
215 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
216 query.exec();
217
218 query.prepare(QStringLiteral("select * from ") + clientName + QStringLiteral(" where valueGroup=:valueGroup"));
219 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
220 } else {
221 // update modification time
222 query.prepare(QStringLiteral("update ") + clientName + QStringLiteral(" set accessTime=date('now') where valueGroup=:valueGroup and id=:key"));
223 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
224 query.bindValue(QStringLiteral(":key"), params[QStringLiteral("key")].toString());
225 query.exec();
226
227 query.prepare(QStringLiteral("select * from ") + clientName + QStringLiteral(" where valueGroup=:valueGroup and id=:key"));
228 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
229 query.bindValue(QStringLiteral(":key"), params[QStringLiteral("key")].toString());
230 }
231
232 const bool success = query.exec();
233
234 QVariant result;
235
236 if (success) {
237 QSqlRecord rec = query.record();
238 const int keyColumn = rec.indexOf(QLatin1String("id"));
239 const int textColumn = rec.indexOf(QLatin1String("txt"));
240 const int intColumn = rec.indexOf(QLatin1String("int"));
241 const int floatColumn = rec.indexOf(QLatin1String("float"));
242 const int binaryColumn = rec.indexOf(QLatin1String("binary"));
243
244 QVariantMap data;
245 while (query.next()) {
246 const QString key = query.value(keyColumn).toString();
247 if (!query.value(textColumn).isNull()) {
248 data.insert(key, query.value(textColumn));
249 } else if (!query.value(intColumn).isNull()) {
250 data.insert(key, query.value(intColumn));
251 } else if (!query.value(floatColumn).isNull()) {
252 data.insert(key, query.value(floatColumn));
253 } else if (!query.value(binaryColumn).isNull()) {
254 QByteArray bytes = query.value(binaryColumn).toByteArray();
255 QDataStream in(bytes);
256 QVariant v;
257 in >> v;
258 data.insert(key, v);
259 }
260 }
261 result = data;
262 } else {
263 result = false;
264 }
265
266 Q_EMIT newResult(caller, result);
267}
268
269void StorageThread::deleteEntry(QPointer<StorageJob> wcaller, const QVariantMap &params)
270{
271 StorageJob *caller = wcaller.data();
272 if (!caller) {
273 return;
274 }
275
276 initializeDb(caller);
277 QString valueGroup = params[QStringLiteral("group")].toString();
278 if (valueGroup.isEmpty()) {
279 valueGroup = QStringLiteral("default");
280 }
281
282 QSqlQuery query(m_db);
283
284 if (params[QStringLiteral("key")].toString().isEmpty()) {
285 query.prepare(QStringLiteral("delete from ") + caller->clientName() + QStringLiteral(" where valueGroup=:valueGroup"));
286 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
287 } else {
288 query.prepare(QStringLiteral("delete from ") + caller->clientName() + QStringLiteral(" where valueGroup=:valueGroup and id=:key"));
289 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
290 query.bindValue(QStringLiteral(":key"), params[QStringLiteral("key")].toString());
291 }
292
293 const bool success = query.exec();
294 m_db.commit();
295
296 Q_EMIT newResult(caller, success);
297}
298
299void StorageThread::expire(QPointer<StorageJob> wcaller, const QVariantMap &params)
300{
301 StorageJob *caller = wcaller.data();
302 if (!caller) {
303 return;
304 }
305
306 initializeDb(caller);
307 QString valueGroup = params[QStringLiteral("group")].toString();
308 if (valueGroup.isEmpty()) {
309 valueGroup = QStringLiteral("default");
310 }
311
312 QSqlQuery query(m_db);
313 if (valueGroup.isEmpty()) {
314 query.prepare(QStringLiteral("delete from ") + caller->clientName() + QStringLiteral(" where accessTime < :date"));
315 QDateTime time(QDateTime::currentDateTime().addSecs(-params[QStringLiteral("age")].toUInt()));
316 query.bindValue(QStringLiteral(":date"), time.toSecsSinceEpoch());
317 } else {
318 query.prepare(QStringLiteral("delete from ") + caller->clientName() + QStringLiteral(" where valueGroup=:valueGroup and accessTime < :date"));
319 query.bindValue(QStringLiteral(":valueGroup"), valueGroup);
320 QDateTime time(QDateTime::currentDateTime().addSecs(-params[QStringLiteral("age")].toUInt()));
321 query.bindValue(QStringLiteral(":date"), time.toSecsSinceEpoch());
322 }
323
324 const bool success = query.exec();
325
326 Q_EMIT newResult(caller, success);
327}
328
329void StorageThread::run()
330{
331 exec();
332}
333
334}
335
336#include "moc_storagethread_p.cpp"
std::optional< QSqlQuery > query(const QString &queryStatement)
char * toString(const EngineQuery &query)
QString name(StandardAction id)
Namespace for everything in libplasma.
Definition datamodel.cpp:15
QDateTime currentDateTime()
bool mkpath(const QString &dirPath) const const
T * data() const const
QSqlDatabase addDatabase(QSqlDriver *driver, const QString &connectionName)
void removeDatabase(const QString &connectionName)
int indexOf(const QString &name) const const
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:59:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.