summaryrefslogtreecommitdiffstats
path: root/common/storage/entitystore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/storage/entitystore.cpp')
-rw-r--r--common/storage/entitystore.cpp121
1 files changed, 85 insertions, 36 deletions
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp
index b7309ab..22e5ae3 100644
--- a/common/storage/entitystore.cpp
+++ b/common/storage/entitystore.cpp
@@ -36,9 +36,75 @@
36using namespace Sink; 36using namespace Sink;
37using namespace Sink::Storage; 37using namespace Sink::Storage;
38 38
39static QMap<QByteArray, int> baseDbs()
40{
41 return {{"revisionType", 0},
42 {"revisions", 0},
43 {"uids", 0},
44 {"default", 0},
45 {"__flagtable", 0}};
46}
47
48template <typename T, typename First>
49void mergeImpl(T &map, First f)
50{
51 for (auto it = f.constBegin(); it != f.constEnd(); it++) {
52 map.insert(it.key(), it.value());
53 }
54}
55
56template <typename T, typename First, typename ... Tail>
57void mergeImpl(T &map, First f, Tail ...maps)
58{
59 for (auto it = f.constBegin(); it != f.constEnd(); it++) {
60 map.insert(it.key(), it.value());
61 }
62 mergeImpl<T, Tail...>(map, maps...);
63}
64
65template <typename First, typename ... Tail>
66First merge(First f, Tail ...maps)
67{
68 First map;
69 mergeImpl(map, f, maps...);
70 return map;
71}
72
73template <class T>
74struct DbLayoutHelper {
75 void operator()(QMap<QByteArray, int> map) const {
76 mergeImpl(map, ApplicationDomain::TypeImplementation<T>::typeDatabases());
77 }
78};
79
80static Sink::Storage::DbLayout dbLayout(const QByteArray &instanceId)
81{
82 static auto databases = [] {
83 QMap<QByteArray, int> map;
84 mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Mail>::typeDatabases());
85 mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Folder>::typeDatabases());
86 mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Contact>::typeDatabases());
87 mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Addressbook>::typeDatabases());
88 mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Event>::typeDatabases());
89 return merge(baseDbs(), map);
90 }();
91 return {instanceId, databases};
92}
93
94
39class EntityStore::Private { 95class EntityStore::Private {
40public: 96public:
41 Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore")) {} 97 Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore"))
98 {
99 static bool initialized = false;
100 if (!initialized) {
101 if (QDir{}.mkpath(entityBlobStorageDir())) {
102 initialized = true;
103 } else {
104 SinkWarningCtx(logCtx) << "Failed to create the directory: " << entityBlobStorageDir();
105 }
106 }
107 }
42 108
43 ResourceContext resourceContext; 109 ResourceContext resourceContext;
44 DataStore::Transaction transaction; 110 DataStore::Transaction transaction;
@@ -56,7 +122,7 @@ public:
56 return transaction; 122 return transaction;
57 } 123 }
58 124
59 Sink::Storage::DataStore store(Sink::storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly); 125 Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(resourceContext.instanceId()), DataStore::ReadOnly);
60 transaction = store.createTransaction(DataStore::ReadOnly); 126 transaction = store.createTransaction(DataStore::ReadOnly);
61 return transaction; 127 return transaction;
62 } 128 }
@@ -93,9 +159,14 @@ public:
93 return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; 159 return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor};
94 } 160 }
95 161
162 QString entityBlobStorageDir()
163 {
164 return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob";
165 }
166
96 QString entityBlobStoragePath(const QByteArray &id) 167 QString entityBlobStoragePath(const QByteArray &id)
97 { 168 {
98 return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob/" + id; 169 return entityBlobStorageDir() +"/"+ id;
99 } 170 }
100 171
101}; 172};
@@ -110,9 +181,8 @@ void EntityStore::startTransaction(Sink::Storage::DataStore::AccessMode accessMo
110{ 181{
111 SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; 182 SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode;
112 Q_ASSERT(!d->transaction); 183 Q_ASSERT(!d->transaction);
113 Sink::Storage::DataStore store(Sink::storageLocation(), d->resourceContext.instanceId(), accessMode); 184 Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(d->resourceContext.instanceId()), accessMode);
114 d->transaction = store.createTransaction(accessMode); 185 d->transaction = store.createTransaction(accessMode);
115 Q_ASSERT(d->transaction.validateNamedDatabases());
116} 186}
117 187
118void EntityStore::commitTransaction() 188void EntityStore::commitTransaction()
@@ -138,9 +208,6 @@ bool EntityStore::hasTransaction() const
138void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) 208void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision)
139{ 209{
140 const auto directory = d->entityBlobStoragePath(entity.identifier()); 210 const auto directory = d->entityBlobStoragePath(entity.identifier());
141 if (!QDir().mkpath(directory)) {
142 SinkWarningCtx(d->logCtx) << "Failed to create the directory: " << directory;
143 }
144 211
145 for (const auto &property : entity.changedProperties()) { 212 for (const auto &property : entity.changedProperties()) {
146 const auto value = entity.getProperty(property); 213 const auto value = entity.getProperty(property);
@@ -149,7 +216,7 @@ void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qi
149 //Any blob that is not part of the storage yet has to be moved there. 216 //Any blob that is not part of the storage yet has to be moved there.
150 if (blob.isExternal) { 217 if (blob.isExternal) {
151 auto oldPath = blob.value; 218 auto oldPath = blob.value;
152 auto filePath = directory + QString("/%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); 219 auto filePath = directory + QString("_%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property));
153 //In case we hit the same revision again due to a rollback. 220 //In case we hit the same revision again due to a rollback.
154 QFile::remove(filePath); 221 QFile::remove(filePath);
155 QFile origFile(oldPath); 222 QFile origFile(oldPath);
@@ -320,6 +387,11 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision)
320{ 387{
321 const auto uid = DataStore::getUidFromRevision(d->transaction, revision); 388 const auto uid = DataStore::getUidFromRevision(d->transaction, revision);
322 const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision); 389 const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision);
390 if (bufferType.isEmpty() || uid.isEmpty()) {
391 SinkErrorCtx(d->logCtx) << "Failed to find revision during cleanup: " << revision;
392 Q_ASSERT(false);
393 return;
394 }
323 SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; 395 SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType;
324 DataStore::mainDatabase(d->transaction, bufferType) 396 DataStore::mainDatabase(d->transaction, bufferType)
325 .scan(uid, 397 .scan(uid,
@@ -337,10 +409,10 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision)
337 DataStore::mainDatabase(d->transaction, bufferType).remove(key); 409 DataStore::mainDatabase(d->transaction, bufferType).remove(key);
338 } 410 }
339 if (isRemoval) { 411 if (isRemoval) {
340 const auto directory = d->entityBlobStoragePath(uid); 412 QDir dir{d->entityBlobStorageDir()};
341 QDir dir(directory); 413 const auto infoList = dir.entryInfoList(QStringList{} << QString{uid + "*"});
342 if (!dir.removeRecursively()) { 414 for (const auto &fileInfo : infoList) {
343 SinkErrorCtx(d->logCtx) << "Failed to cleanup: " << directory; 415 QFile::remove(fileInfo.filePath());
344 } 416 }
345 } 417 }
346 //Don't cleanup more than specified 418 //Don't cleanup more than specified
@@ -614,29 +686,6 @@ qint64 EntityStore::maxRevision()
614 return DataStore::maxRevision(d->getTransaction()); 686 return DataStore::maxRevision(d->getTransaction());
615} 687}
616 688
617/* DataStore::Transaction getTransaction() */
618/* { */
619/* Sink::Storage::DataStore::Transaction transaction; */
620/* { */
621/* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */
622/* if (!storage.exists()) { */
623/* //This is not an error if the resource wasn't started before */
624/* SinkLogCtx(d->logCtx) << "Store doesn't exist: " << mResourceInstanceIdentifier; */
625/* return Sink::Storage::DataStore::Transaction(); */
626/* } */
627/* storage.setDefaultErrorHandler([this](const Sink::Storage::DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.store << error.message; }); */
628/* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */
629/* } */
630
631/* //FIXME this is a temporary measure to recover from a failure to open the named databases correctly. */
632/* //Once the actual problem is fixed it will be enough to simply crash if we open the wrong database (which we check in openDatabase already). */
633/* while (!transaction.validateNamedDatabases()) { */
634/* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */
635/* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */
636/* } */
637/* return transaction; */
638/* } */
639
640Sink::Log::Context EntityStore::logContext() const 689Sink::Log::Context EntityStore::logContext() const
641{ 690{
642 return d->logCtx; 691 return d->logCtx;