summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/bufferadaptor.h3
-rw-r--r--common/bufferutils.h2
-rw-r--r--common/changereplay.cpp57
-rw-r--r--common/changereplay.h3
-rw-r--r--common/commands.cpp22
-rw-r--r--common/commands.h8
-rw-r--r--common/commands/notification.fbs2
-rw-r--r--common/configstore.cpp2
-rw-r--r--common/contactpreprocessor.cpp2
-rw-r--r--common/datastorequery.cpp41
-rw-r--r--common/datastorequery.h1
-rw-r--r--common/definitions.cpp41
-rw-r--r--common/definitions.h1
-rw-r--r--common/domain/applicationdomaintype.cpp3
-rw-r--r--common/domain/applicationdomaintype.h1
-rw-r--r--common/domain/contact.fbs1
-rw-r--r--common/domain/typeimplementations.cpp193
-rw-r--r--common/domain/typeimplementations.h25
-rw-r--r--common/domain/typeimplementations_p.h154
-rw-r--r--common/domainadaptor.h63
-rw-r--r--common/facade.cpp1
-rw-r--r--common/facade.h1
-rw-r--r--common/genericresource.h2
-rw-r--r--common/listener.cpp3
-rw-r--r--common/listener.h2
-rw-r--r--common/log.cpp87
-rw-r--r--common/log.h43
-rw-r--r--common/mail/threadindexer.cpp84
-rw-r--r--common/mail/threadindexer.h1
-rw-r--r--common/mailpreprocessor.cpp2
-rw-r--r--common/messagequeue.cpp2
-rw-r--r--common/modelresult.cpp1
-rw-r--r--common/notification.cpp4
-rw-r--r--common/notification.h2
-rw-r--r--common/pipeline.cpp10
-rw-r--r--common/propertymapper.cpp29
-rw-r--r--common/propertymapper.h164
-rw-r--r--common/queryrunner.cpp6
-rw-r--r--common/queryrunner.h2
-rw-r--r--common/resourceaccess.cpp4
-rw-r--r--common/resourceaccess.h2
-rw-r--r--common/resourcecontrol.cpp2
-rw-r--r--common/resourcefacade.cpp53
-rw-r--r--common/resourcefacade.h18
-rw-r--r--common/specialpurposepreprocessor.cpp2
-rw-r--r--common/storage.h11
-rw-r--r--common/storage/entitystore.cpp121
-rw-r--r--common/storage_common.cpp23
-rw-r--r--common/storage_lmdb.cpp153
-rw-r--r--common/store.cpp15
-rw-r--r--common/store.h12
-rw-r--r--common/synchronizer.cpp47
-rw-r--r--common/synchronizer.h12
-rw-r--r--common/synchronizerstore.cpp2
-rw-r--r--common/test.cpp3
-rw-r--r--common/typeindex.cpp40
-rw-r--r--common/typeindex.h9
57 files changed, 941 insertions, 659 deletions
diff --git a/common/bufferadaptor.h b/common/bufferadaptor.h
index fd4809b..f393388 100644
--- a/common/bufferadaptor.h
+++ b/common/bufferadaptor.h
@@ -66,8 +66,7 @@ public:
66 virtual QVariant getProperty(const QByteArray &key) const 66 virtual QVariant getProperty(const QByteArray &key) const
67 { 67 {
68 if (!mValues.contains(key)) { 68 if (!mValues.contains(key)) {
69 qWarning() << "Tried to read value that is not available; Did you forget to call Query::request? Property: " << key; 69 return {};
70 return QVariant{};
71 } 70 }
72 return mValues.value(key); 71 return mValues.value(key);
73 } 72 }
diff --git a/common/bufferutils.h b/common/bufferutils.h
index 6763ced..f0460b7 100644
--- a/common/bufferutils.h
+++ b/common/bufferutils.h
@@ -38,7 +38,7 @@ static flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::
38{ 38{
39 std::vector<flatbuffers::Offset<flatbuffers::String>> modifiedPropertiesList; 39 std::vector<flatbuffers::Offset<flatbuffers::String>> modifiedPropertiesList;
40 for (const auto &change : list) { 40 for (const auto &change : list) {
41 auto s = fbb.CreateString(change); 41 auto s = fbb.CreateString(change.toStdString());
42 modifiedPropertiesList.push_back(s); 42 modifiedPropertiesList.push_back(s);
43 } 43 }
44 return fbb.CreateVector(modifiedPropertiesList); 44 return fbb.CreateVector(modifiedPropertiesList);
diff --git a/common/changereplay.cpp b/common/changereplay.cpp
index 7895b66..0adbd78 100644
--- a/common/changereplay.cpp
+++ b/common/changereplay.cpp
@@ -30,8 +30,7 @@ using namespace Sink;
30using namespace Sink::Storage; 30using namespace Sink::Storage;
31 31
32ChangeReplay::ChangeReplay(const ResourceContext &resourceContext, const Sink::Log::Context &ctx) 32ChangeReplay::ChangeReplay(const ResourceContext &resourceContext, const Sink::Log::Context &ctx)
33 : mStorage(storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly), mChangeReplayStore(storageLocation(), resourceContext.instanceId() + ".changereplay", DataStore::ReadWrite), mReplayInProgress(false), mLogCtx{ctx.subContext("changereplay")}, 33 : mStorage(storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly), mChangeReplayStore(storageLocation(), resourceContext.instanceId() + ".changereplay", DataStore::ReadWrite), mReplayInProgress(false), mLogCtx{ctx.subContext("changereplay")}
34 mGuard{new QObject}
35{ 34{
36} 35}
37 36
@@ -84,6 +83,8 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
84 auto replayStoreTransaction = mChangeReplayStore.createTransaction(DataStore::ReadOnly, [this](const DataStore::Error &error) { 83 auto replayStoreTransaction = mChangeReplayStore.createTransaction(DataStore::ReadOnly, [this](const DataStore::Error &error) {
85 SinkWarningCtx(mLogCtx) << error.message; 84 SinkWarningCtx(mLogCtx) << error.message;
86 }); 85 });
86 Q_ASSERT(mMainStoreTransaction);
87 Q_ASSERT(replayStoreTransaction);
87 replayStoreTransaction.openDatabase().scan("lastReplayedRevision", 88 replayStoreTransaction.openDatabase().scan("lastReplayedRevision",
88 [lastReplayedRevision](const QByteArray &key, const QByteArray &value) -> bool { 89 [lastReplayedRevision](const QByteArray &key, const QByteArray &value) -> bool {
89 *lastReplayedRevision = value.toLongLong(); 90 *lastReplayedRevision = value.toLongLong();
@@ -98,16 +99,13 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
98 SinkTraceCtx(mLogCtx) << "Changereplay from " << *lastReplayedRevision << " to " << *topRevision; 99 SinkTraceCtx(mLogCtx) << "Changereplay from " << *lastReplayedRevision << " to " << *topRevision;
99 return KAsync::doWhile( 100 return KAsync::doWhile(
100 [this, lastReplayedRevision, topRevision]() -> KAsync::Job<KAsync::ControlFlowFlag> { 101 [this, lastReplayedRevision, topRevision]() -> KAsync::Job<KAsync::ControlFlowFlag> {
101 if (!mGuard) {
102 SinkTraceCtx(mLogCtx) << "Exit due to guard";
103 return KAsync::value(KAsync::Break);
104 }
105 if (*lastReplayedRevision >= *topRevision) { 102 if (*lastReplayedRevision >= *topRevision) {
106 SinkTraceCtx(mLogCtx) << "Done replaying" << *lastReplayedRevision << *topRevision; 103 SinkTraceCtx(mLogCtx) << "Done replaying" << *lastReplayedRevision << *topRevision;
107 return KAsync::value(KAsync::Break); 104 return KAsync::value(KAsync::Break);
108 } 105 }
106 Q_ASSERT(mMainStoreTransaction);
109 107
110 KAsync::Job<void> replayJob = KAsync::null<void>(); 108 auto replayJob = KAsync::null();
111 qint64 revision = *lastReplayedRevision + 1; 109 qint64 revision = *lastReplayedRevision + 1;
112 while (revision <= *topRevision) { 110 while (revision <= *topRevision) {
113 const auto uid = DataStore::getUidFromRevision(mMainStoreTransaction, revision); 111 const auto uid = DataStore::getUidFromRevision(mMainStoreTransaction, revision);
@@ -131,14 +129,14 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
131 if (canReplay(type, key, entityBuffer)) { 129 if (canReplay(type, key, entityBuffer)) {
132 SinkTraceCtx(mLogCtx) << "Replaying " << key; 130 SinkTraceCtx(mLogCtx) << "Replaying " << key;
133 replayJob = replay(type, key, entityBuffer); 131 replayJob = replay(type, key, entityBuffer);
132 //Set the last revision we tried to replay
133 *lastReplayedRevision = revision;
134 //Execute replay job and commit
135 break;
134 } else { 136 } else {
135 SinkTraceCtx(mLogCtx) << "Cannot replay " << key; 137 SinkTraceCtx(mLogCtx) << "Not replaying " << key;
136 //We silently skip over revisions that cannot be replayed, as this is not an error. 138 //We silently skip over revisions that cannot be replayed, as this is not an error.
137 replayJob = KAsync::null();
138 } 139 }
139 //Set the last revision we tried to replay
140 *lastReplayedRevision = revision;
141 break;
142 } 140 }
143 } 141 }
144 //Bump the revision if we failed to even attempt to replay. This will simply skip over those revisions, as we can't recover from those situations. 142 //Bump the revision if we failed to even attempt to replay. This will simply skip over those revisions, as we can't recover from those situations.
@@ -149,22 +147,22 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
149 if (error) { 147 if (error) {
150 SinkWarningCtx(mLogCtx) << "Change replay failed: " << error << "Last replayed revision: " << *lastReplayedRevision; 148 SinkWarningCtx(mLogCtx) << "Change replay failed: " << error << "Last replayed revision: " << *lastReplayedRevision;
151 //We're probably not online or so, so postpone retrying 149 //We're probably not online or so, so postpone retrying
152 return KAsync::value(KAsync::Break); 150 return KAsync::value(KAsync::Break).then(KAsync::error<KAsync::ControlFlowFlag>(error));
151 }
152 SinkTraceCtx(mLogCtx) << "Replayed until: " << *lastReplayedRevision;
153
154 recordReplayedRevision(*lastReplayedRevision);
155 reportProgress(*lastReplayedRevision, *topRevision);
156
157 const bool gotMoreToReplay = (*lastReplayedRevision < *topRevision);
158 if (gotMoreToReplay) {
159 SinkTraceCtx(mLogCtx) << "Replaying some more...";
160 //Replay more if we have more
161 return KAsync::wait(0).then(KAsync::value(KAsync::Continue));
153 } else { 162 } else {
154 SinkTraceCtx(mLogCtx) << "Replayed until: " << *lastReplayedRevision; 163 return KAsync::value(KAsync::Break);
155 recordReplayedRevision(*lastReplayedRevision);
156 if (*lastReplayedRevision < *topRevision) {
157 SinkTraceCtx(mLogCtx) << "Replaying some more...";
158 //Replay more if we have more
159 return KAsync::wait(0).then(KAsync::value(KAsync::Continue));
160 } else {
161 return KAsync::value(KAsync::Break);
162 }
163 } 164 }
164 //We shouldn't ever get here 165 }).guard(&mGuard);
165 Q_ASSERT(false);
166 return KAsync::value(KAsync::Break);
167 });
168 }); 166 });
169 }) 167 })
170 .then([this](const KAsync::Error &error) { 168 .then([this](const KAsync::Error &error) {
@@ -181,7 +179,12 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
181 emit changesReplayed(); 179 emit changesReplayed();
182 } 180 }
183 } 181 }
184 }); 182 if (error) {
183 return KAsync::error(error);
184 } else {
185 return KAsync::null();
186 }
187 }).guard(&mGuard);
185} 188}
186 189
187void ChangeReplay::revisionChanged() 190void ChangeReplay::revisionChanged()
diff --git a/common/changereplay.h b/common/changereplay.h
index edc4462..22e26a5 100644
--- a/common/changereplay.h
+++ b/common/changereplay.h
@@ -54,6 +54,7 @@ public slots:
54protected: 54protected:
55 virtual KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; 55 virtual KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0;
56 virtual bool canReplay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; 56 virtual bool canReplay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0;
57 virtual void reportProgress(int progress, int total, const QByteArrayList &applicableEntities = {}){};
57 Sink::Storage::DataStore mStorage; 58 Sink::Storage::DataStore mStorage;
58 KAsync::Job<void> replayNextRevision(); 59 KAsync::Job<void> replayNextRevision();
59 60
@@ -63,7 +64,7 @@ private:
63 bool mReplayInProgress; 64 bool mReplayInProgress;
64 Sink::Storage::DataStore::Transaction mMainStoreTransaction; 65 Sink::Storage::DataStore::Transaction mMainStoreTransaction;
65 Sink::Log::Context mLogCtx; 66 Sink::Log::Context mLogCtx;
66 QSharedPointer<QObject> mGuard; 67 QObject mGuard;
67}; 68};
68 69
69class NullChangeReplay : public ChangeReplay 70class NullChangeReplay : public ChangeReplay
diff --git a/common/commands.cpp b/common/commands.cpp
index eeb7f08..24f2017 100644
--- a/common/commands.cpp
+++ b/common/commands.cpp
@@ -1,5 +1,6 @@
1/* 1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org> 2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 * Copyright (C) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
3 * 4 *
4 * This library is free software; you can redistribute it and/or 5 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public 6 * modify it under the terms of the GNU Lesser General Public
@@ -20,7 +21,7 @@
20 21
21#include "commands.h" 22#include "commands.h"
22 23
23#include <QIODevice> 24#include <QLocalSocket>
24#include <log.h> 25#include <log.h>
25 26
26namespace Sink { 27namespace Sink {
@@ -73,19 +74,19 @@ int headerSize()
73 return sizeof(int) + (sizeof(uint) * 2); 74 return sizeof(int) + (sizeof(uint) * 2);
74} 75}
75 76
76void write(QIODevice *device, int messageId, int commandId) 77void write(QLocalSocket *device, int messageId, int commandId)
77{ 78{
78 write(device, messageId, commandId, 0, 0); 79 write(device, messageId, commandId, 0, 0);
79} 80}
80 81
81static void write(QIODevice *device, const char *buffer, uint size) 82static void write(QLocalSocket *device, const char *buffer, uint size)
82{ 83{
83 if (device->write(buffer, size) < 0) { 84 if (device->write(buffer, size) < 0) {
84 SinkWarningCtx(Sink::Log::Context{"commands"}) << "Error while writing " << device->errorString(); 85 SinkWarningCtx(Sink::Log::Context{"commands"}) << "Error while writing " << device->errorString();
85 } 86 }
86} 87}
87 88
88void write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size) 89void write(QLocalSocket *device, int messageId, int commandId, const char *buffer, uint size)
89{ 90{
90 if (size > 0 && !buffer) { 91 if (size > 0 && !buffer) {
91 size = 0; 92 size = 0;
@@ -97,15 +98,16 @@ void write(QIODevice *device, int messageId, int commandId, const char *buffer,
97 if (buffer) { 98 if (buffer) {
98 write(device, buffer, size); 99 write(device, buffer, size);
99 } 100 }
101 //The default implementation will happily buffer 200k bytes before sending it out which doesn't make the sytem exactly responsive.
102 //1k is arbitrary, but fits a bunch of messages at least.
103 if (device->bytesToWrite() > 1000) {
104 device->flush();
105 }
100} 106}
101 107
102void write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb) 108void write(QLocalSocket *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb)
103{ 109{
104 const int dataSize = fbb.GetSize(); 110 write(device, messageId, commandId, (const char *)fbb.GetBufferPointer(), fbb.GetSize());
105 write(device, (const char *)&messageId, sizeof(int));
106 write(device, (const char *)&commandId, sizeof(int));
107 write(device, (const char *)&dataSize, sizeof(int));
108 write(device, (const char *)fbb.GetBufferPointer(), dataSize);
109} 111}
110 112
111} // namespace Commands 113} // namespace Commands
diff --git a/common/commands.h b/common/commands.h
index 6d5d39b..1548eac 100644
--- a/common/commands.h
+++ b/common/commands.h
@@ -24,7 +24,7 @@
24#include <flatbuffers/flatbuffers.h> 24#include <flatbuffers/flatbuffers.h>
25#include <QByteArray> 25#include <QByteArray>
26 26
27class QIODevice; 27class QLocalSocket;
28 28
29namespace Sink { 29namespace Sink {
30 30
@@ -55,9 +55,9 @@ enum CommandIds
55QByteArray name(int commandId); 55QByteArray name(int commandId);
56 56
57int SINK_EXPORT headerSize(); 57int SINK_EXPORT headerSize();
58void SINK_EXPORT write(QIODevice *device, int messageId, int commandId); 58void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId);
59void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size); 59void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId, const char *buffer, uint size);
60void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb); 60void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb);
61} 61}
62 62
63} // namespace Sink 63} // namespace Sink
diff --git a/common/commands/notification.fbs b/common/commands/notification.fbs
index 517111c..7ced666 100644
--- a/common/commands/notification.fbs
+++ b/common/commands/notification.fbs
@@ -5,6 +5,8 @@ table Notification {
5 identifier: string; //An identifier that links back to the something related to the notification (e.g. a command id) 5 identifier: string; //An identifier that links back to the something related to the notification (e.g. a command id)
6 message: string; 6 message: string;
7 code: int = 0; //See notification.h 7 code: int = 0; //See notification.h
8 progress: int = 0; //See notification.h
9 total: int = 0; //See notification.h
8 entities: [string]; //A list of entities this applies to 10 entities: [string]; //A list of entities this applies to
9} 11}
10 12
diff --git a/common/configstore.cpp b/common/configstore.cpp
index 1ae9da8..6751907 100644
--- a/common/configstore.cpp
+++ b/common/configstore.cpp
@@ -24,8 +24,6 @@
24#include <log.h> 24#include <log.h>
25#include <definitions.h> 25#include <definitions.h>
26 26
27SINK_DEBUG_AREA("configstore")
28
29static QSharedPointer<QSettings> getConfig(const QByteArray &identifier) 27static QSharedPointer<QSettings> getConfig(const QByteArray &identifier)
30{ 28{
31 return QSharedPointer<QSettings>::create(Sink::configLocation() + "/" + identifier + ".ini", QSettings::IniFormat); 29 return QSharedPointer<QSettings>::create(Sink::configLocation() + "/" + identifier + ".ini", QSettings::IniFormat);
diff --git a/common/contactpreprocessor.cpp b/common/contactpreprocessor.cpp
index ac2c3bc..d331421 100644
--- a/common/contactpreprocessor.cpp
+++ b/common/contactpreprocessor.cpp
@@ -35,6 +35,8 @@ void updatedProperties(Sink::ApplicationDomain::Contact &contact, const KContact
35 emails << Sink::ApplicationDomain::Contact::Email{Sink::ApplicationDomain::Contact::Email::Undefined, email}; 35 emails << Sink::ApplicationDomain::Contact::Email{Sink::ApplicationDomain::Contact::Email::Undefined, email};
36 } 36 }
37 contact.setEmails(emails); 37 contact.setEmails(emails);
38
39 contact.setPhoto(addressee.photo().rawData());
38} 40}
39 41
40ContactPropertyExtractor::~ContactPropertyExtractor() 42ContactPropertyExtractor::~ContactPropertyExtractor()
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp
index 2e0c348..4c95606 100644
--- a/common/datastorequery.cpp
+++ b/common/datastorequery.cpp
@@ -43,6 +43,8 @@ class Source : public FilterBase {
43 43
44 QVector<QByteArray> mIds; 44 QVector<QByteArray> mIds;
45 QVector<QByteArray>::ConstIterator mIt; 45 QVector<QByteArray>::ConstIterator mIt;
46 QVector<QByteArray> mIncrementalIds;
47 QVector<QByteArray>::ConstIterator mIncrementalIt;
46 48
47 Source (const QVector<QByteArray> &ids, DataStoreQuery *store) 49 Source (const QVector<QByteArray> &ids, DataStoreQuery *store)
48 : FilterBase(store), 50 : FilterBase(store),
@@ -63,21 +65,36 @@ class Source : public FilterBase {
63 65
64 void add(const QVector<QByteArray> &ids) 66 void add(const QVector<QByteArray> &ids)
65 { 67 {
66 mIds = ids; 68 mIncrementalIds = ids;
67 mIt = mIds.constBegin(); 69 mIncrementalIt = mIncrementalIds.constBegin();
68 } 70 }
69 71
70 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE 72 bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE
71 { 73 {
72 if (mIt == mIds.constEnd()) { 74 if (!mIncrementalIds.isEmpty()) {
73 return false; 75 if (mIncrementalIt == mIncrementalIds.constEnd()) {
76 return false;
77 }
78 readEntity(*mIncrementalIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) {
79 SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation);
80 callback({entity, operation});
81 });
82 mIncrementalIt++;
83 if (mIncrementalIt == mIncrementalIds.constEnd()) {
84 return false;
85 }
86 return true;
87 } else {
88 if (mIt == mIds.constEnd()) {
89 return false;
90 }
91 readEntity(*mIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) {
92 SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation);
93 callback({entity, operation});
94 });
95 mIt++;
96 return mIt != mIds.constEnd();
74 } 97 }
75 readEntity(*mIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) {
76 SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation);
77 callback({entity, operation});
78 });
79 mIt++;
80 return mIt != mIds.constEnd();
81 } 98 }
82}; 99};
83 100
@@ -599,6 +616,10 @@ ResultSet DataStoreQuery::update(qint64 baseRevision)
599 return ResultSet(generator, [this]() { mCollector->skip(); }); 616 return ResultSet(generator, [this]() { mCollector->skip(); });
600} 617}
601 618
619void DataStoreQuery::updateComplete()
620{
621 mSource->mIncrementalIds.clear();
622}
602 623
603ResultSet DataStoreQuery::execute() 624ResultSet DataStoreQuery::execute()
604{ 625{
diff --git a/common/datastorequery.h b/common/datastorequery.h
index ee5f99e..de4ae26 100644
--- a/common/datastorequery.h
+++ b/common/datastorequery.h
@@ -50,6 +50,7 @@ public:
50 ~DataStoreQuery(); 50 ~DataStoreQuery();
51 ResultSet execute(); 51 ResultSet execute();
52 ResultSet update(qint64 baseRevision); 52 ResultSet update(qint64 baseRevision);
53 void updateComplete();
53 54
54 State::Ptr getState(); 55 State::Ptr getState();
55 56
diff --git a/common/definitions.cpp b/common/definitions.cpp
index 3fc4700..17977bc 100644
--- a/common/definitions.cpp
+++ b/common/definitions.cpp
@@ -23,6 +23,17 @@
23#include <QStandardPaths> 23#include <QStandardPaths>
24#include <QDir> 24#include <QDir>
25 25
26static bool rereadDataLocation = true;
27static bool rereadConfigLocation = true;
28static bool rereadTemporaryFileLocation = true;
29
30void Sink::clearLocationCache()
31{
32 rereadDataLocation = true;
33 rereadConfigLocation = true;
34 rereadTemporaryFileLocation = true;
35}
36
26QString Sink::storageLocation() 37QString Sink::storageLocation()
27{ 38{
28 return dataLocation() + "/storage"; 39 return dataLocation() + "/storage";
@@ -30,21 +41,37 @@ QString Sink::storageLocation()
30 41
31QString Sink::dataLocation() 42QString Sink::dataLocation()
32{ 43{
33 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; 44 static QString location;
45 if (rereadDataLocation) {
46 location = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink";
47 rereadDataLocation = false;
48 }
49 return location;
34} 50}
35 51
36QString Sink::configLocation() 52QString Sink::configLocation()
37{ 53{
38 return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; 54 static QString location;
55 if (rereadConfigLocation) {
56 location = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink";
57 rereadConfigLocation = false;
58 }
59 return location;
39} 60}
40 61
41QString Sink::temporaryFileLocation() 62QString Sink::temporaryFileLocation()
42{ 63{
43 auto path = dataLocation() + "/temporaryFiles"; 64 static QString location;
44 //FIXME create in a singleton on startup? 65 static bool dirCreated = false;
45 QDir dir; 66 if (rereadTemporaryFileLocation) {
46 dir.mkpath(path); 67 location = dataLocation() + "/temporaryFiles";
47 return path; 68 dirCreated = QDir{}.mkpath(location);
69 rereadTemporaryFileLocation = false;
70 }
71 if (!dirCreated && QDir{}.mkpath(location)) {
72 dirCreated = true;
73 }
74 return location;
48} 75}
49 76
50QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifier) 77QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifier)
diff --git a/common/definitions.h b/common/definitions.h
index e8cd45e..ce9e794 100644
--- a/common/definitions.h
+++ b/common/definitions.h
@@ -25,6 +25,7 @@
25#include <QByteArray> 25#include <QByteArray>
26 26
27namespace Sink { 27namespace Sink {
28void SINK_EXPORT clearLocationCache();
28QString SINK_EXPORT storageLocation(); 29QString SINK_EXPORT storageLocation();
29QString SINK_EXPORT dataLocation(); 30QString SINK_EXPORT dataLocation();
30QString SINK_EXPORT configLocation(); 31QString SINK_EXPORT configLocation();
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index 67d463f..3718f77 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -25,8 +25,6 @@
25#include "storage.h" //for generateUid() 25#include "storage.h" //for generateUid()
26#include <QFile> 26#include <QFile>
27 27
28SINK_DEBUG_AREA("applicationdomaintype");
29
30QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::Mail::Contact &c) 28QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::Mail::Contact &c)
31{ 29{
32 d << "Contact(" << c.name << ", " << c.emailAddress << ")"; 30 d << "Contact(" << c.name << ", " << c.emailAddress << ")";
@@ -114,6 +112,7 @@ SINK_REGISTER_PROPERTY(Contact, Lastname);
114SINK_REGISTER_PROPERTY(Contact, Emails); 112SINK_REGISTER_PROPERTY(Contact, Emails);
115SINK_REGISTER_PROPERTY(Contact, Vcard); 113SINK_REGISTER_PROPERTY(Contact, Vcard);
116SINK_REGISTER_PROPERTY(Contact, Addressbook); 114SINK_REGISTER_PROPERTY(Contact, Addressbook);
115SINK_REGISTER_PROPERTY(Contact, Photo);
117 116
118SINK_REGISTER_ENTITY(Addressbook); 117SINK_REGISTER_ENTITY(Addressbook);
119SINK_REGISTER_PROPERTY(Addressbook, Name); 118SINK_REGISTER_PROPERTY(Addressbook, Name);
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index e5aa46e..602d54c 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -377,6 +377,7 @@ struct SINK_EXPORT Contact : public Entity {
377 SINK_PROPERTY(QString, Lastname, lastname); 377 SINK_PROPERTY(QString, Lastname, lastname);
378 SINK_PROPERTY(QList<Email>, Emails, emails); 378 SINK_PROPERTY(QList<Email>, Emails, emails);
379 SINK_PROPERTY(QByteArray, Vcard, vcard); 379 SINK_PROPERTY(QByteArray, Vcard, vcard);
380 SINK_PROPERTY(QByteArray, Photo, photo);
380 SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook); 381 SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook);
381}; 382};
382 383
diff --git a/common/domain/contact.fbs b/common/domain/contact.fbs
index d941d5a..e689756 100644
--- a/common/domain/contact.fbs
+++ b/common/domain/contact.fbs
@@ -13,6 +13,7 @@ table Contact {
13 addressbook:string; 13 addressbook:string;
14 emails: [ContactEmail]; 14 emails: [ContactEmail];
15 vcard: string; 15 vcard: string;
16 photo: string;
16} 17}
17 18
18root_type Contact; 19root_type Contact;
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index eb3851e..47a9cf7 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -28,25 +28,52 @@
28#include "entity_generated.h" 28#include "entity_generated.h"
29#include "mail/threadindexer.h" 29#include "mail/threadindexer.h"
30#include "domainadaptor.h" 30#include "domainadaptor.h"
31#include "typeimplementations_p.h"
31 32
32using namespace Sink; 33using namespace Sink;
33using namespace Sink::ApplicationDomain; 34using namespace Sink::ApplicationDomain;
34 35
36#define SINK_REGISTER_SERIALIZER(MAPPER, ENTITYTYPE, PROPERTY, LOWERCASEPROPERTY) \
37 MAPPER.addMapping<ENTITYTYPE::PROPERTY, Sink::ApplicationDomain::Buffer::ENTITYTYPE, Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder>(&Sink::ApplicationDomain::Buffer::ENTITYTYPE::LOWERCASEPROPERTY, &Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder::add_##LOWERCASEPROPERTY);
38
39typedef IndexConfig<Mail,
40 ValueIndex<Mail::Date>,
41 ValueIndex<Mail::Folder>,
42 ValueIndex<Mail::ParentMessageId>,
43 ValueIndex<Mail::MessageId>,
44 ValueIndex<Mail::Draft>,
45 SortedIndex<Mail::Folder, Mail::Date>,
46 SecondaryIndex<Mail::MessageId, Mail::ThreadId>,
47 SecondaryIndex<Mail::ThreadId, Mail::MessageId>,
48 CustomSecondaryIndex<Mail::MessageId, Mail::ThreadId, ThreadIndexer>
49 > MailIndexConfig;
50
51typedef IndexConfig<Folder,
52 ValueIndex<Folder::Name>,
53 ValueIndex<Folder::Parent>
54 > FolderIndexConfig;
55
56typedef IndexConfig<Contact,
57 ValueIndex<Contact::Uid>
58 > ContactIndexConfig;
59
60typedef IndexConfig<Addressbook,
61 ValueIndex<Addressbook::Parent>
62 > AddressbookIndexConfig;
63
64typedef IndexConfig<Event,
65 ValueIndex<Event::Uid>
66 > EventIndexConfig;
67
68
35void TypeImplementation<Mail>::configure(TypeIndex &index) 69void TypeImplementation<Mail>::configure(TypeIndex &index)
36{ 70{
37 // index.addProperty<Mail::Sender>(); 71 MailIndexConfig::configure(index);
38 /* index.addProperty<QByteArray>(Mail::SenderName::name); */ 72}
39 /* index->addProperty<QString>(Mail::Subject::name); */
40 /* index->addFulltextProperty<QString>(Mail::Subject::name); */
41 index.addProperty<Mail::Date>();
42 index.addProperty<Mail::Folder>();
43 index.addPropertyWithSorting<Mail::Folder, Mail::Date>();
44 index.addProperty<Mail::ParentMessageId>();
45 index.addProperty<Mail::MessageId>();
46 73
47 index.addSecondaryPropertyIndexer<Mail::MessageId, Mail::ThreadId, ThreadIndexer>(); 74QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases()
48 index.addSecondaryProperty<Mail::MessageId, Mail::ThreadId>(); 75{
49 index.addSecondaryProperty<Mail::ThreadId, Mail::MessageId>(); 76 return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", 0}}, MailIndexConfig::databases());
50} 77}
51 78
52void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) 79void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper)
@@ -61,69 +88,44 @@ void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMappe
61 }); 88 });
62} 89}
63 90
64void TypeImplementation<Mail>::configure(ReadPropertyMapper<Buffer> &propertyMapper) 91void TypeImplementation<Mail>::configure(PropertyMapper &propertyMapper)
65{ 92{
66 propertyMapper.addMapping<Mail::Sender, Buffer>(&Buffer::sender); 93 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Sender, sender);
67 propertyMapper.addMapping<Mail::To, Buffer>(&Buffer::to); 94 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, To, to);
68 propertyMapper.addMapping<Mail::Cc, Buffer>(&Buffer::cc); 95 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Cc, cc);
69 propertyMapper.addMapping<Mail::Bcc, Buffer>(&Buffer::bcc); 96 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Bcc, bcc);
70 propertyMapper.addMapping<Mail::Subject, Buffer>(&Buffer::subject); 97 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Subject, subject);
71 propertyMapper.addMapping<Mail::Date, Buffer>(&Buffer::date); 98 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Date, date);
72 propertyMapper.addMapping<Mail::Unread, Buffer>(&Buffer::unread); 99 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Unread, unread);
73 propertyMapper.addMapping<Mail::Important, Buffer>(&Buffer::important); 100 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Important, important);
74 propertyMapper.addMapping<Mail::Folder, Buffer>(&Buffer::folder); 101 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Folder, folder);
75 propertyMapper.addMapping<Mail::MimeMessage, Buffer>(&Buffer::mimeMessage); 102 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, MimeMessage, mimeMessage);
76 propertyMapper.addMapping<Mail::FullPayloadAvailable, Buffer>(&Buffer::fullPayloadAvailable); 103 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, FullPayloadAvailable, fullPayloadAvailable);
77 propertyMapper.addMapping<Mail::Draft, Buffer>(&Buffer::draft); 104 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Draft, draft);
78 propertyMapper.addMapping<Mail::Trash, Buffer>(&Buffer::trash); 105 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Trash, trash);
79 propertyMapper.addMapping<Mail::Sent, Buffer>(&Buffer::sent); 106 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Sent, sent);
80 propertyMapper.addMapping<Mail::MessageId, Buffer>(&Buffer::messageId); 107 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, MessageId, messageId);
81 propertyMapper.addMapping<Mail::ParentMessageId, Buffer>(&Buffer::parentMessageId); 108 SINK_REGISTER_SERIALIZER(propertyMapper, Mail, ParentMessageId, parentMessageId);
82}
83
84void TypeImplementation<Mail>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper)
85{
86 propertyMapper.addMapping<Mail::Sender>(&BufferBuilder::add_sender);
87 propertyMapper.addMapping<Mail::To>(&BufferBuilder::add_to);
88 propertyMapper.addMapping<Mail::Cc>(&BufferBuilder::add_cc);
89 propertyMapper.addMapping<Mail::Bcc>(&BufferBuilder::add_bcc);
90 propertyMapper.addMapping<Mail::Subject>(&BufferBuilder::add_subject);
91 propertyMapper.addMapping<Mail::Date>(&BufferBuilder::add_date);
92 propertyMapper.addMapping<Mail::Unread>(&BufferBuilder::add_unread);
93 propertyMapper.addMapping<Mail::Important>(&BufferBuilder::add_important);
94 propertyMapper.addMapping<Mail::Folder>(&BufferBuilder::add_folder);
95 propertyMapper.addMapping<Mail::MimeMessage>(&BufferBuilder::add_mimeMessage);
96 propertyMapper.addMapping<Mail::FullPayloadAvailable>(&BufferBuilder::add_fullPayloadAvailable);
97 propertyMapper.addMapping<Mail::Draft>(&BufferBuilder::add_draft);
98 propertyMapper.addMapping<Mail::Trash>(&BufferBuilder::add_trash);
99 propertyMapper.addMapping<Mail::Sent>(&BufferBuilder::add_sent);
100 propertyMapper.addMapping<Mail::MessageId>(&BufferBuilder::add_messageId);
101 propertyMapper.addMapping<Mail::ParentMessageId>(&BufferBuilder::add_parentMessageId);
102} 109}
103 110
104 111
105void TypeImplementation<Folder>::configure(TypeIndex &index) 112void TypeImplementation<Folder>::configure(TypeIndex &index)
106{ 113{
107 index.addProperty<QByteArray>(Folder::Parent::name); 114 FolderIndexConfig::configure(index);
108 index.addProperty<QString>(Folder::Name::name);
109} 115}
110 116
111void TypeImplementation<Folder>::configure(ReadPropertyMapper<Buffer> &propertyMapper) 117QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases()
112{ 118{
113 propertyMapper.addMapping<Folder::Parent, Buffer>(&Buffer::parent); 119 return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", 0}}, FolderIndexConfig::databases());
114 propertyMapper.addMapping<Folder::Name, Buffer>(&Buffer::name);
115 propertyMapper.addMapping<Folder::Icon, Buffer>(&Buffer::icon);
116 propertyMapper.addMapping<Folder::SpecialPurpose, Buffer>(&Buffer::specialpurpose);
117 propertyMapper.addMapping<Folder::Enabled, Buffer>(&Buffer::enabled);
118} 120}
119 121
120void TypeImplementation<Folder>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) 122void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper)
121{ 123{
122 propertyMapper.addMapping<Folder::Parent>(&BufferBuilder::add_parent); 124 SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Parent, parent);
123 propertyMapper.addMapping<Folder::Name>(&BufferBuilder::add_name); 125 SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Name, name);
124 propertyMapper.addMapping<Folder::Icon>(&BufferBuilder::add_icon); 126 SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Icon, icon);
125 propertyMapper.addMapping<Folder::SpecialPurpose>(&BufferBuilder::add_specialpurpose); 127 SINK_REGISTER_SERIALIZER(propertyMapper, Folder, SpecialPurpose, specialpurpose);
126 propertyMapper.addMapping<Folder::Enabled>(&BufferBuilder::add_enabled); 128 SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Enabled, enabled);
127} 129}
128 130
129void TypeImplementation<Folder>::configure(IndexPropertyMapper &) 131void TypeImplementation<Folder>::configure(IndexPropertyMapper &)
@@ -134,29 +136,24 @@ void TypeImplementation<Folder>::configure(IndexPropertyMapper &)
134 136
135void TypeImplementation<Contact>::configure(TypeIndex &index) 137void TypeImplementation<Contact>::configure(TypeIndex &index)
136{ 138{
137 index.addProperty<QByteArray>(Contact::Uid::name); 139 ContactIndexConfig::configure(index);
138} 140}
139 141
140void TypeImplementation<Contact>::configure(ReadPropertyMapper<Buffer> &propertyMapper) 142QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases()
141{ 143{
142 propertyMapper.addMapping<Contact::Uid, Buffer>(&Buffer::uid); 144 return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", 0}}, ContactIndexConfig::databases());
143 propertyMapper.addMapping<Contact::Fn, Buffer>(&Buffer::fn);
144 propertyMapper.addMapping<Contact::Emails, Buffer>(&Buffer::emails);
145 propertyMapper.addMapping<Contact::Vcard, Buffer>(&Buffer::vcard);
146 propertyMapper.addMapping<Contact::Addressbook, Buffer>(&Buffer::addressbook);
147 propertyMapper.addMapping<Contact::Firstname, Buffer>(&Buffer::firstname);
148 propertyMapper.addMapping<Contact::Lastname, Buffer>(&Buffer::lastname);
149} 145}
150 146
151void TypeImplementation<Contact>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) 147void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper)
152{ 148{
153 propertyMapper.addMapping<Contact::Uid>(&BufferBuilder::add_uid); 149 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Uid, uid);
154 propertyMapper.addMapping<Contact::Fn>(&BufferBuilder::add_fn); 150 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Fn, fn);
155 propertyMapper.addMapping<Contact::Emails>(&BufferBuilder::add_emails); 151 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Emails, emails);
156 propertyMapper.addMapping<Contact::Vcard>(&BufferBuilder::add_vcard); 152 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Vcard, vcard);
157 propertyMapper.addMapping<Contact::Addressbook>(&BufferBuilder::add_addressbook); 153 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Addressbook, addressbook);
158 propertyMapper.addMapping<Contact::Firstname>(&BufferBuilder::add_firstname); 154 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Firstname, firstname);
159 propertyMapper.addMapping<Contact::Lastname>(&BufferBuilder::add_lastname); 155 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Lastname, lastname);
156 SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Photo, photo);
160} 157}
161 158
162void TypeImplementation<Contact>::configure(IndexPropertyMapper &) 159void TypeImplementation<Contact>::configure(IndexPropertyMapper &)
@@ -167,20 +164,18 @@ void TypeImplementation<Contact>::configure(IndexPropertyMapper &)
167 164
168void TypeImplementation<Addressbook>::configure(TypeIndex &index) 165void TypeImplementation<Addressbook>::configure(TypeIndex &index)
169{ 166{
170 index.addProperty<QByteArray>(Addressbook::Parent::name); 167 AddressbookIndexConfig::configure(index);
171 index.addProperty<QString>(Addressbook::Name::name);
172} 168}
173 169
174void TypeImplementation<Addressbook>::configure(ReadPropertyMapper<Buffer> &propertyMapper) 170QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases()
175{ 171{
176 propertyMapper.addMapping<Addressbook::Parent, Buffer>(&Buffer::parent); 172 return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", 0}}, AddressbookIndexConfig::databases());
177 propertyMapper.addMapping<Addressbook::Name, Buffer>(&Buffer::name);
178} 173}
179 174
180void TypeImplementation<Addressbook>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) 175void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper)
181{ 176{
182 propertyMapper.addMapping<Addressbook::Parent>(&BufferBuilder::add_parent); 177 SINK_REGISTER_SERIALIZER(propertyMapper, Addressbook, Parent, parent);
183 propertyMapper.addMapping<Addressbook::Name>(&BufferBuilder::add_name); 178 SINK_REGISTER_SERIALIZER(propertyMapper, Addressbook, Name, name);
184} 179}
185 180
186void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) 181void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &)
@@ -191,26 +186,24 @@ void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &)
191 186
192void TypeImplementation<Event>::configure(TypeIndex &index) 187void TypeImplementation<Event>::configure(TypeIndex &index)
193{ 188{
194 index.addProperty<QByteArray>(Event::Uid::name); 189 EventIndexConfig::configure(index);
195} 190}
196 191
197void TypeImplementation<Event>::configure(ReadPropertyMapper<Buffer> &propertyMapper) 192QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases()
198{ 193{
199 propertyMapper.addMapping<Event::Summary, Buffer>(&Buffer::summary); 194 return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", 0}}, EventIndexConfig::databases());
200 propertyMapper.addMapping<Event::Description, Buffer>(&Buffer::description);
201 propertyMapper.addMapping<Event::Uid, Buffer>(&Buffer::uid);
202 propertyMapper.addMapping<Event::Attachment, Buffer>(&Buffer::attachment);
203} 195}
204 196
205void TypeImplementation<Event>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) 197void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper)
206{ 198{
207 propertyMapper.addMapping<Event::Summary>(&BufferBuilder::add_summary); 199 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Summary, summary);
208 propertyMapper.addMapping<Event::Description>(&BufferBuilder::add_description); 200 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Description, description);
209 propertyMapper.addMapping<Event::Uid>(&BufferBuilder::add_uid); 201 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Uid, uid);
210 propertyMapper.addMapping<Event::Attachment>(&BufferBuilder::add_attachment); 202 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Attachment, attachment);
211} 203}
212 204
213void TypeImplementation<Event>::configure(IndexPropertyMapper &) 205void TypeImplementation<Event>::configure(IndexPropertyMapper &)
214{ 206{
215 207
216} 208}
209
diff --git a/common/domain/typeimplementations.h b/common/domain/typeimplementations.h
index 37d6ca9..d36dfc1 100644
--- a/common/domain/typeimplementations.h
+++ b/common/domain/typeimplementations.h
@@ -26,10 +26,7 @@
26#include "contact_generated.h" 26#include "contact_generated.h"
27#include "addressbook_generated.h" 27#include "addressbook_generated.h"
28 28
29template<typename T> 29class PropertyMapper;
30class ReadPropertyMapper;
31template<typename T>
32class WritePropertyMapper;
33class IndexPropertyMapper; 30class IndexPropertyMapper;
34 31
35class TypeIndex; 32class TypeIndex;
@@ -48,9 +45,9 @@ public:
48 typedef Sink::ApplicationDomain::Buffer::Mail Buffer; 45 typedef Sink::ApplicationDomain::Buffer::Mail Buffer;
49 typedef Sink::ApplicationDomain::Buffer::MailBuilder BufferBuilder; 46 typedef Sink::ApplicationDomain::Buffer::MailBuilder BufferBuilder;
50 static void configure(TypeIndex &index); 47 static void configure(TypeIndex &index);
51 static void configure(ReadPropertyMapper<Buffer> &propertyMapper); 48 static void configure(PropertyMapper &propertyMapper);
52 static void configure(WritePropertyMapper<BufferBuilder> &propertyMapper);
53 static void configure(IndexPropertyMapper &indexPropertyMapper); 49 static void configure(IndexPropertyMapper &indexPropertyMapper);
50 static QMap<QByteArray, int> typeDatabases();
54}; 51};
55 52
56template<> 53template<>
@@ -59,9 +56,9 @@ public:
59 typedef Sink::ApplicationDomain::Buffer::Folder Buffer; 56 typedef Sink::ApplicationDomain::Buffer::Folder Buffer;
60 typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder; 57 typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder;
61 static void configure(TypeIndex &); 58 static void configure(TypeIndex &);
62 static void configure(ReadPropertyMapper<Buffer> &); 59 static void configure(PropertyMapper &);
63 static void configure(WritePropertyMapper<BufferBuilder> &);
64 static void configure(IndexPropertyMapper &indexPropertyMapper); 60 static void configure(IndexPropertyMapper &indexPropertyMapper);
61 static QMap<QByteArray, int> typeDatabases();
65}; 62};
66 63
67template<> 64template<>
@@ -70,9 +67,9 @@ public:
70 typedef Sink::ApplicationDomain::Buffer::Contact Buffer; 67 typedef Sink::ApplicationDomain::Buffer::Contact Buffer;
71 typedef Sink::ApplicationDomain::Buffer::ContactBuilder BufferBuilder; 68 typedef Sink::ApplicationDomain::Buffer::ContactBuilder BufferBuilder;
72 static void configure(TypeIndex &); 69 static void configure(TypeIndex &);
73 static void configure(ReadPropertyMapper<Buffer> &); 70 static void configure(PropertyMapper &);
74 static void configure(WritePropertyMapper<BufferBuilder> &);
75 static void configure(IndexPropertyMapper &indexPropertyMapper); 71 static void configure(IndexPropertyMapper &indexPropertyMapper);
72 static QMap<QByteArray, int> typeDatabases();
76}; 73};
77 74
78template<> 75template<>
@@ -81,9 +78,9 @@ public:
81 typedef Sink::ApplicationDomain::Buffer::Addressbook Buffer; 78 typedef Sink::ApplicationDomain::Buffer::Addressbook Buffer;
82 typedef Sink::ApplicationDomain::Buffer::AddressbookBuilder BufferBuilder; 79 typedef Sink::ApplicationDomain::Buffer::AddressbookBuilder BufferBuilder;
83 static void configure(TypeIndex &); 80 static void configure(TypeIndex &);
84 static void configure(ReadPropertyMapper<Buffer> &); 81 static void configure(PropertyMapper &);
85 static void configure(WritePropertyMapper<BufferBuilder> &);
86 static void configure(IndexPropertyMapper &indexPropertyMapper); 82 static void configure(IndexPropertyMapper &indexPropertyMapper);
83 static QMap<QByteArray, int> typeDatabases();
87}; 84};
88 85
89template<> 86template<>
@@ -92,9 +89,9 @@ public:
92 typedef Sink::ApplicationDomain::Buffer::Event Buffer; 89 typedef Sink::ApplicationDomain::Buffer::Event Buffer;
93 typedef Sink::ApplicationDomain::Buffer::EventBuilder BufferBuilder; 90 typedef Sink::ApplicationDomain::Buffer::EventBuilder BufferBuilder;
94 static void configure(TypeIndex &); 91 static void configure(TypeIndex &);
95 static void configure(ReadPropertyMapper<Buffer> &); 92 static void configure(PropertyMapper &);
96 static void configure(WritePropertyMapper<BufferBuilder> &);
97 static void configure(IndexPropertyMapper &indexPropertyMapper); 93 static void configure(IndexPropertyMapper &indexPropertyMapper);
94 static QMap<QByteArray, int> typeDatabases();
98}; 95};
99 96
100} 97}
diff --git a/common/domain/typeimplementations_p.h b/common/domain/typeimplementations_p.h
new file mode 100644
index 0000000..6f77a2d
--- /dev/null
+++ b/common/domain/typeimplementations_p.h
@@ -0,0 +1,154 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "typeindex.h"
21#include <QMap>
22
23template <typename T, typename First>
24void mergeImpl(T &map, First f)
25{
26 for (auto it = f.constBegin(); it != f.constEnd(); it++) {
27 map.insert(it.key(), it.value());
28 }
29}
30
31template <typename T, typename First, typename ... Tail>
32void mergeImpl(T &map, First f, Tail ...maps)
33{
34 for (auto it = f.constBegin(); it != f.constEnd(); it++) {
35 map.insert(it.key(), it.value());
36 }
37 mergeImpl<T, Tail...>(map, maps...);
38}
39
40template <typename First, typename ... Tail>
41First merge(First f, Tail ...maps)
42{
43 First map;
44 mergeImpl(map, f, maps...);
45 return map;
46}
47
48template <typename Property>
49class ValueIndex
50{
51public:
52 static void configure(TypeIndex &index)
53 {
54 index.addProperty<Property>();
55 }
56
57 template <typename EntityType>
58 static QMap<QByteArray, int> databases()
59 {
60 return {{QByteArray{EntityType::name} +".index." + Property::name, 1}};
61 }
62};
63
64
65template <typename Property, typename SortProperty>
66class SortedIndex
67{
68public:
69 static void configure(TypeIndex &index)
70 {
71 index.addPropertyWithSorting<Property, SortProperty>();
72 }
73
74 template <typename EntityType>
75 static QMap<QByteArray, int> databases()
76 {
77 return {{QByteArray{EntityType::name} +".index." + Property::name + ".sort." + SortProperty::name, 1}};
78 }
79};
80
81template <typename Property, typename SecondaryProperty>
82class SecondaryIndex
83{
84public:
85 static void configure(TypeIndex &index)
86 {
87 index.addSecondaryProperty<Property, SecondaryProperty>();
88 }
89
90 template <typename EntityType>
91 static QMap<QByteArray, int> databases()
92 {
93 return {{QByteArray{EntityType::name} +".index." + Property::name + SecondaryProperty::name, 1}};
94 }
95};
96
97template <typename Property, typename SecondaryProperty, typename Indexer>
98class CustomSecondaryIndex
99{
100public:
101 static void configure(TypeIndex &index)
102 {
103 index.addSecondaryPropertyIndexer<Property, SecondaryProperty, Indexer>();
104 }
105
106 template <typename EntityType>
107 static QMap<QByteArray, int> databases()
108 {
109 return Indexer::databases();
110 }
111};
112
113template <typename EntityType, typename ... Indexes>
114class IndexConfig
115{
116 template <typename T>
117 static void applyIndex(TypeIndex &index)
118 {
119 T::configure(index);
120 }
121
122 ///Apply recursively for parameter pack
123 template <typename First, typename Second, typename ... Tail>
124 static void applyIndex(TypeIndex &index)
125 {
126 applyIndex<First>(index);
127 applyIndex<Second, Tail...>(index);
128 }
129
130 template <typename T>
131 static QMap<QByteArray, int> getDbs()
132 {
133 return T::template databases<EntityType>();
134 }
135
136 template <typename First, typename Second, typename ... Tail>
137 static QMap<QByteArray, int> getDbs()
138 {
139 return merge(getDbs<First>(), getDbs<Second, Tail...>());
140 }
141
142public:
143 static void configure(TypeIndex &index)
144 {
145 applyIndex<Indexes...>(index);
146 }
147
148 static QMap<QByteArray, int> databases()
149 {
150 return getDbs<Indexes...>();
151 }
152
153};
154
diff --git a/common/domainadaptor.h b/common/domainadaptor.h
index af5d5fc..f981a1f 100644
--- a/common/domainadaptor.h
+++ b/common/domainadaptor.h
@@ -29,21 +29,19 @@
29#include "domain/typeimplementations.h" 29#include "domain/typeimplementations.h"
30#include "bufferadaptor.h" 30#include "bufferadaptor.h"
31#include "entity_generated.h" 31#include "entity_generated.h"
32#include "metadata_generated.h"
33#include "entitybuffer.h" 32#include "entitybuffer.h"
34#include "propertymapper.h" 33#include "propertymapper.h"
35#include "log.h" 34#include "log.h"
36#include "dummy_generated.h"
37 35
38/** 36/**
39 * Create a buffer from a domain object using the provided mappings 37 * Create a buffer from a domain object using the provided mappings
40 */ 38 */
41template <class Builder, class Buffer> 39template <class Builder, class Buffer>
42flatbuffers::Offset<Buffer> 40flatbuffers::Offset<Buffer>
43createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const WritePropertyMapper<Builder> &mapper) 41createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const PropertyMapper &mapper)
44{ 42{
45 // First create a primitives such as strings using the mappings 43 // First create a primitives such as strings using the mappings
46 QList<std::function<void(Builder &)>> propertiesToAddToResource; 44 QList<std::function<void(void *builder)>> propertiesToAddToResource;
47 for (const auto &property : domainObject.changedProperties()) { 45 for (const auto &property : domainObject.changedProperties()) {
48 // SinkTrace() << "copying property " << property; 46 // SinkTrace() << "copying property " << property;
49 const auto value = domainObject.getProperty(property); 47 const auto value = domainObject.getProperty(property);
@@ -57,7 +55,7 @@ createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObj
57 // Then create all porperties using the above generated builderCalls 55 // Then create all porperties using the above generated builderCalls
58 Builder builder(fbb); 56 Builder builder(fbb);
59 for (auto propertyBuilder : propertiesToAddToResource) { 57 for (auto propertyBuilder : propertiesToAddToResource) {
60 propertyBuilder(builder); 58 propertyBuilder(&builder);
61 } 59 }
62 return builder.Finish(); 60 return builder.Finish();
63} 61}
@@ -68,7 +66,7 @@ createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObj
68 * After this the buffer can be extracted from the FlatBufferBuilder object. 66 * After this the buffer can be extracted from the FlatBufferBuilder object.
69 */ 67 */
70template <typename Buffer, typename BufferBuilder> 68template <typename Buffer, typename BufferBuilder>
71static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, WritePropertyMapper<BufferBuilder> &mapper) 69static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, PropertyMapper &mapper)
72{ 70{
73 auto pos = createBufferPart<BufferBuilder, Buffer>(domainObject, fbb, mapper); 71 auto pos = createBufferPart<BufferBuilder, Buffer>(domainObject, fbb, mapper);
74 // Because we cannot template the following call 72 // Because we cannot template the following call
@@ -120,10 +118,8 @@ private:
120/** 118/**
121 * A generic adaptor implementation that uses a property mapper to read/write values. 119 * A generic adaptor implementation that uses a property mapper to read/write values.
122 */ 120 */
123template <class LocalBuffer, class ResourceBuffer>
124class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor 121class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor
125{ 122{
126 SINK_DEBUG_AREA("bufferadaptor")
127public: 123public:
128 DatastoreBufferAdaptor() : BufferAdaptor() 124 DatastoreBufferAdaptor() : BufferAdaptor()
129 { 125 {
@@ -137,9 +133,7 @@ public:
137 133
138 virtual QVariant getProperty(const QByteArray &key) const Q_DECL_OVERRIDE 134 virtual QVariant getProperty(const QByteArray &key) const Q_DECL_OVERRIDE
139 { 135 {
140 if (mResourceBuffer && mResourceMapper->hasMapping(key)) { 136 if (mLocalBuffer && mLocalMapper->hasMapping(key)) {
141 return mResourceMapper->getProperty(key, mResourceBuffer);
142 } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) {
143 return mLocalMapper->getProperty(key, mLocalBuffer); 137 return mLocalMapper->getProperty(key, mLocalBuffer);
144 } else if (mIndex && mIndexMapper->hasMapping(key)) { 138 } else if (mIndex && mIndexMapper->hasMapping(key)) {
145 return mIndexMapper->getProperty(key, *mIndex, *this); 139 return mIndexMapper->getProperty(key, *mIndex, *this);
@@ -152,23 +146,21 @@ public:
152 */ 146 */
153 virtual QList<QByteArray> availableProperties() const Q_DECL_OVERRIDE 147 virtual QList<QByteArray> availableProperties() const Q_DECL_OVERRIDE
154 { 148 {
155 return mResourceMapper->availableProperties() + mLocalMapper->availableProperties() + mIndexMapper->availableProperties(); 149 return mLocalMapper->availableProperties() + mIndexMapper->availableProperties();
156 } 150 }
157 151
158 LocalBuffer const *mLocalBuffer; 152 void const *mLocalBuffer;
159 ResourceBuffer const *mResourceBuffer; 153 QSharedPointer<PropertyMapper> mLocalMapper;
160 QSharedPointer<ReadPropertyMapper<LocalBuffer>> mLocalMapper;
161 QSharedPointer<ReadPropertyMapper<ResourceBuffer>> mResourceMapper;
162 QSharedPointer<IndexPropertyMapper> mIndexMapper; 154 QSharedPointer<IndexPropertyMapper> mIndexMapper;
163 TypeIndex *mIndex; 155 TypeIndex *mIndex;
164}; 156};
165 157
166/** 158/**
167 * The factory should define how to go from an entitybuffer (local + resource buffer), to a domain type adapter. 159 * The factory should define how to go from an entitybuffer (local buffer), to a domain type adapter.
168 * It defines how values are split accross local and resource buffer. 160 * It defines how values are split accross local and resource buffer.
169 * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way. 161 * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way.
170 */ 162 */
171template <typename DomainType, typename ResourceBuffer = Sink::ApplicationDomain::Buffer::Dummy, typename ResourceBuilder = Sink::ApplicationDomain::Buffer::DummyBuilder> 163template <typename DomainType>
172class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface 164class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface
173{ 165{
174 typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::Buffer LocalBuffer; 166 typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::Buffer LocalBuffer;
@@ -176,31 +168,25 @@ class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInte
176 168
177public: 169public:
178 DomainTypeAdaptorFactory() 170 DomainTypeAdaptorFactory()
179 : mLocalMapper(QSharedPointer<ReadPropertyMapper<LocalBuffer>>::create()), 171 : mPropertyMapper(QSharedPointer<PropertyMapper>::create()),
180 mResourceMapper(QSharedPointer<ReadPropertyMapper<ResourceBuffer>>::create()),
181 mLocalWriteMapper(QSharedPointer<WritePropertyMapper<LocalBuilder>>::create()),
182 mResourceWriteMapper(QSharedPointer<WritePropertyMapper<ResourceBuilder>>::create()),
183 mIndexMapper(QSharedPointer<IndexPropertyMapper>::create()) 172 mIndexMapper(QSharedPointer<IndexPropertyMapper>::create())
184 { 173 {
185 Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mLocalMapper); 174 Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mPropertyMapper);
186 Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mLocalWriteMapper);
187 Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mIndexMapper); 175 Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mIndexMapper);
188 } 176 }
189 177
190 virtual ~DomainTypeAdaptorFactory(){}; 178 virtual ~DomainTypeAdaptorFactory(){};
191 179
192 /** 180 /**
193 * Creates an adaptor for the given domain and resource types. 181 * Creates an adaptor for the given domain types.
194 * 182 *
195 * This returns by default a DatastoreBufferAdaptor initialized with the corresponding property mappers. 183 * This returns by default a DatastoreBufferAdaptor initialized with the corresponding property mappers.
196 */ 184 */
197 virtual QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> createAdaptor(const Sink::Entity &entity, TypeIndex *index = nullptr) Q_DECL_OVERRIDE 185 virtual QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> createAdaptor(const Sink::Entity &entity, TypeIndex *index = nullptr) Q_DECL_OVERRIDE
198 { 186 {
199 auto adaptor = QSharedPointer<DatastoreBufferAdaptor<LocalBuffer, ResourceBuffer>>::create(); 187 auto adaptor = QSharedPointer<DatastoreBufferAdaptor>::create();
200 adaptor->mLocalBuffer = Sink::EntityBuffer::readBuffer<LocalBuffer>(entity.local()); 188 adaptor->mLocalBuffer = Sink::EntityBuffer::readBuffer<LocalBuffer>(entity.local());
201 adaptor->mLocalMapper = mLocalMapper; 189 adaptor->mLocalMapper = mPropertyMapper;
202 adaptor->mResourceBuffer = Sink::EntityBuffer::readBuffer<ResourceBuffer>(entity.resource());
203 adaptor->mResourceMapper = mResourceMapper;
204 adaptor->mIndexMapper = mIndexMapper; 190 adaptor->mIndexMapper = mIndexMapper;
205 adaptor->mIndex = index; 191 adaptor->mIndex = index;
206 return adaptor; 192 return adaptor;
@@ -210,18 +196,8 @@ public:
210 createBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = 0, size_t metadataSize = 0) Q_DECL_OVERRIDE 196 createBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = 0, size_t metadataSize = 0) Q_DECL_OVERRIDE
211 { 197 {
212 flatbuffers::FlatBufferBuilder localFbb; 198 flatbuffers::FlatBufferBuilder localFbb;
213 if (mLocalWriteMapper) { 199 createBufferPartBuffer<LocalBuffer, LocalBuilder>(domainObject, localFbb, *mPropertyMapper);
214 // SinkTrace() << "Creating local buffer part"; 200 Sink::EntityBuffer::assembleEntityBuffer(fbb, metadataData, metadataSize, 0, 0, localFbb.GetBufferPointer(), localFbb.GetSize());
215 createBufferPartBuffer<LocalBuffer, LocalBuilder>(domainObject, localFbb, *mLocalWriteMapper);
216 }
217
218 flatbuffers::FlatBufferBuilder resFbb;
219 if (mResourceWriteMapper) {
220 // SinkTrace() << "Creating resouce buffer part";
221 createBufferPartBuffer<ResourceBuffer, ResourceBuilder>(domainObject, resFbb, *mResourceWriteMapper);
222 }
223
224 Sink::EntityBuffer::assembleEntityBuffer(fbb, metadataData, metadataSize, resFbb.GetBufferPointer(), resFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
225 return true; 201 return true;
226 } 202 }
227 203
@@ -236,10 +212,7 @@ public:
236 212
237 213
238protected: 214protected:
239 QSharedPointer<ReadPropertyMapper<LocalBuffer>> mLocalMapper; 215 QSharedPointer<PropertyMapper> mPropertyMapper;
240 QSharedPointer<ReadPropertyMapper<ResourceBuffer>> mResourceMapper;
241 QSharedPointer<WritePropertyMapper<LocalBuilder>> mLocalWriteMapper;
242 QSharedPointer<WritePropertyMapper<ResourceBuilder>> mResourceWriteMapper;
243 QSharedPointer<IndexPropertyMapper> mIndexMapper; 216 QSharedPointer<IndexPropertyMapper> mIndexMapper;
244}; 217};
245 218
diff --git a/common/facade.cpp b/common/facade.cpp
index 38ec1dc..9c14a23 100644
--- a/common/facade.cpp
+++ b/common/facade.cpp
@@ -104,6 +104,7 @@ KAsync::Job<void> GenericFacade<DomainType>::remove(const DomainType &domainObje
104template <class DomainType> 104template <class DomainType>
105QPair<KAsync::Job<void>, typename ResultEmitter<typename DomainType::Ptr>::Ptr> GenericFacade<DomainType>::load(const Sink::Query &query, const Log::Context &ctx) 105QPair<KAsync::Job<void>, typename ResultEmitter<typename DomainType::Ptr>::Ptr> GenericFacade<DomainType>::load(const Sink::Query &query, const Log::Context &ctx)
106{ 106{
107 Q_ASSERT(DomainType::name == query.type() || query.type().isEmpty());
107 // The runner lives for the lifetime of the query 108 // The runner lives for the lifetime of the query
108 auto runner = new QueryRunner<DomainType>(query, mResourceContext, bufferTypeForDomainType(), ctx); 109 auto runner = new QueryRunner<DomainType>(query, mResourceContext, bufferTypeForDomainType(), ctx);
109 runner->setResultTransformation(mResultTransformation); 110 runner->setResultTransformation(mResultTransformation);
diff --git a/common/facade.h b/common/facade.h
index 45c718b..0ec2e59 100644
--- a/common/facade.h
+++ b/common/facade.h
@@ -48,7 +48,6 @@ template <typename DomainType>
48class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType> 48class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType>
49{ 49{
50protected: 50protected:
51 SINK_DEBUG_AREA("facade")
52 SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier) 51 SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier)
53public: 52public:
54 /** 53 /**
diff --git a/common/genericresource.h b/common/genericresource.h
index 558145c..bffc697 100644
--- a/common/genericresource.h
+++ b/common/genericresource.h
@@ -35,8 +35,6 @@ class CommandProcessor;
35 */ 35 */
36class SINK_EXPORT GenericResource : public Resource 36class SINK_EXPORT GenericResource : public Resource
37{ 37{
38protected:
39 SINK_DEBUG_AREA("resource")
40public: 38public:
41 GenericResource(const Sink::ResourceContext &context, const QSharedPointer<Pipeline> &pipeline = QSharedPointer<Pipeline>()); 39 GenericResource(const Sink::ResourceContext &context, const QSharedPointer<Pipeline> &pipeline = QSharedPointer<Pipeline>());
42 virtual ~GenericResource(); 40 virtual ~GenericResource();
diff --git a/common/listener.cpp b/common/listener.cpp
index 983e438..c9fd9d3 100644
--- a/common/listener.cpp
+++ b/common/listener.cpp
@@ -405,6 +405,7 @@ void Listener::updateClientsWithRevision(qint64 revision)
405 405
406 SinkTrace() << "Sending revision update for " << client.name << revision; 406 SinkTrace() << "Sending revision update for " << client.name << revision;
407 Sink::Commands::write(client.socket, ++m_messageId, Sink::Commands::RevisionUpdateCommand, m_fbb); 407 Sink::Commands::write(client.socket, ++m_messageId, Sink::Commands::RevisionUpdateCommand, m_fbb);
408 client.socket->flush();
408 } 409 }
409 m_fbb.Clear(); 410 m_fbb.Clear();
410} 411}
@@ -420,6 +421,8 @@ void Listener::notify(const Sink::Notification &notification)
420 builder.add_identifier(idString); 421 builder.add_identifier(idString);
421 builder.add_message(messageString); 422 builder.add_message(messageString);
422 builder.add_entities(entities); 423 builder.add_entities(entities);
424 builder.add_progress(notification.progress);
425 builder.add_total(notification.total);
423 auto command = builder.Finish(); 426 auto command = builder.Finish();
424 Sink::Commands::FinishNotificationBuffer(m_fbb, command); 427 Sink::Commands::FinishNotificationBuffer(m_fbb, command);
425 for (Client &client : m_connections) { 428 for (Client &client : m_connections) {
diff --git a/common/listener.h b/common/listener.h
index 8d8b529..f29130d 100644
--- a/common/listener.h
+++ b/common/listener.h
@@ -55,8 +55,6 @@ public:
55class SINK_EXPORT Listener : public QObject 55class SINK_EXPORT Listener : public QObject
56{ 56{
57 Q_OBJECT 57 Q_OBJECT
58 SINK_DEBUG_AREA("communication")
59
60public: 58public:
61 Listener(const QByteArray &resourceName, const QByteArray &resourceType, QObject *parent = 0); 59 Listener(const QByteArray &resourceName, const QByteArray &resourceType, QObject *parent = 0);
62 ~Listener(); 60 ~Listener();
diff --git a/common/log.cpp b/common/log.cpp
index 045b3cc..5dfb872 100644
--- a/common/log.cpp
+++ b/common/log.cpp
@@ -1,6 +1,7 @@
1#include "log.h" 1#include "log.h"
2 2
3#include <QString> 3#include <QString>
4#include <QDir>
4#include <QIODevice> 5#include <QIODevice>
5#include <QCoreApplication> 6#include <QCoreApplication>
6#include <QSettings> 7#include <QSettings>
@@ -12,12 +13,18 @@
12#include <memory> 13#include <memory>
13#include <atomic> 14#include <atomic>
14#include <definitions.h> 15#include <definitions.h>
16#include <QThreadStorage>
17#include <QStringBuilder>
15 18
16using namespace Sink::Log; 19using namespace Sink::Log;
17 20
18static QSharedPointer<QSettings> config() 21static QThreadStorage<QSharedPointer<QSettings>> sSettings;
22static QSettings &config()
19{ 23{
20 return QSharedPointer<QSettings>::create(Sink::configLocation() + "/log.ini", QSettings::IniFormat); 24 if (!sSettings.hasLocalData()) {
25 sSettings.setLocalData(QSharedPointer<QSettings>::create(Sink::configLocation() + "/log.ini", QSettings::IniFormat));
26 }
27 return *sSettings.localData();
21} 28}
22 29
23static QByteArray sPrimaryComponent; 30static QByteArray sPrimaryComponent;
@@ -173,22 +180,22 @@ DebugLevel Sink::Log::debugLevelFromName(const QByteArray &name)
173 180
174void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel) 181void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel)
175{ 182{
176 config()->setValue("level", debugLevel); 183 config().setValue("level", debugLevel);
177} 184}
178 185
179Sink::Log::DebugLevel Sink::Log::debugOutputLevel() 186Sink::Log::DebugLevel Sink::Log::debugOutputLevel()
180{ 187{
181 return static_cast<Sink::Log::DebugLevel>(config()->value("level", Sink::Log::Log).toInt()); 188 return static_cast<Sink::Log::DebugLevel>(config().value("level", Sink::Log::Log).toInt());
182} 189}
183 190
184void Sink::Log::setDebugOutputFilter(FilterType type, const QByteArrayList &filter) 191void Sink::Log::setDebugOutputFilter(FilterType type, const QByteArrayList &filter)
185{ 192{
186 switch (type) { 193 switch (type) {
187 case ApplicationName: 194 case ApplicationName:
188 config()->setValue("applicationfilter", QVariant::fromValue(filter)); 195 config().setValue("applicationfilter", QVariant::fromValue(filter));
189 break; 196 break;
190 case Area: 197 case Area:
191 config()->setValue("areafilter", QVariant::fromValue(filter)); 198 config().setValue("areafilter", QVariant::fromValue(filter));
192 break; 199 break;
193 } 200 }
194} 201}
@@ -197,9 +204,9 @@ QByteArrayList Sink::Log::debugOutputFilter(FilterType type)
197{ 204{
198 switch (type) { 205 switch (type) {
199 case ApplicationName: 206 case ApplicationName:
200 return config()->value("applicationfilter").value<QByteArrayList>(); 207 return config().value("applicationfilter").value<QByteArrayList>();
201 case Area: 208 case Area:
202 return config()->value("areafilter").value<QByteArrayList>(); 209 return config().value("areafilter").value<QByteArrayList>();
203 default: 210 default:
204 return QByteArrayList(); 211 return QByteArrayList();
205 } 212 }
@@ -207,12 +214,12 @@ QByteArrayList Sink::Log::debugOutputFilter(FilterType type)
207 214
208void Sink::Log::setDebugOutputFields(const QByteArrayList &output) 215void Sink::Log::setDebugOutputFields(const QByteArrayList &output)
209{ 216{
210 config()->setValue("outputfields", QVariant::fromValue(output)); 217 config().setValue("outputfields", QVariant::fromValue(output));
211} 218}
212 219
213QByteArrayList Sink::Log::debugOutputFields() 220QByteArrayList Sink::Log::debugOutputFields()
214{ 221{
215 return config()->value("outputfields").value<QByteArrayList>(); 222 return config().value("outputfields").value<QByteArrayList>();
216} 223}
217 224
218static QByteArray getProgramName() 225static QByteArray getProgramName()
@@ -277,14 +284,16 @@ static bool containsItemStartingWith(const QByteArray &pattern, const QByteArray
277 for (const auto &item : list) { 284 for (const auto &item : list) {
278 if (item.startsWith('*')) { 285 if (item.startsWith('*')) {
279 auto stripped = item.mid(1); 286 auto stripped = item.mid(1);
280 stripped.endsWith('*'); 287 if (stripped.endsWith('*')) {
281 stripped.chop(1); 288 stripped.chop(1);
289 }
282 if (pattern.contains(stripped)) { 290 if (pattern.contains(stripped)) {
283 return true; 291 return true;
284 } 292 }
285 } 293 } else {
286 if (pattern.startsWith(item)) { 294 if (pattern.contains(item)) {
287 return true; 295 return true;
296 }
288 } 297 }
289 } 298 }
290 return false; 299 return false;
@@ -300,25 +309,51 @@ static bool caseInsensitiveContains(const QByteArray &pattern, const QByteArrayL
300 return false; 309 return false;
301} 310}
302 311
303QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) 312static QByteArray getFileName(const char *file)
304{ 313{
305 static NullStream nullstream; 314 static char sep = QDir::separator().toLatin1();
306 if (debugLevel < debugOutputLevel()) { 315 auto filename = QByteArray(file).split(sep).last();
307 return QDebug(&nullstream); 316 return filename.split('.').first();
308 } 317}
309 318
319static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file)
320{
310 if (sPrimaryComponent.isEmpty()) { 321 if (sPrimaryComponent.isEmpty()) {
311 sPrimaryComponent = getProgramName(); 322 sPrimaryComponent = getProgramName();
312 } 323 }
313 QString fullDebugArea = sPrimaryComponent + "." + (debugComponent ? (QString::fromLatin1(debugComponent) + ".") : "") + (debugArea ? QString::fromLatin1(debugArea) : ""); 324 //Using stringbuilder for fewer allocations
325 return QLatin1String{sPrimaryComponent} % QLatin1String{"."} %
326 (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) %
327 (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)});
328}
329
330static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea)
331{
332 if (debugLevel < debugOutputLevel()) {
333 return true;
334 }
335 const auto areas = debugOutputFilter(Sink::Log::Area);
336 if ((debugLevel <= Sink::Log::Trace) && !areas.isEmpty()) {
337 if (!containsItemStartingWith(fullDebugArea, areas)) {
338 return true;
339 }
340 }
341 return false;
342}
314 343
344bool Sink::Log::isFiltered(DebugLevel debugLevel, const char *debugArea, const char *debugComponent, const char *file)
345{
346 return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1());
347}
348
349QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent)
350{
351 const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file);
315 collectDebugArea(fullDebugArea); 352 collectDebugArea(fullDebugArea);
316 353
317 auto areas = debugOutputFilter(Sink::Log::Area); 354 static NullStream nullstream;
318 if (debugLevel <= Sink::Log::Trace && !areas.isEmpty()) { 355 if (isFiltered(debugLevel, fullDebugArea.toLatin1())) {
319 if (!containsItemStartingWith(fullDebugArea.toUtf8(), areas)) { 356 return QDebug(&nullstream);
320 return QDebug(&nullstream);
321 }
322 } 357 }
323 358
324 QString prefix; 359 QString prefix;
diff --git a/common/log.h b/common/log.h
index e85856a..8266fdb 100644
--- a/common/log.h
+++ b/common/log.h
@@ -85,30 +85,35 @@ SINK_EXPORT inline QDebug operator<<(QDebug d, const TraceTime &time)
85 d << time.time << "[ms]"; 85 d << time.time << "[ms]";
86 return d; 86 return d;
87} 87}
88
89SINK_EXPORT bool isFiltered(DebugLevel debugLevel, const char *debugArea, const char *debugComponent, const char *file);
90
88} 91}
89} 92}
90 93
91static const char *getComponentName() { return nullptr; } 94static const char *getComponentName() { return nullptr; }
92 95
93#define Trace_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, AREA) 96#define SINK_DEBUG_STREAM_IMPL(LEVEL, AREA, COMPONENT) if (!Sink::Log::isFiltered(LEVEL, AREA, COMPONENT, __FILE__)) Sink::Log::debugStream(LEVEL, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT)
94#define Log_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, AREA) 97
95#define Warning_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, AREA) 98#define Trace_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, AREA, nullptr)
96#define Error_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, AREA) 99#define Log_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, AREA, nullptr)
97 100#define Warning_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, AREA, nullptr)
98#define SinkTrace_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) 101#define Error_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, AREA, nullptr)
99#define SinkLog_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) 102
100#define SinkWarning_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) 103#define SinkTrace_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, AREA, COMPONENT)
101#define SinkError_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) 104#define SinkLog_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, AREA, COMPONENT)
102 105#define SinkWarning_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, AREA, COMPONENT)
103#define SinkTrace() Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) 106#define SinkError_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, AREA, COMPONENT)
104#define SinkLog() Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) 107
105#define SinkWarning() Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) 108#define SinkTrace() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, nullptr, getComponentName())
106#define SinkError() Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) 109#define SinkLog() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, nullptr, getComponentName())
107 110#define SinkWarning() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, nullptr, getComponentName())
108#define SinkTraceCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) 111#define SinkError() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, nullptr, getComponentName())
109#define SinkLogCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) 112
110#define SinkWarningCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) 113#define SinkTraceCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, CTX.name, nullptr)
111#define SinkErrorCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) 114#define SinkLogCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, CTX.name, nullptr)
115#define SinkWarningCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, CTX.name, nullptr)
116#define SinkErrorCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, CTX.name, nullptr)
112 117
113#define SINK_DEBUG_AREA(AREA) static constexpr const char* s_sinkDebugArea{AREA}; 118#define SINK_DEBUG_AREA(AREA) static constexpr const char* s_sinkDebugArea{AREA};
114#define SINK_DEBUG_COMPONENT(COMPONENT) const char* getComponentName() const { return COMPONENT; }; 119#define SINK_DEBUG_COMPONENT(COMPONENT) const char* getComponentName() const { return COMPONENT; };
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp
index d91ab5f..4171d85 100644
--- a/common/mail/threadindexer.cpp
+++ b/common/mail/threadindexer.cpp
@@ -21,74 +21,15 @@
21#include "typeindex.h" 21#include "typeindex.h"
22#include "log.h" 22#include "log.h"
23 23
24SINK_DEBUG_AREA("threadindex")
25
26using namespace Sink; 24using namespace Sink;
27using namespace Sink::ApplicationDomain; 25using namespace Sink::ApplicationDomain;
28 26
29static QString stripOffPrefixes(const QString &subject)
30{
31 //TODO this hardcoded list is probably not good enough (especially regarding internationalization)
32 //TODO this whole routine, including internationalized re/fwd ... should go into some library.
33 //We'll require the same for generating reply/forward subjects in kube
34 static QStringList defaultReplyPrefixes = QStringList() << QLatin1String("Re\\s*:")
35 << QLatin1String("Re\\[\\d+\\]:")
36 << QLatin1String("Re\\d+:");
37
38 static QStringList defaultForwardPrefixes = QStringList() << QLatin1String("Fwd:")
39 << QLatin1String("FW:");
40
41 QStringList replyPrefixes; // = GlobalSettings::self()->replyPrefixes();
42 if (replyPrefixes.isEmpty()) {
43 replyPrefixes = defaultReplyPrefixes;
44 }
45
46 QStringList forwardPrefixes; // = GlobalSettings::self()->forwardPrefixes();
47 if (forwardPrefixes.isEmpty()) {
48 forwardPrefixes = defaultReplyPrefixes;
49 }
50
51 const QStringList prefixRegExps = replyPrefixes + forwardPrefixes;
52
53 // construct a big regexp that
54 // 1. is anchored to the beginning of str (sans whitespace)
55 // 2. matches at least one of the part regexps in prefixRegExps
56 const QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QLatin1String(")|(?:")));
57
58 static QString regExpPattern;
59 static QRegExp regExp;
60
61 regExp.setCaseSensitivity(Qt::CaseInsensitive);
62 if (regExpPattern != bigRegExp) {
63 // the prefixes have changed, so update the regexp
64 regExpPattern = bigRegExp;
65 regExp.setPattern(regExpPattern);
66 }
67
68 if(regExp.isValid()) {
69 QString tmp = subject;
70 if (regExp.indexIn( tmp ) == 0) {
71 return tmp.remove(0, regExp.matchedLength());
72 }
73 } else {
74 SinkWarning() << "bigRegExp = \""
75 << bigRegExp << "\"\n"
76 << "prefix regexp is invalid!";
77 }
78
79 return subject;
80}
81
82
83void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) 27void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction)
84{ 28{
85 auto messageId = entity.getProperty(Mail::MessageId::name); 29 auto messageId = entity.getProperty(Mail::MessageId::name);
86 auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name); 30 auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name);
87 const auto subject = entity.getProperty(Mail::Subject::name);
88 const auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8();
89 if (messageId.toByteArray().isEmpty()) { 31 if (messageId.toByteArray().isEmpty()) {
90 SinkWarning() << "Found an email without messageId. This is illegal and threading will break. Entity id: " << identifier; 32 SinkWarning() << "Found an email without messageId. This is illegal and threading will break. Entity id: " << identifier;
91 SinkWarning() << "Subject: " << subject;
92 } 33 }
93 34
94 QVector<QByteArray> thread; 35 QVector<QByteArray> thread;
@@ -101,18 +42,9 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App
101 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); 42 thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId);
102 SinkTrace() << "Found parent: " << thread; 43 SinkTrace() << "Found parent: " << thread;
103 } 44 }
104
105 if (thread.isEmpty()) { 45 if (thread.isEmpty()) {
106 //Try to lookup the thread by subject if not empty 46 thread << QUuid::createUuid().toByteArray();
107 if ( !normalizedSubject.isEmpty()) { 47 SinkTrace() << "Created a new thread: " << thread;
108 thread = index().secondaryLookup<Mail::Subject, Mail::ThreadId>(normalizedSubject);
109 }
110 if (thread.isEmpty()) {
111 thread << QUuid::createUuid().toByteArray();
112 SinkTrace() << "Created a new thread: " << thread;
113 } else {
114 SinkTrace() << "Found thread by subject: " << thread;
115 }
116 } 48 }
117 49
118 Q_ASSERT(!thread.isEmpty()); 50 Q_ASSERT(!thread.isEmpty());
@@ -124,9 +56,6 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App
124 } 56 }
125 index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction); 57 index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction);
126 index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction); 58 index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction);
127 if (!normalizedSubject.isEmpty()) {
128 index().index<Mail::Subject, Mail::ThreadId>(normalizedSubject, thread.first(), transaction);
129 }
130} 59}
131 60
132 61
@@ -142,6 +71,15 @@ void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &old,
142 71
143void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) 72void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity)
144{ 73{
74 auto messageId = entity.getProperty(Mail::MessageId::name);
75 auto thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId);
76 index().unindex<Mail::MessageId, Mail::ThreadId>(messageId.toByteArray(), thread.first(), transaction());
77 index().unindex<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId.toByteArray(), transaction());
78}
145 79
80QMap<QByteArray, int> ThreadIndexer::databases()
81{
82 return {{"mail.index.messageIdthreadId", 1},
83 {"mail.index.threadIdmessageId", 1}};
146} 84}
147 85
diff --git a/common/mail/threadindexer.h b/common/mail/threadindexer.h
index 064ae71..60d0863 100644
--- a/common/mail/threadindexer.h
+++ b/common/mail/threadindexer.h
@@ -29,6 +29,7 @@ public:
29 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; 29 virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
30 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; 30 virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
31 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; 31 virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE;
32 static QMap<QByteArray, int> databases();
32private: 33private:
33 void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); 34 void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction);
34}; 35};
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp
index dff3b3d..5c54fbc 100644
--- a/common/mailpreprocessor.cpp
+++ b/common/mailpreprocessor.cpp
@@ -29,8 +29,6 @@
29 29
30using namespace Sink; 30using namespace Sink;
31 31
32SINK_DEBUG_AREA("mailpreprocessor")
33
34QString MailPropertyExtractor::getFilePathFromMimeMessagePath(const QString &s) const 32QString MailPropertyExtractor::getFilePathFromMimeMessagePath(const QString &s) const
35{ 33{
36 return s; 34 return s;
diff --git a/common/messagequeue.cpp b/common/messagequeue.cpp
index 6e79d89..362ddfd 100644
--- a/common/messagequeue.cpp
+++ b/common/messagequeue.cpp
@@ -3,8 +3,6 @@
3#include <QDebug> 3#include <QDebug>
4#include <log.h> 4#include <log.h>
5 5
6SINK_DEBUG_AREA("messagequeue")
7
8MessageQueue::MessageQueue(const QString &storageRoot, const QString &name) : mStorage(storageRoot, name, Sink::Storage::DataStore::ReadWrite) 6MessageQueue::MessageQueue(const QString &storageRoot, const QString &name) : mStorage(storageRoot, name, Sink::Storage::DataStore::ReadWrite)
9{ 7{
10} 8}
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
index b12216b..58703ab 100644
--- a/common/modelresult.cpp
+++ b/common/modelresult.cpp
@@ -224,7 +224,6 @@ QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &p
224 return createIndex(row, column, childId); 224 return createIndex(row, column, childId);
225 } 225 }
226 SinkWarningCtx(mLogCtx) << "Index not available " << row << column << parent; 226 SinkWarningCtx(mLogCtx) << "Index not available " << row << column << parent;
227 Q_ASSERT(false);
228 return QModelIndex(); 227 return QModelIndex();
229} 228}
230 229
diff --git a/common/notification.cpp b/common/notification.cpp
index e688b6d..da31e20 100644
--- a/common/notification.cpp
+++ b/common/notification.cpp
@@ -48,8 +48,8 @@ static QByteArray name(int type)
48 48
49QDebug operator<<(QDebug dbg, const Sink::Notification &n) 49QDebug operator<<(QDebug dbg, const Sink::Notification &n)
50{ 50{
51 dbg << "Notification(Type: " << name(n.type) << "Id, : " << n.id << ", Code: "; 51 dbg << "Notification(Type:" << name(n.type) << "Id, :" << n.id << ", Code:";
52 dbg << n.code; 52 dbg << n.code;
53 dbg << ", Message: " << n.message << ", Entities: " << n.entities << ")"; 53 dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")";
54 return dbg.space(); 54 return dbg.space();
55} 55}
diff --git a/common/notification.h b/common/notification.h
index f5379fd..30e240c 100644
--- a/common/notification.h
+++ b/common/notification.h
@@ -56,6 +56,8 @@ public:
56 QString message; 56 QString message;
57 //A return code. Zero typically indicates success. 57 //A return code. Zero typically indicates success.
58 int code = 0; 58 int code = 0;
59 int progress = 0;
60 int total = 0;
59 QByteArray resource; 61 QByteArray resource;
60}; 62};
61} 63}
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index f5cf995..f29c7db 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -283,6 +283,8 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
283 case Preprocessor::DropModification: 283 case Preprocessor::DropModification:
284 SinkTraceCtx(d->logCtx) << "Dropping modification"; 284 SinkTraceCtx(d->logCtx) << "Dropping modification";
285 return KAsync::error<qint64>(0); 285 return KAsync::error<qint64>(0);
286 case Preprocessor::NoAction:
287 case Preprocessor::DeleteEntity:
286 default: 288 default:
287 break; 289 break;
288 } 290 }
@@ -301,8 +303,8 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
301 SinkTraceCtx(d->logCtx) << "Move of " << current.identifier() << "was successfull"; 303 SinkTraceCtx(d->logCtx) << "Move of " << current.identifier() << "was successfull";
302 if (isMove) { 304 if (isMove) {
303 flatbuffers::FlatBufferBuilder fbb; 305 flatbuffers::FlatBufferBuilder fbb;
304 auto entityId = fbb.CreateString(current.identifier()); 306 auto entityId = fbb.CreateString(current.identifier().toStdString());
305 auto type = fbb.CreateString(bufferType); 307 auto type = fbb.CreateString(bufferType.toStdString());
306 auto location = Sink::Commands::CreateDeleteEntity(fbb, current.revision(), entityId, type, true); 308 auto location = Sink::Commands::CreateDeleteEntity(fbb, current.revision(), entityId, type, true);
307 Sink::Commands::FinishDeleteEntityBuffer(fbb, location); 309 Sink::Commands::FinishDeleteEntityBuffer(fbb, location);
308 const auto data = BufferUtils::extractBuffer(fbb); 310 const auto data = BufferUtils::extractBuffer(fbb);
@@ -448,8 +450,8 @@ void Preprocessor::createEntity(const Sink::ApplicationDomain::ApplicationDomain
448 const auto entityBuffer = BufferUtils::extractBuffer(entityFbb); 450 const auto entityBuffer = BufferUtils::extractBuffer(entityFbb);
449 451
450 flatbuffers::FlatBufferBuilder fbb; 452 flatbuffers::FlatBufferBuilder fbb;
451 auto entityId = fbb.CreateString(entity.identifier()); 453 auto entityId = fbb.CreateString(entity.identifier().toStdString());
452 auto type = fbb.CreateString(typeName); 454 auto type = fbb.CreateString(typeName.toStdString());
453 auto delta = Sink::EntityBuffer::appendAsVector(fbb, entityBuffer.constData(), entityBuffer.size()); 455 auto delta = Sink::EntityBuffer::appendAsVector(fbb, entityBuffer.constData(), entityBuffer.size());
454 auto location = Sink::Commands::CreateCreateEntity(fbb, entityId, type, delta); 456 auto location = Sink::Commands::CreateCreateEntity(fbb, entityId, type, delta);
455 Sink::Commands::FinishCreateEntityBuffer(fbb, location); 457 Sink::Commands::FinishCreateEntityBuffer(fbb, location);
diff --git a/common/propertymapper.cpp b/common/propertymapper.cpp
index c72cf31..dbf93a3 100644
--- a/common/propertymapper.cpp
+++ b/common/propertymapper.cpp
@@ -21,6 +21,7 @@
21 21
22#include "applicationdomaintype.h" 22#include "applicationdomaintype.h"
23#include <QDateTime> 23#include <QDateTime>
24#include <QDataStream>
24#include "mail_generated.h" 25#include "mail_generated.h"
25#include "contact_generated.h" 26#include "contact_generated.h"
26 27
@@ -57,7 +58,9 @@ template <>
57flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) 58flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb)
58{ 59{
59 if (property.isValid()) { 60 if (property.isValid()) {
60 return fbb.CreateString(property.toByteArray().toStdString()).o; 61 const auto ba = property.toByteArray();
62 const auto s = fbb.CreateString(ba.constData(), ba.size());
63 return s.o;
61 } 64 }
62 return 0; 65 return 0;
63} 66}
@@ -66,7 +69,10 @@ template <>
66flatbuffers::uoffset_t variantToProperty<QDateTime>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) 69flatbuffers::uoffset_t variantToProperty<QDateTime>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb)
67{ 70{
68 if (property.isValid()) { 71 if (property.isValid()) {
69 return fbb.CreateString(property.toDateTime().toString().toStdString()).o; 72 QByteArray ba;
73 QDataStream ds(&ba, QIODevice::WriteOnly);
74 ds << property.toDateTime();
75 return fbb.CreateString(ba.toStdString()).o;
70 } 76 }
71 return 0; 77 return 0;
72} 78}
@@ -131,7 +137,7 @@ QString propertyToString(const flatbuffers::String *property)
131{ 137{
132 if (property) { 138 if (property) {
133 // We have to copy the memory, otherwise it would become eventually invalid 139 // We have to copy the memory, otherwise it would become eventually invalid
134 return QString::fromStdString(property->c_str()); 140 return QString::fromStdString(property->str());
135 } 141 }
136 return QString(); 142 return QString();
137} 143}
@@ -141,7 +147,7 @@ QVariant propertyToVariant<QString>(const flatbuffers::String *property)
141{ 147{
142 if (property) { 148 if (property) {
143 // We have to copy the memory, otherwise it would become eventually invalid 149 // We have to copy the memory, otherwise it would become eventually invalid
144 return QString::fromStdString(property->c_str()); 150 return QString::fromStdString(property->str());
145 } 151 }
146 return QVariant(); 152 return QVariant();
147} 153}
@@ -151,7 +157,7 @@ QVariant propertyToVariant<Sink::ApplicationDomain::BLOB>(const flatbuffers::Str
151{ 157{
152 if (property) { 158 if (property) {
153 // We have to copy the memory, otherwise it would become eventually invalid 159 // We have to copy the memory, otherwise it would become eventually invalid
154 auto s = QString::fromStdString(property->c_str()); 160 auto s = QString::fromStdString(property->str());
155 auto ext = s.endsWith(":ext"); 161 auto ext = s.endsWith(":ext");
156 s.chop(4); 162 s.chop(4);
157 163
@@ -167,7 +173,7 @@ QVariant propertyToVariant<Sink::ApplicationDomain::Reference>(const flatbuffers
167{ 173{
168 if (property) { 174 if (property) {
169 // We have to copy the memory, otherwise it would become eventually invalid 175 // We have to copy the memory, otherwise it would become eventually invalid
170 return QVariant::fromValue(Sink::ApplicationDomain::Reference{QString::fromStdString(property->c_str()).toUtf8()}); 176 return QVariant::fromValue(Sink::ApplicationDomain::Reference{QString::fromStdString(property->str()).toUtf8()});
171 } 177 }
172 return QVariant(); 178 return QVariant();
173} 179}
@@ -177,7 +183,7 @@ QVariant propertyToVariant<QByteArray>(const flatbuffers::String *property)
177{ 183{
178 if (property) { 184 if (property) {
179 // We have to copy the memory, otherwise it would become eventually invalid 185 // We have to copy the memory, otherwise it would become eventually invalid
180 return QString::fromStdString(property->c_str()).toUtf8(); 186 return QByteArray(property->c_str(), property->Length());
181 } 187 }
182 return QVariant(); 188 return QVariant();
183} 189}
@@ -199,7 +205,7 @@ QVariant propertyToVariant<QByteArrayList>(const flatbuffers::Vector<flatbuffers
199 QByteArrayList list; 205 QByteArrayList list;
200 for (auto it = property->begin(); it != property->end();) { 206 for (auto it = property->begin(); it != property->end();) {
201 // We have to copy the memory, otherwise it would become eventually invalid 207 // We have to copy the memory, otherwise it would become eventually invalid
202 list << QString::fromStdString((*it)->c_str()).toUtf8(); 208 list << QString::fromStdString((*it)->str()).toUtf8();
203 it.operator++(); 209 it.operator++();
204 } 210 }
205 return QVariant::fromValue(list); 211 return QVariant::fromValue(list);
@@ -256,8 +262,11 @@ template <>
256QVariant propertyToVariant<QDateTime>(const flatbuffers::String *property) 262QVariant propertyToVariant<QDateTime>(const flatbuffers::String *property)
257{ 263{
258 if (property) { 264 if (property) {
259 // We have to copy the memory, otherwise it would become eventually invalid 265 auto ba = QByteArray::fromRawData(property->c_str(), property->size());
260 return QDateTime::fromString(QString::fromStdString(property->c_str())); 266 QDateTime dt;
267 QDataStream ds(&ba, QIODevice::ReadOnly);
268 ds >> dt;
269 return dt;
261 } 270 }
262 return QVariant(); 271 return QVariant();
263} 272}
diff --git a/common/propertymapper.h b/common/propertymapper.h
index 9ea0b73..fd24278 100644
--- a/common/propertymapper.h
+++ b/common/propertymapper.h
@@ -65,13 +65,19 @@ QVariant SINK_EXPORT propertyToVariant(const flatbuffers::Vector<flatbuffers::Of
65 * a virtual method per property, the property mapper can be filled with accessors 65 * a virtual method per property, the property mapper can be filled with accessors
66 * that extract the properties from resource types. 66 * that extract the properties from resource types.
67 */ 67 */
68template <typename BufferType> 68class PropertyMapper
69class ReadPropertyMapper
70{ 69{
71public: 70public:
72 virtual ~ReadPropertyMapper(){}; 71 virtual ~PropertyMapper(){};
73 72
74 virtual QVariant getProperty(const QByteArray &key, BufferType const *buffer) const 73 template <typename T, typename Buffer, typename BufferBuilder, typename FunctionReturnValue, typename Arg>
74 void addMapping(FunctionReturnValue (Buffer::*f)() const, void (BufferBuilder::*f2)(Arg))
75 {
76 addReadMapping<T, Buffer, FunctionReturnValue>(f);
77 addWriteMapping<T, BufferBuilder>(f2);
78 }
79
80 virtual QVariant getProperty(const QByteArray &key, void const *buffer) const
75 { 81 {
76 if (mReadAccessors.contains(key)) { 82 if (mReadAccessors.contains(key)) {
77 auto accessor = mReadAccessors.value(key); 83 auto accessor = mReadAccessors.value(key);
@@ -79,163 +85,69 @@ public:
79 } 85 }
80 return QVariant(); 86 return QVariant();
81 } 87 }
82 bool hasMapping(const QByteArray &key) const
83 {
84 return mReadAccessors.contains(key);
85 }
86 QList<QByteArray> availableProperties() const
87 {
88 return mReadAccessors.keys();
89 }
90 void addMapping(const QByteArray &property, const std::function<QVariant(BufferType const *)> &mapping)
91 {
92 mReadAccessors.insert(property, mapping);
93 }
94
95 template <typename T, typename Buffer>
96 void addMapping(const flatbuffers::String *(Buffer::*f)() const)
97 {
98 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
99 }
100
101 template <typename T, typename Buffer>
102 void addMapping(uint8_t (Buffer::*f)() const)
103 {
104 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
105 }
106
107 template <typename T, typename Buffer>
108 void addMapping(bool (Buffer::*f)() const)
109 {
110 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
111 }
112
113 template <typename T, typename Buffer>
114 void addMapping(const flatbuffers::Vector<uint8_t> *(Buffer::*f)() const)
115 {
116 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
117 }
118
119 template <typename T, typename Buffer>
120 void addMapping(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *(Buffer::*f)() const)
121 {
122 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
123 }
124
125 template <typename T, typename Buffer>
126 void addMapping(const Sink::ApplicationDomain::Buffer::MailContact *(Buffer::*f)() const)
127 {
128 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
129 }
130
131 template <typename T, typename Buffer>
132 void addMapping(const flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>> *(Buffer::*f)() const)
133 {
134 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
135 }
136 88
137 template <typename T, typename Buffer> 89 virtual void setProperty(const QByteArray &key, const QVariant &value, QList<std::function<void(void *builder)>> &builderCalls, flatbuffers::FlatBufferBuilder &fbb) const
138 void addMapping(const flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::ContactEmail>> *(Buffer::*f)() const)
139 {
140 addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); });
141 }
142
143private:
144 QHash<QByteArray, std::function<QVariant(BufferType const *)>> mReadAccessors;
145};
146
147template <typename BufferBuilder>
148class WritePropertyMapper
149{
150public:
151 virtual ~WritePropertyMapper(){};
152
153 virtual void setProperty(const QByteArray &key, const QVariant &value, QList<std::function<void(BufferBuilder &)>> &builderCalls, flatbuffers::FlatBufferBuilder &fbb) const
154 { 90 {
155 if (mWriteAccessors.contains(key)) { 91 if (mWriteAccessors.contains(key)) {
156 auto accessor = mWriteAccessors.value(key); 92 auto accessor = mWriteAccessors.value(key);
157 builderCalls << accessor(value, fbb); 93 builderCalls << accessor(value, fbb);
158 } 94 }
159 } 95 }
96
160 bool hasMapping(const QByteArray &key) const 97 bool hasMapping(const QByteArray &key) const
161 { 98 {
162 return mWriteAccessors.contains(key); 99 return mReadAccessors.contains(key);
163 }
164 void addMapping(const QByteArray &property, const std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> &mapping)
165 {
166 mWriteAccessors.insert(property, mapping);
167 } 100 }
168 101
169 template <typename T> 102 QList<QByteArray> availableProperties() const
170 void addMapping(void (BufferBuilder::*f)(uint8_t))
171 { 103 {
172 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 104 return mReadAccessors.keys();
173 return [value, f](BufferBuilder &builder) { (builder.*f)(value.value<typename T::Type>()); };
174 });
175 } 105 }
176 106
177 template <typename T> 107private:
178 void addMapping(void (BufferBuilder::*f)(bool)) 108 void addReadMapping(const QByteArray &property, const std::function<QVariant(void const *)> &mapping)
179 { 109 {
180 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 110 mReadAccessors.insert(property, mapping);
181 return [value, f](BufferBuilder &builder) { (builder.*f)(value.value<typename T::Type>()); };
182 });
183 } 111 }
184 112
185 template <typename T> 113 template <typename T, typename Buffer, typename FunctionReturnValue>
186 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::String>)) 114 void addReadMapping(FunctionReturnValue (Buffer::*f)() const)
187 { 115 {
188 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 116 addReadMapping(T::name, [f](void const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((static_cast<const Buffer*>(buffer)->*f)()); });
189 auto offset = variantToProperty<typename T::Type>(value, fbb);
190 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); };
191 });
192 } 117 }
193 118
194 template <typename T>
195 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<uint8_t>>))
196 {
197 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> {
198 auto offset = variantToProperty<typename T::Type>(value, fbb);
199 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); };
200 });
201 }
202 119
203 template <typename T> 120 void addWriteMapping(const QByteArray &property, const std::function<std::function<void(void *builder)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> &mapping)
204 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>>))
205 { 121 {
206 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 122 mWriteAccessors.insert(property, mapping);
207 auto offset = variantToProperty<typename T::Type>(value, fbb);
208 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); };
209 });
210 } 123 }
211 124
212 template <typename T> 125 template <typename T, typename BufferBuilder>
213 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>)) 126 void addWriteMapping(void (BufferBuilder::*f)(uint8_t))
214 { 127 {
215 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 128 addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> {
216 auto offset = variantToProperty<typename T::Type>(value, fbb); 129 return [value, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(value.value<typename T::Type>()); };
217 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); };
218 }); 130 });
219 } 131 }
220 132
221 template <typename T> 133 template <typename T, typename BufferBuilder>
222 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>>>)) 134 void addWriteMapping(void (BufferBuilder::*f)(bool))
223 { 135 {
224 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 136 addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> {
225 auto offset = variantToProperty<typename T::Type>(value, fbb); 137 return [value, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(value.value<typename T::Type>()); };
226 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); };
227 }); 138 });
228 } 139 }
229 140
230 template <typename T> 141 template <typename T, typename BufferBuilder, typename Arg>
231 void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::ContactEmail>>>)) 142 void addWriteMapping(void (BufferBuilder::*f)(flatbuffers::Offset<Arg>))
232 { 143 {
233 addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { 144 addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> {
234 auto offset = variantToProperty<typename T::Type>(value, fbb); 145 auto offset = variantToProperty<typename T::Type>(value, fbb);
235 return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; 146 return [offset, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(offset); };
236 }); 147 });
237 } 148 }
238 149
239private: 150 QHash<QByteArray, std::function<QVariant(void const *)>> mReadAccessors;
240 QHash<QByteArray, std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)>> mWriteAccessors; 151 QHash<QByteArray, std::function<std::function<void(void *builder)>(const QVariant &, flatbuffers::FlatBufferBuilder &)>> mWriteAccessors;
241}; 152};
153
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp
index 43f48c0..f196965 100644
--- a/common/queryrunner.cpp
+++ b/common/queryrunner.cpp
@@ -231,11 +231,11 @@ ReplayResult QueryWorker<DomainType>::executeIncrementalQuery(const Sink::Query
231 auto replayResult = resultSet.replaySet(0, 0, [this, query, &resultProvider](const ResultSet::Result &result) { 231 auto replayResult = resultSet.replaySet(0, 0, [this, query, &resultProvider](const ResultSet::Result &result) {
232 resultProviderCallback(query, resultProvider, result); 232 resultProviderCallback(query, resultProvider, result);
233 }); 233 });
234 234 preparedQuery.updateComplete();
235 SinkTraceCtx(mLogCtx) << "Replayed " << replayResult.replayedEntities << " results.\n" 235 SinkTraceCtx(mLogCtx) << "Replayed " << replayResult.replayedEntities << " results.\n"
236 << (replayResult.replayedAll ? "Replayed all available results.\n" : "") 236 << (replayResult.replayedAll ? "Replayed all available results.\n" : "")
237 << "Incremental query took: " << Log::TraceTime(time.elapsed()); 237 << "Incremental query took: " << Log::TraceTime(time.elapsed());
238 return {entityStore.maxRevision(), replayResult.replayedEntities, replayResult.replayedAll, preparedQuery.getState()}; 238 return {entityStore.maxRevision(), replayResult.replayedEntities, false, preparedQuery.getState()};
239} 239}
240 240
241template <class DomainType> 241template <class DomainType>
@@ -264,7 +264,7 @@ ReplayResult QueryWorker<DomainType>::executeInitialQuery(
264 return DataStoreQuery{modifiedQuery, ApplicationDomain::getTypeName<DomainType>(), entityStore}; 264 return DataStoreQuery{modifiedQuery, ApplicationDomain::getTypeName<DomainType>(), entityStore};
265 } 265 }
266 }(); 266 }();
267 auto resultSet = preparedQuery.execute();; 267 auto resultSet = preparedQuery.execute();
268 268
269 SinkTraceCtx(mLogCtx) << "Filtered set retrieved." << Log::TraceTime(time.elapsed()); 269 SinkTraceCtx(mLogCtx) << "Filtered set retrieved." << Log::TraceTime(time.elapsed());
270 auto replayResult = resultSet.replaySet(0, batchsize, [this, query, &resultProvider](const ResultSet::Result &result) { 270 auto replayResult = resultSet.replaySet(0, batchsize, [this, query, &resultProvider](const ResultSet::Result &result) {
diff --git a/common/queryrunner.h b/common/queryrunner.h
index 5308eac..11a302f 100644
--- a/common/queryrunner.h
+++ b/common/queryrunner.h
@@ -32,8 +32,6 @@
32class QueryRunnerBase : public QObject 32class QueryRunnerBase : public QObject
33{ 33{
34 Q_OBJECT 34 Q_OBJECT
35protected:
36 SINK_DEBUG_AREA("queryrunner")
37public: 35public:
38 typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation; 36 typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation;
39 37
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index ad8cae9..808d892 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -477,7 +477,7 @@ void ResourceAccess::connected()
477 477
478 { 478 {
479 flatbuffers::FlatBufferBuilder fbb; 479 flatbuffers::FlatBufferBuilder fbb;
480 auto name = fbb.CreateString(QString("PID: %1 ResourceAccess: %2").arg(QCoreApplication::applicationPid()).arg(reinterpret_cast<qlonglong>(this)).toLatin1()); 480 auto name = fbb.CreateString(QString("PID: %1 ResourceAccess: %2").arg(QCoreApplication::applicationPid()).arg(reinterpret_cast<qlonglong>(this)).toLatin1().toStdString());
481 auto command = Sink::Commands::CreateHandshake(fbb, name); 481 auto command = Sink::Commands::CreateHandshake(fbb, name);
482 Sink::Commands::FinishHandshakeBuffer(fbb, command); 482 Sink::Commands::FinishHandshakeBuffer(fbb, command);
483 Commands::write(d->socket.data(), ++d->messageId, Commands::HandshakeCommand, fbb); 483 Commands::write(d->socket.data(), ++d->messageId, Commands::HandshakeCommand, fbb);
@@ -547,6 +547,8 @@ static Sink::Notification getNotification(const Sink::Commands::Notification *bu
547 } 547 }
548 n.type = buffer->type(); 548 n.type = buffer->type();
549 n.code = buffer->code(); 549 n.code = buffer->code();
550 n.progress = buffer->progress();
551 n.total = buffer->total();
550 n.entities = BufferUtils::fromVector(*buffer->entities()); 552 n.entities = BufferUtils::fromVector(*buffer->entities());
551 return n; 553 return n;
552} 554}
diff --git a/common/resourceaccess.h b/common/resourceaccess.h
index c32566b..b6a0b34 100644
--- a/common/resourceaccess.h
+++ b/common/resourceaccess.h
@@ -101,7 +101,6 @@ protected:
101class SINK_EXPORT ResourceAccess : public ResourceAccessInterface 101class SINK_EXPORT ResourceAccess : public ResourceAccessInterface
102{ 102{
103 Q_OBJECT 103 Q_OBJECT
104 SINK_DEBUG_AREA("communication")
105public: 104public:
106 typedef QSharedPointer<ResourceAccess> Ptr; 105 typedef QSharedPointer<ResourceAccess> Ptr;
107 106
@@ -158,7 +157,6 @@ private:
158 */ 157 */
159class SINK_EXPORT ResourceAccessFactory 158class SINK_EXPORT ResourceAccessFactory
160{ 159{
161 SINK_DEBUG_AREA("ResourceAccessFactory")
162public: 160public:
163 static ResourceAccessFactory &instance(); 161 static ResourceAccessFactory &instance();
164 Sink::ResourceAccess::Ptr getAccess(const QByteArray &instanceIdentifier, const QByteArray resourceType); 162 Sink::ResourceAccess::Ptr getAccess(const QByteArray &instanceIdentifier, const QByteArray resourceType);
diff --git a/common/resourcecontrol.cpp b/common/resourcecontrol.cpp
index 70a3f7d..b6a4c0b 100644
--- a/common/resourcecontrol.cpp
+++ b/common/resourcecontrol.cpp
@@ -30,8 +30,6 @@
30#include "log.h" 30#include "log.h"
31#include "notifier.h" 31#include "notifier.h"
32 32
33SINK_DEBUG_AREA("resourcecontrol")
34
35namespace Sink { 33namespace Sink {
36 34
37KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier) 35KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier)
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index dee0711..dab6aed 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -21,16 +21,12 @@
21#include "resourceconfig.h" 21#include "resourceconfig.h"
22#include "query.h" 22#include "query.h"
23#include "definitions.h" 23#include "definitions.h"
24#include "storage.h"
25#include "store.h" 24#include "store.h"
26#include "resourceaccess.h" 25#include "resourceaccess.h"
27#include "resource.h" 26#include "resource.h"
28#include <QDir>
29 27
30using namespace Sink; 28using namespace Sink;
31 29
32SINK_DEBUG_AREA("ResourceFacade")
33
34template<typename DomainType> 30template<typename DomainType>
35ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier; 31ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier;
36 32
@@ -100,17 +96,24 @@ template<typename DomainType>
100LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, const QByteArray &typeName, ConfigNotifier &configNotifier, const Sink::Log::Context &ctx) 96LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, const QByteArray &typeName, ConfigNotifier &configNotifier, const Sink::Log::Context &ctx)
101 : mResultProvider(new ResultProvider<typename DomainType::Ptr>), mConfigStore(identifier, typeName), mGuard(new QObject), mLogCtx(ctx.subContext("config")) 97 : mResultProvider(new ResultProvider<typename DomainType::Ptr>), mConfigStore(identifier, typeName), mGuard(new QObject), mLogCtx(ctx.subContext("config"))
102{ 98{
99
100 auto matchesTypeAndIds = [query, this] (const QByteArray &type, const QByteArray &id) {
101 if (query.hasFilter(ApplicationDomain::SinkResource::ResourceType::name) && query.getFilter(ApplicationDomain::SinkResource::ResourceType::name).value.toByteArray() != type) {
102 SinkTraceCtx(mLogCtx) << "Skipping due to type.";
103 return false;
104 }
105 if (!query.ids().isEmpty() && !query.ids().contains(id)) {
106 return false;
107 }
108 return true;
109 };
110
103 QObject *guard = new QObject; 111 QObject *guard = new QObject;
104 mResultProvider->setFetcher([this, query, guard, &configNotifier](const QSharedPointer<DomainType> &) { 112 mResultProvider->setFetcher([this, query, guard, &configNotifier, matchesTypeAndIds](const QSharedPointer<DomainType> &) {
105 const auto entries = mConfigStore.getEntries(); 113 const auto entries = mConfigStore.getEntries();
106 for (const auto &res : entries.keys()) { 114 for (const auto &res : entries.keys()) {
107 const auto type = entries.value(res); 115 const auto type = entries.value(res);
108 116 if (!matchesTypeAndIds(type, res)){
109 if (query.hasFilter(ApplicationDomain::SinkResource::ResourceType::name) && query.getFilter(ApplicationDomain::SinkResource::ResourceType::name).value.toByteArray() != type) {
110 SinkTraceCtx(mLogCtx) << "Skipping due to type.";
111 continue;
112 }
113 if (!query.ids().isEmpty() && !query.ids().contains(res)) {
114 continue; 117 continue;
115 } 118 }
116 auto entity = readFromConfig<DomainType>(mConfigStore, res, type, query.requestedProperties); 119 auto entity = readFromConfig<DomainType>(mConfigStore, res, type, query.requestedProperties);
@@ -128,8 +131,14 @@ LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query,
128 }); 131 });
129 if (query.liveQuery()) { 132 if (query.liveQuery()) {
130 { 133 {
131 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { 134 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this, query, matchesTypeAndIds](const ApplicationDomain::ApplicationDomainType::Ptr &entry, const QByteArray &type) {
132 auto entity = entry.staticCast<DomainType>(); 135 auto entity = entry.staticCast<DomainType>();
136 if (!matchesTypeAndIds(type, entity->identifier())){
137 return;
138 }
139 if (!matchesFilter(query.getBaseFilters(), *entity)){
140 return;
141 }
133 SinkTraceCtx(mLogCtx) << "A new resource has been added: " << entity->identifier(); 142 SinkTraceCtx(mLogCtx) << "A new resource has been added: " << entity->identifier();
134 updateStatus(*entity); 143 updateStatus(*entity);
135 mResultProvider->add(entity); 144 mResultProvider->add(entity);
@@ -137,8 +146,14 @@ LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query,
137 Q_ASSERT(ret); 146 Q_ASSERT(ret);
138 } 147 }
139 { 148 {
140 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { 149 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this, query, matchesTypeAndIds](const ApplicationDomain::ApplicationDomainType::Ptr &entry, const QByteArray &type) {
141 auto entity = entry.staticCast<DomainType>(); 150 auto entity = entry.staticCast<DomainType>();
151 if (!matchesTypeAndIds(type, entity->identifier())){
152 return;
153 }
154 if (!matchesFilter(query.getBaseFilters(), *entity)){
155 return;
156 }
142 updateStatus(*entity); 157 updateStatus(*entity);
143 mResultProvider->modify(entity); 158 mResultProvider->modify(entity);
144 }); 159 });
@@ -222,7 +237,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domai
222 } 237 }
223 configStore.modify(identifier, configurationValues); 238 configStore.modify(identifier, configurationValues);
224 } 239 }
225 sConfigNotifier.add(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{})); 240 sConfigNotifier.add(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{}), type);
226 }); 241 });
227} 242}
228 243
@@ -251,7 +266,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domai
251 } 266 }
252 267
253 const auto type = configStore.getEntries().value(identifier); 268 const auto type = configStore.getEntries().value(identifier);
254 sConfigNotifier.modify(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{})); 269 sConfigNotifier.modify(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{}), type);
255 }); 270 });
256} 271}
257 272
@@ -281,7 +296,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domai
281 SinkTrace() << "Removing: " << identifier; 296 SinkTrace() << "Removing: " << identifier;
282 auto configStore = ConfigStore(configStoreIdentifier, typeName); 297 auto configStore = ConfigStore(configStoreIdentifier, typeName);
283 configStore.remove(identifier); 298 configStore.remove(identifier);
284 sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject)); 299 sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject), typeName);
285 }); 300 });
286} 301}
287 302
@@ -373,10 +388,10 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain
373 if (states.contains(ApplicationDomain::BusyStatus)) { 388 if (states.contains(ApplicationDomain::BusyStatus)) {
374 return ApplicationDomain::BusyStatus; 389 return ApplicationDomain::BusyStatus;
375 } 390 }
376 if (states.contains(ApplicationDomain::ConnectedStatus)) { 391 if (states.contains(ApplicationDomain::OfflineStatus)) {
377 return ApplicationDomain::ConnectedStatus; 392 return ApplicationDomain::OfflineStatus;
378 } 393 }
379 return ApplicationDomain::OfflineStatus; 394 return ApplicationDomain::ConnectedStatus;
380 }(); 395 }();
381 account.setStatusStatus(status); 396 account.setStatusStatus(status);
382 }); 397 });
diff --git a/common/resourcefacade.h b/common/resourcefacade.h
index 1cc075c..76fadce 100644
--- a/common/resourcefacade.h
+++ b/common/resourcefacade.h
@@ -36,24 +36,24 @@ class ConfigNotifier : public QObject
36{ 36{
37 Q_OBJECT 37 Q_OBJECT
38public: 38public:
39 void add(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) 39 void add(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type)
40 { 40 {
41 emit added(account); 41 emit added(account, type);
42 } 42 }
43 43
44 void remove(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) 44 void remove(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type)
45 { 45 {
46 emit removed(account); 46 emit removed(account, type);
47 } 47 }
48 48
49 void modify(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) 49 void modify(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type)
50 { 50 {
51 emit modified(account); 51 emit modified(account, type);
52 } 52 }
53signals: 53signals:
54 void added(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); 54 void added(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type);
55 void removed(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); 55 void removed(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type);
56 void modified(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); 56 void modified(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type);
57}; 57};
58 58
59template <typename DomainType> 59template <typename DomainType>
diff --git a/common/specialpurposepreprocessor.cpp b/common/specialpurposepreprocessor.cpp
index 25a6d1a..54f7f46 100644
--- a/common/specialpurposepreprocessor.cpp
+++ b/common/specialpurposepreprocessor.cpp
@@ -5,8 +5,6 @@
5 5
6using namespace Sink; 6using namespace Sink;
7 7
8SINK_DEBUG_AREA("SpecialPurposeProcessor")
9
10static QHash<QByteArray, QString> specialPurposeFolders() 8static QHash<QByteArray, QString> specialPurposeFolders()
11{ 9{
12 QHash<QByteArray, QString> hash; 10 QHash<QByteArray, QString> hash;
diff --git a/common/storage.h b/common/storage.h
index 71e9401..8c129df 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -25,10 +25,19 @@
25#include <string> 25#include <string>
26#include <functional> 26#include <functional>
27#include <QString> 27#include <QString>
28#include <QMap>
28 29
29namespace Sink { 30namespace Sink {
30namespace Storage { 31namespace Storage {
31 32
33struct SINK_EXPORT DbLayout {
34 typedef QMap<QByteArray, int> Databases;
35 DbLayout();
36 DbLayout(const QByteArray &, const Databases &);
37 QByteArray name;
38 Databases tables;
39};
40
32class SINK_EXPORT DataStore 41class SINK_EXPORT DataStore
33{ 42{
34public: 43public:
@@ -132,7 +141,6 @@ public:
132 void abort(); 141 void abort();
133 142
134 QList<QByteArray> getDatabaseNames() const; 143 QList<QByteArray> getDatabaseNames() const;
135 bool validateNamedDatabases();
136 144
137 NamedDatabase openDatabase(const QByteArray &name = {"default"}, 145 NamedDatabase openDatabase(const QByteArray &name = {"default"},
138 const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const; 146 const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const;
@@ -152,6 +160,7 @@ public:
152 }; 160 };
153 161
154 DataStore(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly); 162 DataStore(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly);
163 DataStore(const QString &storageRoot, const DbLayout &layout, AccessMode mode = ReadOnly);
155 ~DataStore(); 164 ~DataStore();
156 165
157 Transaction createTransaction(AccessMode mode = ReadWrite, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); 166 Transaction createTransaction(AccessMode mode = ReadWrite, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
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;
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index 81a38c7..8603787 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -24,8 +24,6 @@
24#include "log.h" 24#include "log.h"
25#include <QUuid> 25#include <QUuid>
26 26
27SINK_DEBUG_AREA("storage")
28
29QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error) 27QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error)
30{ 28{
31 dbg << error.message << "Code: " << error.code << "Db: " << error.store; 29 dbg << error.message << "Code: " << error.code << "Db: " << error.store;
@@ -38,6 +36,18 @@ namespace Storage {
38static const char *s_internalPrefix = "__internal"; 36static const char *s_internalPrefix = "__internal";
39static const int s_internalPrefixSize = strlen(s_internalPrefix); 37static const int s_internalPrefixSize = strlen(s_internalPrefix);
40 38
39DbLayout::DbLayout()
40{
41
42}
43
44DbLayout::DbLayout(const QByteArray &n, const Databases &t)
45 : name(n),
46 tables(t)
47{
48
49}
50
41void errorHandler(const DataStore::Error &error) 51void errorHandler(const DataStore::Error &error)
42{ 52{
43 if (error.code == DataStore::TransactionError) { 53 if (error.code == DataStore::TransactionError) {
@@ -113,7 +123,7 @@ QByteArray DataStore::getUidFromRevision(const DataStore::Transaction &transacti
113 transaction.openDatabase("revisions") 123 transaction.openDatabase("revisions")
114 .scan(QByteArray::number(revision), 124 .scan(QByteArray::number(revision),
115 [&](const QByteArray &, const QByteArray &value) -> bool { 125 [&](const QByteArray &, const QByteArray &value) -> bool {
116 uid = value; 126 uid = QByteArray{value.constData(), value.size()};
117 return false; 127 return false;
118 }, 128 },
119 [revision](const Error &error) { SinkWarning() << "Couldn't find uid for revision: " << revision << error.message; }); 129 [revision](const Error &error) { SinkWarning() << "Couldn't find uid for revision: " << revision << error.message; });
@@ -126,7 +136,7 @@ QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transact
126 transaction.openDatabase("revisionType") 136 transaction.openDatabase("revisionType")
127 .scan(QByteArray::number(revision), 137 .scan(QByteArray::number(revision),
128 [&](const QByteArray &, const QByteArray &value) -> bool { 138 [&](const QByteArray &, const QByteArray &value) -> bool {
129 type = value; 139 type = QByteArray{value.constData(), value.size()};
130 return false; 140 return false;
131 }, 141 },
132 [revision](const Error &error) { SinkWarning() << "Couldn't find type for revision " << revision; }); 142 [revision](const Error &error) { SinkWarning() << "Couldn't find type for revision " << revision; });
@@ -207,6 +217,11 @@ QByteArray DataStore::generateUid()
207 217
208DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type) 218DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type)
209{ 219{
220 if (type.isEmpty()) {
221 SinkError() << "Tried to open main database for empty type.";
222 Q_ASSERT(false);
223 return {};
224 }
210 return t.openDatabase(type + ".main"); 225 return t.openDatabase(type + ".main");
211} 226}
212 227
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 08eea37..f7999d1 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -35,9 +35,6 @@
35#include <lmdb.h> 35#include <lmdb.h>
36#include "log.h" 36#include "log.h"
37 37
38SINK_DEBUG_AREA("storage")
39// SINK_DEBUG_COMPONENT(d->storageRoot.toLatin1() + '/' + d->name.toLatin1())
40
41namespace Sink { 38namespace Sink {
42namespace Storage { 39namespace Storage {
43 40
@@ -169,6 +166,27 @@ public:
169 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) { 166 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) {
170 //Create the db if it is not existing already 167 //Create the db if it is not existing already
171 if (rc == MDB_NOTFOUND && !readOnly) { 168 if (rc == MDB_NOTFOUND && !readOnly) {
169 //Sanity check db name
170 {
171 auto parts = db.split('.');
172 for (const auto &p : parts) {
173 auto containsSpecialCharacter = [] (const QByteArray &p) {
174 for (int i = 0; i < p.size(); i++) {
175 const auto c = p.at(i);
176 //Between 0 and z in the ascii table. Essentially ensures that the name is printable and doesn't contain special chars
177 if (c < 0x30 || c > 0x7A) {
178 return true;
179 }
180 }
181 return false;
182 };
183 if (p.isEmpty() || containsSpecialCharacter(p)) {
184 SinkError() << "Tried to create a db with an invalid name. Hex:" << db.toHex() << " ASCII:" << db;
185 Q_ASSERT(false);
186 throw std::runtime_error("Fatal error while creating db.");
187 }
188 }
189 }
172 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) { 190 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) {
173 SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc)); 191 SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc));
174 Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc))); 192 Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc)));
@@ -542,6 +560,7 @@ DataStore::Transaction::Transaction(Transaction &&other) : d(nullptr)
542DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other) 560DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other)
543{ 561{
544 if (&other != this) { 562 if (&other != this) {
563 abort();
545 delete d; 564 delete d;
546 d = other.d; 565 d = other.d;
547 other.d = nullptr; 566 other.d = nullptr;
@@ -639,11 +658,6 @@ static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray
639 return !openedTheWrongDatabase; 658 return !openedTheWrongDatabase;
640} 659}
641 660
642bool DataStore::Transaction::validateNamedDatabases()
643{
644 return true;
645}
646
647DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const 661DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const
648{ 662{
649 if (!d) { 663 if (!d) {
@@ -694,7 +708,7 @@ QList<QByteArray> DataStore::Transaction::getDatabaseNames() const
694class DataStore::Private 708class DataStore::Private
695{ 709{
696public: 710public:
697 Private(const QString &s, const QString &n, AccessMode m); 711 Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout = {});
698 ~Private(); 712 ~Private();
699 713
700 QString storageRoot; 714 QString storageRoot;
@@ -702,68 +716,85 @@ public:
702 716
703 MDB_env *env; 717 MDB_env *env;
704 AccessMode mode; 718 AccessMode mode;
719 Sink::Log::Context logCtx;
720
721 void initEnvironment(const QString &fullPath, const DbLayout &layout)
722 {
723 // Ensure the environment is only created once, and that we only have one environment per process
724 if (!(env = sEnvironments.value(fullPath))) {
725 QMutexLocker locker(&sMutex);
726 if (!(env = sEnvironments.value(fullPath))) {
727 int rc = 0;
728 if ((rc = mdb_env_create(&env))) {
729 SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
730 qCritical() << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
731 } else {
732 //Limit large enough to accomodate all our named dbs. This only starts to matter if the number gets large, otherwise it's just a bunch of extra entries in the main table.
733 mdb_env_set_maxdbs(env, 50);
734 const bool readOnly = (mode == ReadOnly);
735 unsigned int flags = MDB_NOTLS;
736 if (readOnly) {
737 flags |= MDB_RDONLY;
738 }
739 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
740 SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc);
741 mdb_env_close(env);
742 env = 0;
743 } else {
744 if (RUNNING_ON_VALGRIND) {
745 // In order to run valgrind this size must be smaller than half your available RAM
746 // https://github.com/BVLC/caffe/issues/2404
747 mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)1000); // 1MB * 1000
748 } else {
749 //This is the maximum size of the db (but will not be used directly), so we make it large enough that we hopefully never run into the limit.
750 mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)100000); // 1MB * 1000
751 }
752 Q_ASSERT(env);
753 sEnvironments.insert(fullPath, env);
754 //Open all available dbi's
755 bool noLock = true;
756 auto t = Transaction(new Transaction::Private(readOnly, nullptr, name, env, noLock));
757 if (!layout.tables.isEmpty()) {
758
759 //TODO upgrade db if the layout has changed:
760 //* read existing layout
761 //* if layout is not the same create new layout
762 //If the db is read only, abort if the db is not yet existing.
763 //If the db is not read-only but is not existing, ensure we have a layout and create all tables.
764
765 for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) {
766 bool allowDuplicates = it.value();
767 t.openDatabase(it.key(), {}, allowDuplicates);
768 }
769 } else {
770 for (const auto &db : t.getDatabaseNames()) {
771 //Get dbi to store for future use.
772 t.openDatabase(db);
773 }
774 }
775 //To persist the dbis (this is also necessary for read-only transactions)
776 t.commit();
777 }
778 }
779 }
780 }
781 }
782
705}; 783};
706 784
707DataStore::Private::Private(const QString &s, const QString &n, AccessMode m) : storageRoot(s), name(n), env(0), mode(m) 785DataStore::Private::Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout) : storageRoot(s), name(n), env(0), mode(m), logCtx(n.toLatin1())
708{ 786{
787
709 const QString fullPath(storageRoot + '/' + name); 788 const QString fullPath(storageRoot + '/' + name);
710 QFileInfo dirInfo(fullPath); 789 QFileInfo dirInfo(fullPath);
711 if (!dirInfo.exists() && mode == ReadWrite) { 790 if (!dirInfo.exists() && mode == ReadWrite) {
712 QDir().mkpath(fullPath); 791 QDir().mkpath(fullPath);
713 dirInfo.refresh(); 792 dirInfo.refresh();
714 } 793 }
715 Sink::Log::Context logCtx{n.toLatin1()};
716 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { 794 if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) {
717 qCritical() << fullPath << "does not have write permissions. Aborting"; 795 qCritical() << fullPath << "does not have write permissions. Aborting";
718 } else if (dirInfo.exists()) { 796 } else if (dirInfo.exists()) {
719 // Ensure the environment is only created once 797 initEnvironment(fullPath, layout);
720 QMutexLocker locker(&sMutex);
721
722 /*
723 * It seems we can only ever have one environment open in the process.
724 * Otherwise multi-threading breaks.
725 */
726 env = sEnvironments.value(fullPath);
727 if (!env) {
728 int rc = 0;
729 if ((rc = mdb_env_create(&env))) {
730 // TODO: handle error
731 SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc);
732 } else {
733 mdb_env_set_maxdbs(env, 50);
734 unsigned int flags = MDB_NOTLS;
735 if (mode == ReadOnly) {
736 flags |= MDB_RDONLY;
737 }
738 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
739 SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc);
740 mdb_env_close(env);
741 env = 0;
742 } else {
743 if (RUNNING_ON_VALGRIND) {
744 // In order to run valgrind this size must be smaller than half your available RAM
745 // https://github.com/BVLC/caffe/issues/2404
746 const size_t dbSize = (size_t)10485760 * (size_t)1000; // 1MB * 1000
747 mdb_env_set_mapsize(env, dbSize);
748 } else {
749 // FIXME: dynamic resize
750 const size_t dbSize = (size_t)10485760 * (size_t)8000; // 1MB * 8000
751 mdb_env_set_mapsize(env, dbSize);
752 }
753 sEnvironments.insert(fullPath, env);
754 //Open all available dbi's
755 bool noLock = true;
756 bool requestedRead = m == ReadOnly;
757 auto t = Transaction(new Transaction::Private(requestedRead, nullptr, name, env, noLock));
758 for (const auto &db : t.getDatabaseNames()) {
759 //Get dbi to store for future use.
760 t.openDatabase(db);
761 }
762 //To persist the dbis (this is also necessary for read-only transactions)
763 t.commit();
764 }
765 }
766 }
767 } 798 }
768} 799}
769 800
@@ -777,6 +808,10 @@ DataStore::DataStore(const QString &storageRoot, const QString &name, AccessMode
777{ 808{
778} 809}
779 810
811DataStore::DataStore(const QString &storageRoot, const DbLayout &dbLayout, AccessMode mode) : d(new Private(storageRoot, dbLayout.name, mode, dbLayout))
812{
813}
814
780DataStore::~DataStore() 815DataStore::~DataStore()
781{ 816{
782 delete d; 817 delete d;
diff --git a/common/store.cpp b/common/store.cpp
index d266098..4735113 100644
--- a/common/store.cpp
+++ b/common/store.cpp
@@ -36,8 +36,6 @@
36#include "storage.h" 36#include "storage.h"
37#include "log.h" 37#include "log.h"
38 38
39SINK_DEBUG_AREA("store")
40
41Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) 39Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>)
42Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); 40Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>);
43Q_DECLARE_METATYPE(std::shared_ptr<void>); 41Q_DECLARE_METATYPE(std::shared_ptr<void>);
@@ -277,6 +275,19 @@ KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier)
277 }); 275 });
278} 276}
279 277
278KAsync::Job<void> Store::upgrade()
279{
280 SinkLog() << "Upgrading...";
281 return fetchAll<ApplicationDomain::SinkResource>({})
282 .template each([](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> {
283 SinkLog() << "Removing caches for " << resource->identifier();
284 return removeDataFromDisk(resource->identifier());
285 })
286 .then([] {
287 SinkLog() << "Upgrade complete.";
288 });
289}
290
280static KAsync::Job<void> synchronize(const QByteArray &resource, const Sink::SyncScope &scope) 291static KAsync::Job<void> synchronize(const QByteArray &resource, const Sink::SyncScope &scope)
281{ 292{
282 SinkLog() << "Synchronizing " << resource << scope; 293 SinkLog() << "Synchronizing " << resource << scope;
diff --git a/common/store.h b/common/store.h
index fae76e5..34e14df 100644
--- a/common/store.h
+++ b/common/store.h
@@ -122,6 +122,18 @@ KAsync::Job<void> SINK_EXPORT synchronize(const Sink::SyncScope &query);
122 */ 122 */
123KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier); 123KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier);
124 124
125/**
126 * Run upgrade jobs.
127 *
128 * Run this to upgrade your local database to a new version.
129 * Note that this may:
130 * * take a while
131 * * remove some/all of your local caches
132 *
133 * Note: The initial implementation simply calls removeDataFromDisk for all resources.
134 */
135KAsync::Job<void> SINK_EXPORT upgrade();
136
125template <class DomainType> 137template <class DomainType>
126KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query); 138KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query);
127 139
diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp
index 3e7bd30..3b32e68 100644
--- a/common/synchronizer.cpp
+++ b/common/synchronizer.cpp
@@ -304,9 +304,29 @@ void Synchronizer::emitNotification(Notification::NoticationType type, int code,
304 emit notify(n); 304 emit notify(n);
305} 305}
306 306
307void Synchronizer::reportProgress(int progress, int total) 307void Synchronizer::emitProgressNotification(Notification::NoticationType type, int progress, int total, const QByteArray &id, const QByteArrayList &entities)
308{ 308{
309 SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; 309 Sink::Notification n;
310 n.id = id;
311 n.type = type;
312 n.progress = progress;
313 n.total = total;
314 n.entities = entities;
315 emit notify(n);
316}
317
318void Synchronizer::reportProgress(int progress, int total, const QByteArrayList &entities)
319{
320 if (progress > 0 && total > 0) {
321 SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total << mCurrentRequest.requestId << mCurrentRequest.applicableEntities;
322 const auto applicableEntities = [&] {
323 if (entities.isEmpty()) {
324 return mCurrentRequest.applicableEntities;
325 }
326 return entities;
327 }();
328 emitProgressNotification(Notification::Progress, progress, total, mCurrentRequest.requestId, applicableEntities);
329 }
310} 330}
311 331
312void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString &s, const QByteArray &requestId) 332void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString &s, const QByteArray &requestId)
@@ -315,6 +335,9 @@ void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString
315 if (error.errorCode == ApplicationDomain::ConnectionError) { 335 if (error.errorCode == ApplicationDomain::ConnectionError) {
316 //Couldn't connect, so we assume we don't have a network connection. 336 //Couldn't connect, so we assume we don't have a network connection.
317 setStatus(ApplicationDomain::OfflineStatus, s, requestId); 337 setStatus(ApplicationDomain::OfflineStatus, s, requestId);
338 } else if (error.errorCode == ApplicationDomain::NoServerError) {
339 //Failed to contact the server.
340 setStatus(ApplicationDomain::OfflineStatus, s, requestId);
318 } else if (error.errorCode == ApplicationDomain::ConfigurationError) { 341 } else if (error.errorCode == ApplicationDomain::ConfigurationError) {
319 //There is an error with the configuration. 342 //There is an error with the configuration.
320 setStatus(ApplicationDomain::ErrorStatus, s, requestId); 343 setStatus(ApplicationDomain::ErrorStatus, s, requestId);
@@ -354,6 +377,7 @@ KAsync::Job<void> Synchronizer::processRequest(const SyncRequest &request)
354 } else if (request.requestType == Synchronizer::SyncRequest::Synchronization) { 377 } else if (request.requestType == Synchronizer::SyncRequest::Synchronization) {
355 return KAsync::start([this, request] { 378 return KAsync::start([this, request] {
356 SinkLogCtx(mLogCtx) << "Synchronizing: " << request.query; 379 SinkLogCtx(mLogCtx) << "Synchronizing: " << request.query;
380 setBusy(true, "Synchronization has started.", request.requestId);
357 emitNotification(Notification::Info, ApplicationDomain::SyncInProgress, {}, {}, request.applicableEntities); 381 emitNotification(Notification::Info, ApplicationDomain::SyncInProgress, {}, {}, request.applicableEntities);
358 }).then(synchronizeWithSource(request.query)).then([this] { 382 }).then(synchronizeWithSource(request.query)).then([this] {
359 //Commit after every request, so implementations only have to commit more if they add a lot of data. 383 //Commit after every request, so implementations only have to commit more if they add a lot of data.
@@ -391,11 +415,12 @@ KAsync::Job<void> Synchronizer::processRequest(const SyncRequest &request)
391 return KAsync::null(); 415 return KAsync::null();
392 } else { 416 } else {
393 return KAsync::start([this, request] { 417 return KAsync::start([this, request] {
418 setBusy(true, "ChangeReplay has started.", request.requestId);
394 SinkLogCtx(mLogCtx) << "Replaying changes."; 419 SinkLogCtx(mLogCtx) << "Replaying changes.";
395 }) 420 })
396 .then(replayNextRevision()) 421 .then(replayNextRevision())
397 .then<void>([this, request](const KAsync::Error &error) { 422 .then<void>([this, request](const KAsync::Error &error) {
398 setStatusFromResult(error, "Changereplay has ended.", "changereplay"); 423 setStatusFromResult(error, "Changereplay has ended.", request.requestId);
399 if (error) { 424 if (error) {
400 SinkWarningCtx(mLogCtx) << "Changereplay failed: " << error.errorMessage; 425 SinkWarningCtx(mLogCtx) << "Changereplay failed: " << error.errorMessage;
401 return KAsync::error(error); 426 return KAsync::error(error);
@@ -462,16 +487,13 @@ KAsync::Job<void> Synchronizer::processSyncQueue()
462 mMessageQueue->startTransaction(); 487 mMessageQueue->startTransaction();
463 mEntityStore->startTransaction(Sink::Storage::DataStore::ReadOnly); 488 mEntityStore->startTransaction(Sink::Storage::DataStore::ReadOnly);
464 mSyncInProgress = true; 489 mSyncInProgress = true;
465 if (request.requestType == Synchronizer::SyncRequest::Synchronization) { 490 mCurrentRequest = request;
466 setBusy(true, "Synchronization has started.", request.requestId);
467 } else if (request.requestType == Synchronizer::SyncRequest::ChangeReplay) {
468 setBusy(true, "ChangeReplay has started.", "changereplay");
469 }
470 }) 491 })
471 .then(processRequest(request)) 492 .then(processRequest(request))
472 .then<void>([this, request](const KAsync::Error &error) { 493 .then<void>([this, request](const KAsync::Error &error) {
473 SinkTraceCtx(mLogCtx) << "Sync request processed"; 494 SinkTraceCtx(mLogCtx) << "Sync request processed";
474 setBusy(false, {}, request.requestId); 495 setBusy(false, {}, request.requestId);
496 mCurrentRequest = {};
475 mEntityStore->abortTransaction(); 497 mEntityStore->abortTransaction();
476 mSyncTransaction.abort(); 498 mSyncTransaction.abort();
477 mMessageQueue->commit(); 499 mMessageQueue->commit();
@@ -516,7 +538,7 @@ void Synchronizer::revisionChanged()
516 return; 538 return;
517 } 539 }
518 } 540 }
519 mSyncRequestQueue << Synchronizer::SyncRequest{Synchronizer::SyncRequest::ChangeReplay}; 541 mSyncRequestQueue << Synchronizer::SyncRequest{Synchronizer::SyncRequest::ChangeReplay, "changereplay"};
520 processSyncQueue().exec(); 542 processSyncQueue().exec();
521} 543}
522 544
@@ -607,11 +629,14 @@ KAsync::Job<void> Synchronizer::replay(const QByteArray &type, const QByteArray
607 } 629 }
608 }) 630 })
609 .then([this](const KAsync::Error &error) { 631 .then([this](const KAsync::Error &error) {
632 //We need to commit here otherwise the next change-replay step will abort the transaction
633 mSyncStore.clear();
634 mSyncTransaction.commit();
610 if (error) { 635 if (error) {
611 SinkWarningCtx(mLogCtx) << "Failed to replay change: " << error.errorMessage; 636 SinkWarningCtx(mLogCtx) << "Failed to replay change: " << error.errorMessage;
637 return KAsync::error(error);
612 } 638 }
613 mSyncStore.clear(); 639 return KAsync::null();
614 mSyncTransaction.commit();
615 }); 640 });
616} 641}
617 642
diff --git a/common/synchronizer.h b/common/synchronizer.h
index b1ee122..cc082be 100644
--- a/common/synchronizer.h
+++ b/common/synchronizer.h
@@ -131,6 +131,8 @@ protected:
131 RequestFlush 131 RequestFlush
132 }; 132 };
133 133
134 SyncRequest() = default;
135
134 SyncRequest(const Sink::QueryBase &q, const QByteArray &requestId_ = QByteArray(), RequestOptions o = NoOptions) 136 SyncRequest(const Sink::QueryBase &q, const QByteArray &requestId_ = QByteArray(), RequestOptions o = NoOptions)
135 : requestId(requestId_), 137 : requestId(requestId_),
136 requestType(Synchronization), 138 requestType(Synchronization),
@@ -145,6 +147,12 @@ protected:
145 { 147 {
146 } 148 }
147 149
150 SyncRequest(RequestType type, const QByteArray &requestId_)
151 : requestId(requestId_),
152 requestType(type)
153 {
154 }
155
148 SyncRequest(RequestType type, int flushType_, const QByteArray &requestId_) 156 SyncRequest(RequestType type, int flushType_, const QByteArray &requestId_)
149 : flushType(flushType_), 157 : flushType(flushType_),
150 requestId(requestId_), 158 requestId(requestId_),
@@ -184,11 +192,12 @@ protected:
184 virtual void mergeIntoQueue(const Synchronizer::SyncRequest &request, QList<Synchronizer::SyncRequest> &queue); 192 virtual void mergeIntoQueue(const Synchronizer::SyncRequest &request, QList<Synchronizer::SyncRequest> &queue);
185 193
186 void emitNotification(Notification::NoticationType type, int code, const QString &message, const QByteArray &id = QByteArray{}, const QByteArrayList &entiteis = QByteArrayList{}); 194 void emitNotification(Notification::NoticationType type, int code, const QString &message, const QByteArray &id = QByteArray{}, const QByteArrayList &entiteis = QByteArrayList{});
195 void emitProgressNotification(Notification::NoticationType type, int progress, int total, const QByteArray &id, const QByteArrayList &entities);
187 196
188 /** 197 /**
189 * Report progress for current task 198 * Report progress for current task
190 */ 199 */
191 void reportProgress(int progress, int total); 200 virtual void reportProgress(int progress, int total, const QByteArrayList &entities = {}) Q_DECL_OVERRIDE;
192 201
193protected: 202protected:
194 Sink::Log::Context mLogCtx; 203 Sink::Log::Context mLogCtx;
@@ -211,6 +220,7 @@ private:
211 Sink::Storage::DataStore::Transaction mSyncTransaction; 220 Sink::Storage::DataStore::Transaction mSyncTransaction;
212 std::function<void(int commandId, const QByteArray &data)> mEnqueue; 221 std::function<void(int commandId, const QByteArray &data)> mEnqueue;
213 QList<SyncRequest> mSyncRequestQueue; 222 QList<SyncRequest> mSyncRequestQueue;
223 SyncRequest mCurrentRequest;
214 MessageQueue *mMessageQueue; 224 MessageQueue *mMessageQueue;
215 bool mSyncInProgress; 225 bool mSyncInProgress;
216 QMultiHash<QByteArray, SyncRequest> mPendingSyncRequests; 226 QMultiHash<QByteArray, SyncRequest> mPendingSyncRequests;
diff --git a/common/synchronizerstore.cpp b/common/synchronizerstore.cpp
index 5364094..79cd920 100644
--- a/common/synchronizerstore.cpp
+++ b/common/synchronizerstore.cpp
@@ -25,8 +25,6 @@
25 25
26using namespace Sink; 26using namespace Sink;
27 27
28SINK_DEBUG_AREA("synchronizerstore")
29
30SynchronizerStore::SynchronizerStore(Sink::Storage::DataStore::Transaction &transaction) 28SynchronizerStore::SynchronizerStore(Sink::Storage::DataStore::Transaction &transaction)
31 : mTransaction(transaction) 29 : mTransaction(transaction)
32{ 30{
diff --git a/common/test.cpp b/common/test.cpp
index 90586ba..237d3bb 100644
--- a/common/test.cpp
+++ b/common/test.cpp
@@ -29,8 +29,6 @@
29#include "resourceconfig.h" 29#include "resourceconfig.h"
30#include "definitions.h" 30#include "definitions.h"
31 31
32SINK_DEBUG_AREA("test")
33
34using namespace Sink; 32using namespace Sink;
35 33
36void Sink::Test::initTest() 34void Sink::Test::initTest()
@@ -83,6 +81,7 @@ void Sink::Test::initTest()
83void Sink::Test::setTestModeEnabled(bool enabled) 81void Sink::Test::setTestModeEnabled(bool enabled)
84{ 82{
85 QStandardPaths::setTestModeEnabled(enabled); 83 QStandardPaths::setTestModeEnabled(enabled);
84 Sink::clearLocationCache();
86 if (enabled) { 85 if (enabled) {
87 qputenv("SINK_TESTMODE", "TRUE"); 86 qputenv("SINK_TESTMODE", "TRUE");
88 } else { 87 } else {
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 153aa43..5a19839 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -21,18 +21,20 @@
21#include "log.h" 21#include "log.h"
22#include "index.h" 22#include "index.h"
23#include <QDateTime> 23#include <QDateTime>
24 24#include <QDataStream>
25SINK_DEBUG_AREA("typeindex")
26 25
27using namespace Sink; 26using namespace Sink;
28 27
29static QByteArray getByteArray(const QVariant &value) 28static QByteArray getByteArray(const QVariant &value)
30{ 29{
31 if (value.type() == QVariant::DateTime) { 30 if (value.type() == QVariant::DateTime) {
32 const auto result = value.toDateTime().toString().toLatin1(); 31 QByteArray result;
33 if (result.isEmpty()) { 32 QDataStream ds(&result, QIODevice::WriteOnly);
34 return "nodate"; 33 ds << value.toDateTime();
35 } 34 return result;
35 }
36 if (value.type() == QVariant::Bool) {
37 return value.toBool() ? "t" : "f";
36 } 38 }
37 if (value.canConvert<Sink::ApplicationDomain::Reference>()) { 39 if (value.canConvert<Sink::ApplicationDomain::Reference>()) {
38 const auto ba = value.value<Sink::ApplicationDomain::Reference>().value; 40 const auto ba = value.value<Sink::ApplicationDomain::Reference>().value;
@@ -85,6 +87,20 @@ void TypeIndex::addProperty<QByteArray>(const QByteArray &property)
85} 87}
86 88
87template <> 89template <>
90void TypeIndex::addProperty<bool>(const QByteArray &property)
91{
92 auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) {
93 if (add) {
94 Index(indexName(property), transaction).add(getByteArray(value), identifier);
95 } else {
96 Index(indexName(property), transaction).remove(getByteArray(value), identifier);
97 }
98 };
99 mIndexer.insert(property, indexer);
100 mProperties << property;
101}
102
103template <>
88void TypeIndex::addProperty<QString>(const QByteArray &property) 104void TypeIndex::addProperty<QString>(const QByteArray &property)
89{ 105{
90 auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) { 106 auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) {
@@ -266,6 +282,18 @@ void TypeIndex::index<QString, QByteArray>(const QByteArray &leftName, const QBy
266} 282}
267 283
268template <> 284template <>
285void TypeIndex::unindex<QByteArray, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction)
286{
287 Index(indexName(leftName + rightName), transaction).remove(getByteArray(leftValue), getByteArray(rightValue));
288}
289
290template <>
291void TypeIndex::unindex<QString, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction)
292{
293 Index(indexName(leftName + rightName), transaction).remove(getByteArray(leftValue), getByteArray(rightValue));
294}
295
296template <>
269QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value) 297QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value)
270{ 298{
271 QVector<QByteArray> keys; 299 QVector<QByteArray> keys;
diff --git a/common/typeindex.h b/common/typeindex.h
index 1f216a7..890c3db 100644
--- a/common/typeindex.h
+++ b/common/typeindex.h
@@ -95,6 +95,15 @@ public:
95 template <typename LeftType, typename RightType> 95 template <typename LeftType, typename RightType>
96 void index(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction); 96 void index(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction);
97 97
98 template <typename Left, typename Right>
99 void unindex(const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction)
100 {
101 index<typename Left::Type, typename Right::Type>(Left::name, Right::name, leftValue, rightValue, transaction);
102 }
103
104 template <typename LeftType, typename RightType>
105 void unindex(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction);
106
98 107
99private: 108private:
100 friend class Sink::Storage::EntityStore; 109 friend class Sink::Storage::EntityStore;