File indexing completed on 2024-05-12 05:26:05
0001 /* 0002 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org> 0003 * Copyright (C) 2014 Christian Mollekopf <mollekopf@kolabsys.com> 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Lesser General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2.1 of the License, or (at your option) version 3, or any 0009 * later version accepted by the membership of KDE e.V. (or its 0010 * successor approved by the membership of KDE e.V.), which shall 0011 * act as a proxy defined in Section 6 of version 3 of the license. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Lesser General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Lesser General Public 0019 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 0020 */ 0021 0022 #include "storage.h" 0023 0024 #include "log.h" 0025 #include "utils.h" 0026 0027 QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error) 0028 { 0029 dbg << error.message << "Code: " << error.code << "Db: " << error.store; 0030 return dbg; 0031 } 0032 0033 namespace Sink { 0034 namespace Storage { 0035 0036 QMap<QByteArray, int> DataStore::baseDbs() 0037 { 0038 return {{"revisionType", Storage::IntegerKeys}, 0039 {"revisions", Storage::IntegerKeys}, 0040 {"uidsToRevisions", Storage::AllowDuplicates | Storage::IntegerValues}, 0041 {"default", 0}, 0042 {"__metadata", 0}, 0043 {"__flagtable", 0}}; 0044 } 0045 0046 DbLayout::DbLayout() 0047 { 0048 0049 } 0050 0051 DbLayout::DbLayout(const QByteArray &n, const Databases &t) 0052 : name(n), 0053 tables(t) 0054 { 0055 0056 } 0057 0058 void errorHandler(const DataStore::Error &error) 0059 { 0060 if (error.code == DataStore::TransactionError) { 0061 SinkError() << "Transaction error:" << error; 0062 } else { 0063 SinkWarning() << "Database error:" << error; 0064 } 0065 } 0066 0067 std::function<void(const DataStore::Error &error)> DataStore::basicErrorHandler() 0068 { 0069 return errorHandler; 0070 } 0071 0072 void DataStore::setDefaultErrorHandler(const std::function<void(const DataStore::Error &error)> &errorHandler) 0073 { 0074 mErrorHandler = errorHandler; 0075 } 0076 0077 std::function<void(const DataStore::Error &error)> DataStore::defaultErrorHandler() const 0078 { 0079 if (mErrorHandler) { 0080 return mErrorHandler; 0081 } 0082 return basicErrorHandler(); 0083 } 0084 0085 void DataStore::setMaxRevision(DataStore::Transaction &transaction, qint64 revision) 0086 { 0087 transaction.openDatabase("__metadata").write("maxRevision", QByteArray::number(revision)); 0088 } 0089 0090 qint64 DataStore::maxRevision(const DataStore::Transaction &transaction) 0091 { 0092 qint64 r = 0; 0093 transaction.openDatabase("__metadata").scan("maxRevision", 0094 [&](const QByteArray &, const QByteArray &revision) -> bool { 0095 r = revision.toLongLong(); 0096 return false; 0097 }, 0098 [](const Error &error) { 0099 if (error.code != DataStore::NotFound) { 0100 SinkWarning() << "Couldn't find the maximum revision: " << error; 0101 } 0102 }); 0103 return r; 0104 } 0105 0106 void DataStore::setCleanedUpRevision(DataStore::Transaction &transaction, qint64 revision) 0107 { 0108 transaction.openDatabase("__metadata").write("cleanedUpRevision", QByteArray::number(revision)); 0109 } 0110 0111 qint64 DataStore::cleanedUpRevision(const DataStore::Transaction &transaction) 0112 { 0113 qint64 r = 0; 0114 transaction.openDatabase("__metadata").scan("cleanedUpRevision", 0115 [&](const QByteArray &, const QByteArray &revision) -> bool { 0116 r = revision.toLongLong(); 0117 return false; 0118 }, 0119 [](const Error &error) { 0120 if (error.code != DataStore::NotFound) { 0121 SinkWarning() << "Couldn't find the cleanedUpRevision: " << error; 0122 } 0123 }); 0124 return r; 0125 } 0126 0127 Identifier DataStore::getUidFromRevision(const DataStore::Transaction &transaction, size_t revision) 0128 { 0129 QByteArray uid; 0130 transaction 0131 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys) 0132 .scan(revision, 0133 [&](const size_t, const QByteArray &value) -> bool { 0134 uid = QByteArray{ value.constData(), value.size() }; 0135 return false; 0136 }, 0137 [revision, &transaction](const Error &error) { 0138 //This can fail if we attempt to replay from a removed revision that has been cleaned up already. 0139 SinkError() << "Couldn't find uid for revision: " << revision << error.message; 0140 SinkTrace() << "Cleaned up revision: " << cleanedUpRevision(transaction); 0141 }); 0142 Q_ASSERT(!uid.isEmpty()); 0143 return Identifier::fromInternalByteArray(uid); 0144 } 0145 0146 size_t DataStore::getLatestRevisionFromUid(DataStore::Transaction &t, const Identifier &uid) 0147 { 0148 size_t revision = 0; 0149 t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues) 0150 .findLast(uid.toInternalByteArray(), [&revision](const QByteArray &key, const QByteArray &value) { 0151 revision = byteArrayToSizeT(value); 0152 }, 0153 [](const Error &error) { 0154 //This is expected if we attempt to lookup an id that doesn't exist. 0155 if (error.code != DataStore::NotFound) { 0156 SinkWarning() << "Error during getLatestRevisionFromUid: " << error; 0157 } 0158 }); 0159 0160 return revision; 0161 } 0162 0163 QList<size_t> DataStore::getRevisionsUntilFromUid(DataStore::Transaction &t, const Identifier &uid, size_t lastRevision) 0164 { 0165 QList<size_t> queriedRevisions; 0166 t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues) 0167 .scan(uid.toInternalByteArray(), [&queriedRevisions, lastRevision](const QByteArray &, const QByteArray &value) { 0168 const size_t currentRevision = byteArrayToSizeT(value); 0169 if (currentRevision < lastRevision) { 0170 queriedRevisions << currentRevision; 0171 return true; 0172 } 0173 0174 return false; 0175 }); 0176 0177 return queriedRevisions; 0178 } 0179 0180 QList<size_t> DataStore::getRevisionsFromUid(DataStore::Transaction &t, const Identifier &uid) 0181 { 0182 return getRevisionsUntilFromUid(t, uid, SIZE_MAX); 0183 } 0184 0185 QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transaction, size_t revision) 0186 { 0187 QByteArray type; 0188 transaction.openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) 0189 .scan(revision, 0190 [&](const size_t, const QByteArray &value) -> bool { 0191 type = QByteArray{value.constData(), value.size()}; 0192 return false; 0193 }, 0194 [revision](const Error &error) { SinkWarning() << "Couldn't find type for revision " << revision; }); 0195 Q_ASSERT(!type.isEmpty()); 0196 return type; 0197 } 0198 0199 void DataStore::recordRevision(DataStore::Transaction &transaction, size_t revision, 0200 const Identifier &uid, const QByteArray &type) 0201 { 0202 const auto uidBa = uid.toInternalByteArray(); 0203 transaction 0204 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys) 0205 .write(revision, uidBa); 0206 transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues) 0207 .write(uidBa, sizeTToByteArray(revision)); 0208 transaction 0209 .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) 0210 .write(revision, type); 0211 } 0212 0213 void DataStore::removeRevision(DataStore::Transaction &transaction, size_t revision) 0214 { 0215 const auto uid = getUidFromRevision(transaction, revision); 0216 0217 transaction 0218 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys) 0219 .remove(revision); 0220 transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues) 0221 .remove(uid.toInternalByteArray(), sizeTToByteArray(revision)); 0222 transaction 0223 .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) 0224 .remove(revision); 0225 } 0226 0227 void DataStore::recordUid(DataStore::Transaction &transaction, const Identifier &uid, const QByteArray &type) 0228 { 0229 transaction.openDatabase(type + "uids", {}, IntegerKeys).write(uid.toInternalByteArray(), ""); 0230 } 0231 0232 void DataStore::removeUid(DataStore::Transaction &transaction, const Identifier &uid, const QByteArray &type) 0233 { 0234 transaction.openDatabase(type + "uids", {}, IntegerKeys).remove(uid.toInternalByteArray()); 0235 } 0236 0237 void DataStore::getUids(const QByteArray &type, const Transaction &transaction, const std::function<void(const Identifier &uid)> &callback) 0238 { 0239 transaction.openDatabase(type + "uids", {}, IntegerKeys).scan("", [&] (const QByteArray &key, const QByteArray &) { 0240 callback(Identifier::fromInternalByteArray(key)); 0241 return true; 0242 }); 0243 } 0244 0245 bool DataStore::hasUid(const QByteArray &type, const Transaction &transaction, const Identifier &uid) 0246 { 0247 bool hasTheUid = false; 0248 transaction.openDatabase(type + "uids", {}, IntegerKeys).scan(uid.toInternalByteArray(), [&](const QByteArray &key, const QByteArray &) { 0249 Q_ASSERT(uid.toInternalByteArray() == key); 0250 hasTheUid = true; 0251 return false; 0252 }); 0253 0254 return hasTheUid; 0255 } 0256 0257 QByteArray DataStore::generateUid() 0258 { 0259 return createUuid(); 0260 } 0261 0262 DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type) 0263 { 0264 if (type.isEmpty()) { 0265 SinkError() << "Tried to open main database for empty type."; 0266 Q_ASSERT(false); 0267 return {}; 0268 } 0269 return t.openDatabase(type + ".main", /* errorHandler= */ {}, IntegerKeys); 0270 } 0271 0272 bool DataStore::NamedDatabase::contains(const QByteArray &uid) 0273 { 0274 bool found = false; 0275 scan(uid, 0276 [&found](const QByteArray &, const QByteArray &) -> bool { 0277 found = true; 0278 return false; 0279 }, 0280 [](const DataStore::Error &error) {}, true); 0281 return found; 0282 } 0283 0284 void DataStore::setDatabaseVersion(DataStore::Transaction &transaction, qint64 revision) 0285 { 0286 transaction.openDatabase("__metadata").write("databaseVersion", QByteArray::number(revision)); 0287 } 0288 0289 qint64 DataStore::databaseVersion(const DataStore::Transaction &transaction) 0290 { 0291 qint64 r = 0; 0292 transaction.openDatabase("__metadata").scan("databaseVersion", 0293 [&](const QByteArray &, const QByteArray &revision) -> bool { 0294 r = revision.toLongLong(); 0295 return false; 0296 }, 0297 [](const Error &error) { 0298 if (error.code != DataStore::NotFound) { 0299 SinkWarning() << "Couldn't find the database version: " << error; 0300 } 0301 }); 0302 return r; 0303 } 0304 0305 0306 } 0307 } // namespace Sink