summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/storage.h11
-rw-r--r--common/storage_common.cpp11
-rw-r--r--common/storage_lmdb.cpp47
-rw-r--r--tests/storagetest.cpp51
4 files changed, 109 insertions, 11 deletions
diff --git a/common/storage.h b/common/storage.h
index d186b2e..98b12ed 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -76,9 +76,13 @@ public:
76 * 76 *
77 * @return The number of values retrieved. 77 * @return The number of values retrieved.
78 */ 78 */
79 int scan(const QByteArray &k, 79 int scan(const QByteArray &key,
80 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, 80 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
81 const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()) const; 81 const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>(), bool findSubstringKeys = false) const;
82
83 void findLatest(const QByteArray &uid,
84 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
85 const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()) const;
82 86
83 NamedDatabase(NamedDatabase&& other) : d(other.d) 87 NamedDatabase(NamedDatabase&& other) : d(other.d)
84 { 88 {
@@ -170,6 +174,9 @@ public:
170 static bool isInternalKey(void *key, int keySize); 174 static bool isInternalKey(void *key, int keySize);
171 static bool isInternalKey(const QByteArray &key); 175 static bool isInternalKey(const QByteArray &key);
172 176
177 static QByteArray assembleKey(const QByteArray &key, qint64 revision);
178 static QByteArray uidFromKey(const QByteArray &key);
179
173private: 180private:
174 std::function<void(const Storage::Error &error)> mErrorHandler; 181 std::function<void(const Storage::Error &error)> mErrorHandler;
175 182
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index f22150a..28fb4c2 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -93,4 +93,15 @@ bool Storage::isInternalKey(const QByteArray &key)
93 return key.startsWith(s_internalPrefix); 93 return key.startsWith(s_internalPrefix);
94} 94}
95 95
96QByteArray Storage::assembleKey(const QByteArray &key, qint64 revision)
97{
98 Q_ASSERT(key.size() == 38);
99 return key + QByteArray::number(revision);
100}
101
102QByteArray Storage::uidFromKey(const QByteArray &key)
103{
104 return key.mid(0, 38);
105}
106
96} // namespace Akonadi2 107} // namespace Akonadi2
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index a048a71..3073d37 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -169,7 +169,8 @@ void Storage::NamedDatabase::remove(const QByteArray &k,
169 169
170int Storage::NamedDatabase::scan(const QByteArray &k, 170int Storage::NamedDatabase::scan(const QByteArray &k,
171 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, 171 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
172 const std::function<void(const Storage::Error &error)> &errorHandler) const 172 const std::function<void(const Storage::Error &error)> &errorHandler,
173 bool findSubstringKeys) const
173{ 174{
174 if (!d || !d->transaction) { 175 if (!d || !d->transaction) {
175 //Not an error. We rely on this to read nothing from non-existing databases. 176 //Not an error. We rely on this to read nothing from non-existing databases.
@@ -193,14 +194,25 @@ int Storage::NamedDatabase::scan(const QByteArray &k,
193 194
194 int numberOfRetrievedValues = 0; 195 int numberOfRetrievedValues = 0;
195 196
196 if (k.isEmpty() || d->allowDuplicates) { 197 if (k.isEmpty() || d->allowDuplicates || findSubstringKeys) {
197 if ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_SET : MDB_FIRST)) == 0) { 198 MDB_cursor_op op = d->allowDuplicates ? MDB_SET : MDB_FIRST;
198 numberOfRetrievedValues++; 199 if (findSubstringKeys) {
199 if (resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) { 200 op = MDB_SET_RANGE;
200 while ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_NEXT_DUP : MDB_NEXT)) == 0) { 201 }
201 numberOfRetrievedValues++; 202 if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) {
202 if (!resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) { 203 //The first lookup will find a key that is equal or greather than our key
203 break; 204 if (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) {
205 numberOfRetrievedValues++;
206 if (resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) {
207 MDB_cursor_op nextOp = d->allowDuplicates ? MDB_NEXT_DUP : MDB_NEXT;
208 while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) {
209 //Every consequent lookup simply iterates through the list
210 if (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) {
211 numberOfRetrievedValues++;
212 if (!resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) {
213 break;
214 }
215 }
204 } 216 }
205 } 217 }
206 } 218 }
@@ -226,6 +238,23 @@ int Storage::NamedDatabase::scan(const QByteArray &k,
226 238
227 return numberOfRetrievedValues; 239 return numberOfRetrievedValues;
228} 240}
241void Storage::NamedDatabase::findLatest(const QByteArray &uid,
242 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
243 const std::function<void(const Storage::Error &error)> &errorHandler) const
244{
245 QByteArray latestKey;
246 scan(uid, [&](const QByteArray &key, const QByteArray &value) -> bool {
247 latestKey = key;
248 return true;
249 },
250 errorHandler, true);
251
252 scan(latestKey, [=](const QByteArray &key, const QByteArray &value) -> bool {
253 resultHandler(key, value);
254 return false;
255 },
256 errorHandler);
257}
229 258
230 259
231 260
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index 7060ef2..8d5ee00 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -317,6 +317,41 @@ private Q_SLOTS:
317 QCOMPARE(numValues, 1); 317 QCOMPARE(numValues, 1);
318 } 318 }
319 319
320 void testFindSubstringKeys()
321 {
322 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
323 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
324 auto db = transaction.openDatabase("test", nullptr, false);
325 db.write("sub","value1");
326 db.write("subsub","value2");
327 db.write("wubsub","value3");
328 int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool {
329 return true;
330 }, nullptr, true);
331
332 QCOMPARE(numValues, 2);
333 }
334
335 void testKeySorting()
336 {
337 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
338 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
339 auto db = transaction.openDatabase("test", nullptr, false);
340 db.write("sub_2","value2");
341 db.write("sub_1","value1");
342 db.write("sub_3","value3");
343 QList<QByteArray> results;
344 int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool {
345 results << value;
346 return true;
347 }, nullptr, true);
348
349 QCOMPARE(numValues, 3);
350 QCOMPARE(results.at(0), QByteArray("value1"));
351 QCOMPARE(results.at(1), QByteArray("value2"));
352 QCOMPARE(results.at(2), QByteArray("value3"));
353 }
354
320 //Ensure we don't retrieve a key that is greater than the current key. We only want equal keys. 355 //Ensure we don't retrieve a key that is greater than the current key. We only want equal keys.
321 void testKeyRange() 356 void testKeyRange()
322 { 357 {
@@ -330,6 +365,22 @@ private Q_SLOTS:
330 365
331 QCOMPARE(numValues, 0); 366 QCOMPARE(numValues, 0);
332 } 367 }
368
369 void testFindLatest()
370 {
371 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
372 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
373 auto db = transaction.openDatabase("test", nullptr, false);
374 db.write("sub1","value1");
375 db.write("sub2","value2");
376 db.write("wub3","value3");
377 QByteArray result;
378 db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) {
379 result = value;
380 });
381
382 QCOMPARE(result, QByteArray("value2"));
383 }
333}; 384};
334 385
335QTEST_MAIN(StorageTest) 386QTEST_MAIN(StorageTest)