diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-10-15 13:56:45 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-10-15 13:56:45 +0200 |
commit | 35b28af1f449edb1bac0b0bda606c3c06b2fe102 (patch) | |
tree | 6237f0421afc8dc7adeb37dc823f7b34c30846a1 /common/facade.h | |
parent | 2eb3810b66de3130f3e650627380c28a96acded7 (diff) | |
download | sink-35b28af1f449edb1bac0b0bda606c3c06b2fe102.tar.gz sink-35b28af1f449edb1bac0b0bda606c3c06b2fe102.zip |
Moved more entitystorage functionality back to facade.
To avoid unnecessary abstraction layers that don't solve a problem,
and to allow facades to customize how entities are loaded.
Diffstat (limited to 'common/facade.h')
-rw-r--r-- | common/facade.h | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/common/facade.h b/common/facade.h index 38388c7..8b37579 100644 --- a/common/facade.h +++ b/common/facade.h | |||
@@ -82,6 +82,27 @@ private: | |||
82 | qint64 mLatestRevision; | 82 | qint64 mLatestRevision; |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &bufferType) | ||
86 | { | ||
87 | //TODO use a result set with an iterator, to read values on demand | ||
88 | QVector<QByteArray> keys; | ||
89 | transaction.openDatabase(bufferType + ".main").scan(QByteArray(), [&](const QByteArray &key, const QByteArray &value) -> bool { | ||
90 | //Skip internals | ||
91 | if (Akonadi2::Storage::isInternalKey(key)) { | ||
92 | return true; | ||
93 | } | ||
94 | keys << Akonadi2::Storage::uidFromKey(key); | ||
95 | return true; | ||
96 | }, | ||
97 | [](const Akonadi2::Storage::Error &error) { | ||
98 | qWarning() << "Error during query: " << error.message; | ||
99 | }); | ||
100 | |||
101 | Trace() << "Full scan found " << keys.size() << " results"; | ||
102 | return ResultSet(keys); | ||
103 | } | ||
104 | |||
105 | |||
85 | namespace Akonadi2 { | 106 | namespace Akonadi2 { |
86 | /** | 107 | /** |
87 | * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class. | 108 | * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class. |
@@ -107,13 +128,75 @@ public: | |||
107 | GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>()) | 128 | GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>()) |
108 | : Akonadi2::StoreFacade<DomainType>(), | 129 | : Akonadi2::StoreFacade<DomainType>(), |
109 | mResourceAccess(resourceAccess), | 130 | mResourceAccess(resourceAccess), |
110 | mStorage(storage ? storage : QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier, adaptorFactory, bufferTypeForDomainType())), | 131 | mStorage(storage), |
111 | mDomainTypeAdaptorFactory(adaptorFactory), | 132 | mDomainTypeAdaptorFactory(adaptorFactory), |
112 | mResourceInstanceIdentifier(resourceIdentifier) | 133 | mResourceInstanceIdentifier(resourceIdentifier) |
113 | { | 134 | { |
114 | if (!mResourceAccess) { | 135 | if (!mResourceAccess) { |
115 | mResourceAccess = QSharedPointer<Akonadi2::ResourceAccess>::create(resourceIdentifier); | 136 | mResourceAccess = QSharedPointer<Akonadi2::ResourceAccess>::create(resourceIdentifier); |
116 | } | 137 | } |
138 | if (!mStorage) { | ||
139 | mStorage = QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier); | ||
140 | const auto bufferType = bufferTypeForDomainType(); | ||
141 | |||
142 | mStorage->readEntity = [bufferType, this] (const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback) | ||
143 | { | ||
144 | //This only works for a 1:1 mapping of resource to domain types. | ||
145 | //Not i.e. for tags that are stored as flags in each entity of an imap store. | ||
146 | //additional properties that don't have a 1:1 mapping (such as separately stored tags), | ||
147 | //could be added to the adaptor. | ||
148 | transaction.openDatabase(bufferType + ".main").findLatest(key, [=](const QByteArray &key, const QByteArray &value) -> bool { | ||
149 | Akonadi2::EntityBuffer buffer(value.data(), value.size()); | ||
150 | const Akonadi2::Entity &entity = buffer.entity(); | ||
151 | const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata()); | ||
152 | Q_ASSERT(metadataBuffer); | ||
153 | const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; | ||
154 | resultCallback(DomainType::Ptr::create(mResourceInstanceIdentifier, Akonadi2::Storage::uidFromKey(key), revision, mDomainTypeAdaptorFactory->createAdaptor(entity)), metadataBuffer->operation()); | ||
155 | return false; | ||
156 | }, | ||
157 | [](const Akonadi2::Storage::Error &error) { | ||
158 | qWarning() << "Error during query: " << error.message; | ||
159 | }); | ||
160 | }; | ||
161 | |||
162 | mStorage->loadInitialResultSet = [bufferType, this] (const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet | ||
163 | { | ||
164 | QSet<QByteArray> appliedFilters; | ||
165 | auto resultSet = Akonadi2::ApplicationDomain::TypeImplementation<DomainType>::queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction); | ||
166 | remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; | ||
167 | |||
168 | //We do a full scan if there were no indexes available to create the initial set. | ||
169 | if (appliedFilters.isEmpty()) { | ||
170 | //TODO this should be replaced by an index lookup as well | ||
171 | return fullScan(transaction, bufferType); | ||
172 | } | ||
173 | return resultSet; | ||
174 | }; | ||
175 | |||
176 | mStorage->loadIncrementalResultSet = [bufferType, this] (qint64 baseRevision, const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters) -> ResultSet | ||
177 | { | ||
178 | auto revisionCounter = QSharedPointer<qint64>::create(baseRevision); | ||
179 | return ResultSet([bufferType, revisionCounter, &transaction, this]() -> QByteArray { | ||
180 | const qint64 topRevision = Akonadi2::Storage::maxRevision(transaction); | ||
181 | //Spit out the revision keys one by one. | ||
182 | while (*revisionCounter <= topRevision) { | ||
183 | const auto uid = Akonadi2::Storage::getUidFromRevision(transaction, *revisionCounter); | ||
184 | const auto type = Akonadi2::Storage::getTypeFromRevision(transaction, *revisionCounter); | ||
185 | Trace() << "Revision" << *revisionCounter << type << uid; | ||
186 | if (type != bufferType) { | ||
187 | //Skip revision | ||
188 | *revisionCounter += 1; | ||
189 | continue; | ||
190 | } | ||
191 | const auto key = Akonadi2::Storage::assembleKey(uid, *revisionCounter); | ||
192 | *revisionCounter += 1; | ||
193 | return key; | ||
194 | } | ||
195 | //We're done | ||
196 | return QByteArray(); | ||
197 | }); | ||
198 | }; | ||
199 | } | ||
117 | } | 200 | } |
118 | 201 | ||
119 | ~GenericFacade() | 202 | ~GenericFacade() |