Akonadi

parthelper.cpp
1/***************************************************************************
2 * SPDX-FileCopyrightText: 2009 Andras Mantia <amantia@kde.org> *
3 * SPDX-FileCopyrightText: 2010 Volker Krause <vkrause@kde.org> *
4 * *
5 * SPDX-License-Identifier: LGPL-2.0-or-later *
6 ***************************************************************************/
7
8#include "parthelper.h"
9#include "dbconfig.h"
10#include "parttypehelper.h"
11#include "selectquerybuilder.h"
12
13#include "private/externalpartstorage_p.h"
14
15#include <QFile>
16
17#include "akonadiserver_debug.h"
18
19using namespace Akonadi;
20using namespace Akonadi::Server;
21
22void PartHelper::update(Part *part, const QByteArray &data, qint64 dataSize)
23{
24 if (!part) {
25 throw PartHelperException("Invalid part");
26 }
27
28 const bool storeExternal = dataSize > DbConfig::configuredDatabase()->sizeThreshold();
29
30 QByteArray newFile;
31 if (part->storage() == Part::External && storeExternal) {
32 if (!ExternalPartStorage::self()->updatePartFile(data, part->data(), newFile)) {
33 throw PartHelperException(QStringLiteral("Failed to update external payload part"));
34 }
35 part->setData(newFile);
36 } else if (part->storage() != Part::External && storeExternal) {
37 if (!ExternalPartStorage::self()->createPartFile(data, part->id(), newFile)) {
38 throw PartHelperException(QStringLiteral("Failed to create external payload part"));
39 }
40 part->setData(newFile);
41 part->setStorage(Part::External);
42 } else {
43 if (part->storage() == Part::External && !storeExternal) {
44 const QString file = ExternalPartStorage::resolveAbsolutePath(part->data());
45 ExternalPartStorage::self()->removePartFile(file);
46 }
47 part->setData(data);
48 part->setStorage(Part::Internal);
49 }
50
51 part->setDatasize(dataSize);
52 const bool result = part->update();
53 if (!result) {
54 throw PartHelperException("Failed to update database record");
55 }
56}
57
58bool PartHelper::insert(Part *part, qint64 *insertId)
59{
60 if (!part) {
61 return false;
62 }
63
64 const bool storeInFile = part->datasize() > DbConfig::configuredDatabase()->sizeThreshold();
65 // it is needed to insert first the metadata so a new id is generated for the part,
66 // and we need this id for the payload file name
67 QByteArray data;
68 if (storeInFile) {
69 data = part->data();
70 part->setData(QByteArray());
71 part->setStorage(Part::External);
72 } else {
73 part->setStorage(Part::Internal);
74 }
75
76 bool result = part->insert(insertId);
77
78 if (storeInFile && result) {
79 QByteArray filename;
80 if (!ExternalPartStorage::self()->createPartFile(data, part->id(), filename)) {
81 throw PartHelperException("Failed to create external payload part");
82 }
83 part->setData(filename);
84 result = part->update();
85 }
86
87 return result;
88}
89
90bool PartHelper::remove(Part *part)
91{
92 if (!part) {
93 return false;
94 }
95
96 if (part->storage() == Part::External) {
97 ExternalPartStorage::self()->removePartFile(ExternalPartStorage::resolveAbsolutePath(part->data()));
98 }
99 return part->remove();
100}
101
102bool PartHelper::remove(const QString &column, const QVariant &value)
103{
105 builder.addValueCondition(column, Query::Equals, value);
106 builder.addValueCondition(Part::storageColumn(), Query::Equals, Part::External);
107 builder.addValueCondition(Part::dataColumn(), Query::IsNot, QVariant());
108 if (!builder.exec()) {
109 // qCDebug(AKONADISERVER_LOG) << "Error selecting records to be deleted from table"
110 // << Part::tableName() << builder.query().lastError().text();
111 return false;
112 }
113 const Part::List parts = builder.result();
114 Part::List::ConstIterator it = parts.constBegin();
115 Part::List::ConstIterator end = parts.constEnd();
116 for (; it != end; ++it) {
117 ExternalPartStorage::self()->removePartFile(ExternalPartStorage::resolveAbsolutePath((*it).data()));
118 }
119 return Part::remove(column, value);
120}
121
122QByteArray PartHelper::translateData(const QByteArray &data, Part::Storage storage)
123{
124 if (storage == Part::External || storage == Part::Foreign) {
125 QFile file;
126 if (storage == Part::External) {
127 file.setFileName(ExternalPartStorage::resolveAbsolutePath(data));
128 } else {
129 file.setFileName(QString::fromUtf8(data));
130 }
131
132 if (file.open(QIODevice::ReadOnly)) {
133 const QByteArray payload = file.readAll();
134 file.close();
135 return payload;
136 } else {
137 qCCritical(AKONADISERVER_LOG) << "Payload file " << file.fileName() << " could not be open for reading!";
138 qCCritical(AKONADISERVER_LOG) << "Error: " << file.errorString();
139 return QByteArray();
140 }
141 } else {
142 // not external
143 return data;
144 }
145}
146
148{
149 return translateData(part.data(), part.storage());
150}
151
152bool PartHelper::truncate(Part &part)
153{
154 if (part.storage() == Part::External) {
155 ExternalPartStorage::self()->removePartFile(ExternalPartStorage::resolveAbsolutePath(part.data()));
156 }
157
158 part.setData(QByteArray());
159 part.setDatasize(0);
160 part.setStorage(Part::Internal);
161 return part.update();
162}
163
164bool PartHelper::verify(Part &part)
165{
166 if (part.storage() == Part::Internal) {
167 return true;
168 }
169
170 QString fileName;
171 if (part.storage() == Part::External) {
172 fileName = ExternalPartStorage::resolveAbsolutePath(part.data());
173 } else if (part.storage() == Part::Foreign) {
174 fileName = QString::fromUtf8(part.data());
175 } else {
176 Q_ASSERT(false);
177 }
178
179 if (!QFile::exists(fileName)) {
180 qCCritical(AKONADISERVER_LOG) << "Payload file" << fileName << "is missing, trying to recover.";
181 part.setData(QByteArray());
182 part.setDatasize(0);
183 part.setStorage(Part::Internal);
184 return part.update();
185 }
186
187 return true;
188}
virtual qint64 sizeThreshold() const
Payload data bigger than this value will be stored in separate files, instead of the database.
Definition dbconfig.cpp:138
static DbConfig * configuredDatabase()
Returns the DbConfig instance for the database the user has configured.
Definition dbconfig.cpp:77
void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, ConditionType type=WhereCondition)
Add a WHERE or HAVING condition which compares a column with a given value.
bool exec()
Executes the query, returns true on success.
Helper class for creating and executing database SELECT queries.
QList< T > result()
Returns the result of this SELECT query.
bool insert(Part *part, qint64 *insertId=nullptr)
Adds a new part to the database and if necessary to the filesystem.
void update(Part *part, const QByteArray &data, qint64 dataSize)
Update payload of an existing part part to data and size dataSize.
bool truncate(Part &part)
Truncate the payload of part and update filesystem/database accordingly.
QByteArray translateData(const QByteArray &data, Part::Storage storageType)
Returns the payload data.
bool remove(Part *part)
Deletes part from the database and also removes existing filesystem data if needed.
bool verify(Part &part)
Verifies and if necessary fixes the external reference of this part.
Helper integration between Akonadi and Qt.
char * data()
QByteArray & insert(qsizetype i, QByteArrayView data)
bool exists() const const
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QString errorString() const const
QByteArray readAll()
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:31:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.