diff options
Diffstat (limited to 'common')
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; | |||
30 | using namespace Sink::Storage; | 30 | using namespace Sink::Storage; |
31 | 31 | ||
32 | ChangeReplay::ChangeReplay(const ResourceContext &resourceContext, const Sink::Log::Context &ctx) | 32 | ChangeReplay::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 | ||
187 | void ChangeReplay::revisionChanged() | 190 | void 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: | |||
54 | protected: | 54 | protected: |
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 | ||
69 | class NullChangeReplay : public ChangeReplay | 70 | class 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 | ||
26 | namespace Sink { | 27 | namespace 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 | ||
76 | void write(QIODevice *device, int messageId, int commandId) | 77 | void 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 | ||
81 | static void write(QIODevice *device, const char *buffer, uint size) | 82 | static 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 | ||
88 | void write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size) | 89 | void 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 | ||
102 | void write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb) | 108 | void 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 | ||
27 | class QIODevice; | 27 | class QLocalSocket; |
28 | 28 | ||
29 | namespace Sink { | 29 | namespace Sink { |
30 | 30 | ||
@@ -55,9 +55,9 @@ enum CommandIds | |||
55 | QByteArray name(int commandId); | 55 | QByteArray name(int commandId); |
56 | 56 | ||
57 | int SINK_EXPORT headerSize(); | 57 | int SINK_EXPORT headerSize(); |
58 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId); | 58 | void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId); |
59 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size); | 59 | void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId, const char *buffer, uint size); |
60 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb); | 60 | void 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 | ||
27 | SINK_DEBUG_AREA("configstore") | ||
28 | |||
29 | static QSharedPointer<QSettings> getConfig(const QByteArray &identifier) | 27 | static 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 | ||
40 | ContactPropertyExtractor::~ContactPropertyExtractor() | 42 | ContactPropertyExtractor::~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 | ||
619 | void DataStoreQuery::updateComplete() | ||
620 | { | ||
621 | mSource->mIncrementalIds.clear(); | ||
622 | } | ||
602 | 623 | ||
603 | ResultSet DataStoreQuery::execute() | 624 | ResultSet 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 | ||
26 | static bool rereadDataLocation = true; | ||
27 | static bool rereadConfigLocation = true; | ||
28 | static bool rereadTemporaryFileLocation = true; | ||
29 | |||
30 | void Sink::clearLocationCache() | ||
31 | { | ||
32 | rereadDataLocation = true; | ||
33 | rereadConfigLocation = true; | ||
34 | rereadTemporaryFileLocation = true; | ||
35 | } | ||
36 | |||
26 | QString Sink::storageLocation() | 37 | QString Sink::storageLocation() |
27 | { | 38 | { |
28 | return dataLocation() + "/storage"; | 39 | return dataLocation() + "/storage"; |
@@ -30,21 +41,37 @@ QString Sink::storageLocation() | |||
30 | 41 | ||
31 | QString Sink::dataLocation() | 42 | QString 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 | ||
36 | QString Sink::configLocation() | 52 | QString 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 | ||
41 | QString Sink::temporaryFileLocation() | 62 | QString 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 | ||
50 | QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifier) | 77 | QString 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 | ||
27 | namespace Sink { | 27 | namespace Sink { |
28 | void SINK_EXPORT clearLocationCache(); | ||
28 | QString SINK_EXPORT storageLocation(); | 29 | QString SINK_EXPORT storageLocation(); |
29 | QString SINK_EXPORT dataLocation(); | 30 | QString SINK_EXPORT dataLocation(); |
30 | QString SINK_EXPORT configLocation(); | 31 | QString 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 | ||
28 | SINK_DEBUG_AREA("applicationdomaintype"); | ||
29 | |||
30 | QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::Mail::Contact &c) | 28 | QDebug 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); | |||
114 | SINK_REGISTER_PROPERTY(Contact, Emails); | 112 | SINK_REGISTER_PROPERTY(Contact, Emails); |
115 | SINK_REGISTER_PROPERTY(Contact, Vcard); | 113 | SINK_REGISTER_PROPERTY(Contact, Vcard); |
116 | SINK_REGISTER_PROPERTY(Contact, Addressbook); | 114 | SINK_REGISTER_PROPERTY(Contact, Addressbook); |
115 | SINK_REGISTER_PROPERTY(Contact, Photo); | ||
117 | 116 | ||
118 | SINK_REGISTER_ENTITY(Addressbook); | 117 | SINK_REGISTER_ENTITY(Addressbook); |
119 | SINK_REGISTER_PROPERTY(Addressbook, Name); | 118 | SINK_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 | ||
18 | root_type Contact; | 19 | root_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 | ||
32 | using namespace Sink; | 33 | using namespace Sink; |
33 | using namespace Sink::ApplicationDomain; | 34 | using 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 | |||
39 | typedef 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 | |||
51 | typedef IndexConfig<Folder, | ||
52 | ValueIndex<Folder::Name>, | ||
53 | ValueIndex<Folder::Parent> | ||
54 | > FolderIndexConfig; | ||
55 | |||
56 | typedef IndexConfig<Contact, | ||
57 | ValueIndex<Contact::Uid> | ||
58 | > ContactIndexConfig; | ||
59 | |||
60 | typedef IndexConfig<Addressbook, | ||
61 | ValueIndex<Addressbook::Parent> | ||
62 | > AddressbookIndexConfig; | ||
63 | |||
64 | typedef IndexConfig<Event, | ||
65 | ValueIndex<Event::Uid> | ||
66 | > EventIndexConfig; | ||
67 | |||
68 | |||
35 | void TypeImplementation<Mail>::configure(TypeIndex &index) | 69 | void 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>(); | 74 | QMap<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 | ||
52 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) | 79 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) |
@@ -61,69 +88,44 @@ void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMappe | |||
61 | }); | 88 | }); |
62 | } | 89 | } |
63 | 90 | ||
64 | void TypeImplementation<Mail>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 91 | void 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 | |||
84 | void 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 | ||
105 | void TypeImplementation<Folder>::configure(TypeIndex &index) | 112 | void 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 | ||
111 | void TypeImplementation<Folder>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 117 | QMap<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 | ||
120 | void TypeImplementation<Folder>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 122 | void 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 | ||
129 | void TypeImplementation<Folder>::configure(IndexPropertyMapper &) | 131 | void TypeImplementation<Folder>::configure(IndexPropertyMapper &) |
@@ -134,29 +136,24 @@ void TypeImplementation<Folder>::configure(IndexPropertyMapper &) | |||
134 | 136 | ||
135 | void TypeImplementation<Contact>::configure(TypeIndex &index) | 137 | void TypeImplementation<Contact>::configure(TypeIndex &index) |
136 | { | 138 | { |
137 | index.addProperty<QByteArray>(Contact::Uid::name); | 139 | ContactIndexConfig::configure(index); |
138 | } | 140 | } |
139 | 141 | ||
140 | void TypeImplementation<Contact>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 142 | QMap<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 | ||
151 | void TypeImplementation<Contact>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 147 | void 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 | ||
162 | void TypeImplementation<Contact>::configure(IndexPropertyMapper &) | 159 | void TypeImplementation<Contact>::configure(IndexPropertyMapper &) |
@@ -167,20 +164,18 @@ void TypeImplementation<Contact>::configure(IndexPropertyMapper &) | |||
167 | 164 | ||
168 | void TypeImplementation<Addressbook>::configure(TypeIndex &index) | 165 | void 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 | ||
174 | void TypeImplementation<Addressbook>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 170 | QMap<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 | ||
180 | void TypeImplementation<Addressbook>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 175 | void 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 | ||
186 | void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) | 181 | void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) |
@@ -191,26 +186,24 @@ void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) | |||
191 | 186 | ||
192 | void TypeImplementation<Event>::configure(TypeIndex &index) | 187 | void TypeImplementation<Event>::configure(TypeIndex &index) |
193 | { | 188 | { |
194 | index.addProperty<QByteArray>(Event::Uid::name); | 189 | EventIndexConfig::configure(index); |
195 | } | 190 | } |
196 | 191 | ||
197 | void TypeImplementation<Event>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 192 | QMap<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 | ||
205 | void TypeImplementation<Event>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 197 | void 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 | ||
213 | void TypeImplementation<Event>::configure(IndexPropertyMapper &) | 205 | void 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 | ||
29 | template<typename T> | 29 | class PropertyMapper; |
30 | class ReadPropertyMapper; | ||
31 | template<typename T> | ||
32 | class WritePropertyMapper; | ||
33 | class IndexPropertyMapper; | 30 | class IndexPropertyMapper; |
34 | 31 | ||
35 | class TypeIndex; | 32 | class 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 | ||
56 | template<> | 53 | template<> |
@@ -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 | ||
67 | template<> | 64 | template<> |
@@ -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 | ||
78 | template<> | 75 | template<> |
@@ -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 | ||
89 | template<> | 86 | template<> |
@@ -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 | |||
23 | template <typename T, typename First> | ||
24 | void 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 | |||
31 | template <typename T, typename First, typename ... Tail> | ||
32 | void 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 | |||
40 | template <typename First, typename ... Tail> | ||
41 | First merge(First f, Tail ...maps) | ||
42 | { | ||
43 | First map; | ||
44 | mergeImpl(map, f, maps...); | ||
45 | return map; | ||
46 | } | ||
47 | |||
48 | template <typename Property> | ||
49 | class ValueIndex | ||
50 | { | ||
51 | public: | ||
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 | |||
65 | template <typename Property, typename SortProperty> | ||
66 | class SortedIndex | ||
67 | { | ||
68 | public: | ||
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 | |||
81 | template <typename Property, typename SecondaryProperty> | ||
82 | class SecondaryIndex | ||
83 | { | ||
84 | public: | ||
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 | |||
97 | template <typename Property, typename SecondaryProperty, typename Indexer> | ||
98 | class CustomSecondaryIndex | ||
99 | { | ||
100 | public: | ||
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 | |||
113 | template <typename EntityType, typename ... Indexes> | ||
114 | class 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 | |||
142 | public: | ||
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 | */ |
41 | template <class Builder, class Buffer> | 39 | template <class Builder, class Buffer> |
42 | flatbuffers::Offset<Buffer> | 40 | flatbuffers::Offset<Buffer> |
43 | createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const WritePropertyMapper<Builder> &mapper) | 41 | createBufferPart(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 | */ |
70 | template <typename Buffer, typename BufferBuilder> | 68 | template <typename Buffer, typename BufferBuilder> |
71 | static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, WritePropertyMapper<BufferBuilder> &mapper) | 69 | static 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 | */ |
123 | template <class LocalBuffer, class ResourceBuffer> | ||
124 | class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor | 121 | class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor |
125 | { | 122 | { |
126 | SINK_DEBUG_AREA("bufferadaptor") | ||
127 | public: | 123 | public: |
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 | */ |
171 | template <typename DomainType, typename ResourceBuffer = Sink::ApplicationDomain::Buffer::Dummy, typename ResourceBuilder = Sink::ApplicationDomain::Buffer::DummyBuilder> | 163 | template <typename DomainType> |
172 | class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface | 164 | class 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 | ||
177 | public: | 169 | public: |
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 | ||
238 | protected: | 214 | protected: |
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 | |||
104 | template <class DomainType> | 104 | template <class DomainType> |
105 | QPair<KAsync::Job<void>, typename ResultEmitter<typename DomainType::Ptr>::Ptr> GenericFacade<DomainType>::load(const Sink::Query &query, const Log::Context &ctx) | 105 | QPair<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> | |||
48 | class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType> | 48 | class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType> |
49 | { | 49 | { |
50 | protected: | 50 | protected: |
51 | SINK_DEBUG_AREA("facade") | ||
52 | SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier) | 51 | SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier) |
53 | public: | 52 | public: |
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 | */ |
36 | class SINK_EXPORT GenericResource : public Resource | 36 | class SINK_EXPORT GenericResource : public Resource |
37 | { | 37 | { |
38 | protected: | ||
39 | SINK_DEBUG_AREA("resource") | ||
40 | public: | 38 | public: |
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 ¬ification) | |||
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: | |||
55 | class SINK_EXPORT Listener : public QObject | 55 | class SINK_EXPORT Listener : public QObject |
56 | { | 56 | { |
57 | Q_OBJECT | 57 | Q_OBJECT |
58 | SINK_DEBUG_AREA("communication") | ||
59 | |||
60 | public: | 58 | public: |
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 | ||
16 | using namespace Sink::Log; | 19 | using namespace Sink::Log; |
17 | 20 | ||
18 | static QSharedPointer<QSettings> config() | 21 | static QThreadStorage<QSharedPointer<QSettings>> sSettings; |
22 | static 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 | ||
23 | static QByteArray sPrimaryComponent; | 30 | static QByteArray sPrimaryComponent; |
@@ -173,22 +180,22 @@ DebugLevel Sink::Log::debugLevelFromName(const QByteArray &name) | |||
173 | 180 | ||
174 | void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel) | 181 | void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel) |
175 | { | 182 | { |
176 | config()->setValue("level", debugLevel); | 183 | config().setValue("level", debugLevel); |
177 | } | 184 | } |
178 | 185 | ||
179 | Sink::Log::DebugLevel Sink::Log::debugOutputLevel() | 186 | Sink::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 | ||
184 | void Sink::Log::setDebugOutputFilter(FilterType type, const QByteArrayList &filter) | 191 | void 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 | ||
208 | void Sink::Log::setDebugOutputFields(const QByteArrayList &output) | 215 | void 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 | ||
213 | QByteArrayList Sink::Log::debugOutputFields() | 220 | QByteArrayList Sink::Log::debugOutputFields() |
214 | { | 221 | { |
215 | return config()->value("outputfields").value<QByteArrayList>(); | 222 | return config().value("outputfields").value<QByteArrayList>(); |
216 | } | 223 | } |
217 | 224 | ||
218 | static QByteArray getProgramName() | 225 | static 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 | ||
303 | QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) | 312 | static 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 | ||
319 | static 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 | |||
330 | static 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 | ||
344 | bool 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 | |||
349 | QDebug 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 | |||
89 | SINK_EXPORT bool isFiltered(DebugLevel debugLevel, const char *debugArea, const char *debugComponent, const char *file); | ||
90 | |||
88 | } | 91 | } |
89 | } | 92 | } |
90 | 93 | ||
91 | static const char *getComponentName() { return nullptr; } | 94 | static 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 | ||
24 | SINK_DEBUG_AREA("threadindex") | ||
25 | |||
26 | using namespace Sink; | 24 | using namespace Sink; |
27 | using namespace Sink::ApplicationDomain; | 25 | using namespace Sink::ApplicationDomain; |
28 | 26 | ||
29 | static 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 | |||
83 | void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) | 27 | void 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 | ||
143 | void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) | 72 | void 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 | ||
80 | QMap<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(); | ||
32 | private: | 33 | private: |
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 | ||
30 | using namespace Sink; | 30 | using namespace Sink; |
31 | 31 | ||
32 | SINK_DEBUG_AREA("mailpreprocessor") | ||
33 | |||
34 | QString MailPropertyExtractor::getFilePathFromMimeMessagePath(const QString &s) const | 32 | QString 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 | ||
6 | SINK_DEBUG_AREA("messagequeue") | ||
7 | |||
8 | MessageQueue::MessageQueue(const QString &storageRoot, const QString &name) : mStorage(storageRoot, name, Sink::Storage::DataStore::ReadWrite) | 6 | MessageQueue::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 | ||
49 | QDebug operator<<(QDebug dbg, const Sink::Notification &n) | 49 | QDebug 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 <> | |||
57 | flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) | 58 | flatbuffers::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 <> | |||
66 | flatbuffers::uoffset_t variantToProperty<QDateTime>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) | 69 | flatbuffers::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 <> | |||
256 | QVariant propertyToVariant<QDateTime>(const flatbuffers::String *property) | 262 | QVariant 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 | */ |
68 | template <typename BufferType> | 68 | class PropertyMapper |
69 | class ReadPropertyMapper | ||
70 | { | 69 | { |
71 | public: | 70 | public: |
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 | |||
143 | private: | ||
144 | QHash<QByteArray, std::function<QVariant(BufferType const *)>> mReadAccessors; | ||
145 | }; | ||
146 | |||
147 | template <typename BufferBuilder> | ||
148 | class WritePropertyMapper | ||
149 | { | ||
150 | public: | ||
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> | 107 | private: |
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 | ||
239 | private: | 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 | ||
241 | template <class DomainType> | 241 | template <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 @@ | |||
32 | class QueryRunnerBase : public QObject | 32 | class QueryRunnerBase : public QObject |
33 | { | 33 | { |
34 | Q_OBJECT | 34 | Q_OBJECT |
35 | protected: | ||
36 | SINK_DEBUG_AREA("queryrunner") | ||
37 | public: | 35 | public: |
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: | |||
101 | class SINK_EXPORT ResourceAccess : public ResourceAccessInterface | 101 | class SINK_EXPORT ResourceAccess : public ResourceAccessInterface |
102 | { | 102 | { |
103 | Q_OBJECT | 103 | Q_OBJECT |
104 | SINK_DEBUG_AREA("communication") | ||
105 | public: | 104 | public: |
106 | typedef QSharedPointer<ResourceAccess> Ptr; | 105 | typedef QSharedPointer<ResourceAccess> Ptr; |
107 | 106 | ||
@@ -158,7 +157,6 @@ private: | |||
158 | */ | 157 | */ |
159 | class SINK_EXPORT ResourceAccessFactory | 158 | class SINK_EXPORT ResourceAccessFactory |
160 | { | 159 | { |
161 | SINK_DEBUG_AREA("ResourceAccessFactory") | ||
162 | public: | 160 | public: |
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 | ||
33 | SINK_DEBUG_AREA("resourcecontrol") | ||
34 | |||
35 | namespace Sink { | 33 | namespace Sink { |
36 | 34 | ||
37 | KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier) | 35 | KAsync::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 | ||
30 | using namespace Sink; | 28 | using namespace Sink; |
31 | 29 | ||
32 | SINK_DEBUG_AREA("ResourceFacade") | ||
33 | |||
34 | template<typename DomainType> | 30 | template<typename DomainType> |
35 | ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier; | 31 | ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier; |
36 | 32 | ||
@@ -100,17 +96,24 @@ template<typename DomainType> | |||
100 | LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, const QByteArray &typeName, ConfigNotifier &configNotifier, const Sink::Log::Context &ctx) | 96 | LocalStorageQueryRunner<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 |
38 | public: | 38 | public: |
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 | } |
53 | signals: | 53 | signals: |
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 | ||
59 | template <typename DomainType> | 59 | template <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 | ||
6 | using namespace Sink; | 6 | using namespace Sink; |
7 | 7 | ||
8 | SINK_DEBUG_AREA("SpecialPurposeProcessor") | ||
9 | |||
10 | static QHash<QByteArray, QString> specialPurposeFolders() | 8 | static 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 | ||
29 | namespace Sink { | 30 | namespace Sink { |
30 | namespace Storage { | 31 | namespace Storage { |
31 | 32 | ||
33 | struct 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 | |||
32 | class SINK_EXPORT DataStore | 41 | class SINK_EXPORT DataStore |
33 | { | 42 | { |
34 | public: | 43 | public: |
@@ -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 @@ | |||
36 | using namespace Sink; | 36 | using namespace Sink; |
37 | using namespace Sink::Storage; | 37 | using namespace Sink::Storage; |
38 | 38 | ||
39 | static QMap<QByteArray, int> baseDbs() | ||
40 | { | ||
41 | return {{"revisionType", 0}, | ||
42 | {"revisions", 0}, | ||
43 | {"uids", 0}, | ||
44 | {"default", 0}, | ||
45 | {"__flagtable", 0}}; | ||
46 | } | ||
47 | |||
48 | template <typename T, typename First> | ||
49 | void 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 | |||
56 | template <typename T, typename First, typename ... Tail> | ||
57 | void 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 | |||
65 | template <typename First, typename ... Tail> | ||
66 | First merge(First f, Tail ...maps) | ||
67 | { | ||
68 | First map; | ||
69 | mergeImpl(map, f, maps...); | ||
70 | return map; | ||
71 | } | ||
72 | |||
73 | template <class T> | ||
74 | struct DbLayoutHelper { | ||
75 | void operator()(QMap<QByteArray, int> map) const { | ||
76 | mergeImpl(map, ApplicationDomain::TypeImplementation<T>::typeDatabases()); | ||
77 | } | ||
78 | }; | ||
79 | |||
80 | static 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 | |||
39 | class EntityStore::Private { | 95 | class EntityStore::Private { |
40 | public: | 96 | public: |
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 | ||
118 | void EntityStore::commitTransaction() | 188 | void EntityStore::commitTransaction() |
@@ -138,9 +208,6 @@ bool EntityStore::hasTransaction() const | |||
138 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) | 208 | void 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 | |||
640 | Sink::Log::Context EntityStore::logContext() const | 689 | Sink::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 | ||
27 | SINK_DEBUG_AREA("storage") | ||
28 | |||
29 | QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error) | 27 | QDebug& 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 { | |||
38 | static const char *s_internalPrefix = "__internal"; | 36 | static const char *s_internalPrefix = "__internal"; |
39 | static const int s_internalPrefixSize = strlen(s_internalPrefix); | 37 | static const int s_internalPrefixSize = strlen(s_internalPrefix); |
40 | 38 | ||
39 | DbLayout::DbLayout() | ||
40 | { | ||
41 | |||
42 | } | ||
43 | |||
44 | DbLayout::DbLayout(const QByteArray &n, const Databases &t) | ||
45 | : name(n), | ||
46 | tables(t) | ||
47 | { | ||
48 | |||
49 | } | ||
50 | |||
41 | void errorHandler(const DataStore::Error &error) | 51 | void 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 | ||
208 | DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type) | 218 | DataStore::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 | ||
38 | SINK_DEBUG_AREA("storage") | ||
39 | // SINK_DEBUG_COMPONENT(d->storageRoot.toLatin1() + '/' + d->name.toLatin1()) | ||
40 | |||
41 | namespace Sink { | 38 | namespace Sink { |
42 | namespace Storage { | 39 | namespace 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) | |||
542 | DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other) | 560 | DataStore::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 | ||
642 | bool DataStore::Transaction::validateNamedDatabases() | ||
643 | { | ||
644 | return true; | ||
645 | } | ||
646 | |||
647 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const | 661 | DataStore::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 | |||
694 | class DataStore::Private | 708 | class DataStore::Private |
695 | { | 709 | { |
696 | public: | 710 | public: |
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 | ||
707 | DataStore::Private::Private(const QString &s, const QString &n, AccessMode m) : storageRoot(s), name(n), env(0), mode(m) | 785 | DataStore::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 | ||
811 | DataStore::DataStore(const QString &storageRoot, const DbLayout &dbLayout, AccessMode mode) : d(new Private(storageRoot, dbLayout.name, mode, dbLayout)) | ||
812 | { | ||
813 | } | ||
814 | |||
780 | DataStore::~DataStore() | 815 | DataStore::~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 | ||
39 | SINK_DEBUG_AREA("store") | ||
40 | |||
41 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) | 39 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) |
42 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); | 40 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); |
43 | Q_DECLARE_METATYPE(std::shared_ptr<void>); | 41 | Q_DECLARE_METATYPE(std::shared_ptr<void>); |
@@ -277,6 +275,19 @@ KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier) | |||
277 | }); | 275 | }); |
278 | } | 276 | } |
279 | 277 | ||
278 | KAsync::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 | |||
280 | static KAsync::Job<void> synchronize(const QByteArray &resource, const Sink::SyncScope &scope) | 291 | static 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 | */ |
123 | KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier); | 123 | KAsync::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 | */ | ||
135 | KAsync::Job<void> SINK_EXPORT upgrade(); | ||
136 | |||
125 | template <class DomainType> | 137 | template <class DomainType> |
126 | KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query); | 138 | KAsync::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 | ||
307 | void Synchronizer::reportProgress(int progress, int total) | 307 | void 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 | |||
318 | void 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 | ||
312 | void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString &s, const QByteArray &requestId) | 332 | void 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 | ||
193 | protected: | 202 | protected: |
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 | ||
26 | using namespace Sink; | 26 | using namespace Sink; |
27 | 27 | ||
28 | SINK_DEBUG_AREA("synchronizerstore") | ||
29 | |||
30 | SynchronizerStore::SynchronizerStore(Sink::Storage::DataStore::Transaction &transaction) | 28 | SynchronizerStore::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 | ||
32 | SINK_DEBUG_AREA("test") | ||
33 | |||
34 | using namespace Sink; | 32 | using namespace Sink; |
35 | 33 | ||
36 | void Sink::Test::initTest() | 34 | void Sink::Test::initTest() |
@@ -83,6 +81,7 @@ void Sink::Test::initTest() | |||
83 | void Sink::Test::setTestModeEnabled(bool enabled) | 81 | void 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> | |
25 | SINK_DEBUG_AREA("typeindex") | ||
26 | 25 | ||
27 | using namespace Sink; | 26 | using namespace Sink; |
28 | 27 | ||
29 | static QByteArray getByteArray(const QVariant &value) | 28 | static 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 | ||
87 | template <> | 89 | template <> |
90 | void 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 | |||
103 | template <> | ||
88 | void TypeIndex::addProperty<QString>(const QByteArray &property) | 104 | void 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 | ||
268 | template <> | 284 | template <> |
285 | void 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 | |||
290 | template <> | ||
291 | void 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 | |||
296 | template <> | ||
269 | QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value) | 297 | QVector<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 | ||
99 | private: | 108 | private: |
100 | friend class Sink::Storage::EntityStore; | 109 | friend class Sink::Storage::EntityStore; |