diff options
-rw-r--r-- | common/storage_lmdb.cpp | 71 | ||||
-rw-r--r-- | tests/storagetest.cpp | 30 |
2 files changed, 89 insertions, 12 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 1516e69..a247e38 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -253,22 +253,69 @@ int Storage::NamedDatabase::scan(const QByteArray &k, | |||
253 | 253 | ||
254 | return numberOfRetrievedValues; | 254 | return numberOfRetrievedValues; |
255 | } | 255 | } |
256 | void Storage::NamedDatabase::findLatest(const QByteArray &uid, | 256 | |
257 | void Storage::NamedDatabase::findLatest(const QByteArray &k, | ||
257 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | 258 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, |
258 | const std::function<void(const Storage::Error &error)> &errorHandler) const | 259 | const std::function<void(const Storage::Error &error)> &errorHandler) const |
259 | { | 260 | { |
260 | QByteArray latestKey; | 261 | if (!d || !d->transaction) { |
261 | scan(uid, [&](const QByteArray &key, const QByteArray &value) -> bool { | 262 | //Not an error. We rely on this to read nothing from non-existing databases. |
262 | latestKey = key; | 263 | return; |
263 | return true; | 264 | } |
264 | }, | ||
265 | errorHandler, true); | ||
266 | 265 | ||
267 | scan(latestKey, [=](const QByteArray &key, const QByteArray &value) -> bool { | 266 | int rc; |
268 | resultHandler(key, value); | 267 | MDB_val key; |
269 | return false; | 268 | MDB_val data; |
270 | }, | 269 | MDB_cursor *cursor; |
271 | errorHandler); | 270 | |
271 | key.mv_data = (void*)k.constData(); | ||
272 | key.mv_size = k.size(); | ||
273 | |||
274 | rc = mdb_cursor_open(d->transaction, d->dbi, &cursor); | ||
275 | if (rc) { | ||
276 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during mdb_cursor open: ") + QByteArray(mdb_strerror(rc))); | ||
277 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | MDB_cursor_op op = MDB_SET_RANGE; | ||
282 | if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) { | ||
283 | //The first lookup will find a key that is equal or greather than our key | ||
284 | if (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) { | ||
285 | bool advanced = false; | ||
286 | while (QByteArray::fromRawData((char*)key.mv_data, key.mv_size).startsWith(k)) { | ||
287 | advanced = true; | ||
288 | MDB_cursor_op nextOp = MDB_NEXT; | ||
289 | rc = mdb_cursor_get(cursor, &key, &data, nextOp); | ||
290 | if (rc) { | ||
291 | break; | ||
292 | } | ||
293 | } | ||
294 | if (advanced) { | ||
295 | MDB_cursor_op prefOp = MDB_PREV; | ||
296 | //We read past the end above, just take the last value | ||
297 | if (rc == MDB_NOTFOUND) { | ||
298 | prefOp = MDB_LAST; | ||
299 | } | ||
300 | rc = mdb_cursor_get(cursor, &key, &data, prefOp); | ||
301 | resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size)); | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | //We never find the last value | ||
307 | if (rc == MDB_NOTFOUND) { | ||
308 | rc = 0; | ||
309 | } | ||
310 | |||
311 | mdb_cursor_close(cursor); | ||
312 | |||
313 | if (rc) { | ||
314 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); | ||
315 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
316 | } | ||
317 | |||
318 | return; | ||
272 | } | 319 | } |
273 | 320 | ||
274 | 321 | ||
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp index bef8755..d950961 100644 --- a/tests/storagetest.cpp +++ b/tests/storagetest.cpp | |||
@@ -376,6 +376,7 @@ private Q_SLOTS: | |||
376 | db.write("sub1","value1"); | 376 | db.write("sub1","value1"); |
377 | db.write("sub2","value2"); | 377 | db.write("sub2","value2"); |
378 | db.write("wub3","value3"); | 378 | db.write("wub3","value3"); |
379 | db.write("wub4","value4"); | ||
379 | QByteArray result; | 380 | QByteArray result; |
380 | db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { | 381 | db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { |
381 | result = value; | 382 | result = value; |
@@ -384,6 +385,35 @@ private Q_SLOTS: | |||
384 | QCOMPARE(result, QByteArray("value2")); | 385 | QCOMPARE(result, QByteArray("value2")); |
385 | } | 386 | } |
386 | 387 | ||
388 | void testFindLatestInSingle() | ||
389 | { | ||
390 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | ||
391 | auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite); | ||
392 | auto db = transaction.openDatabase("test", nullptr, false); | ||
393 | db.write("sub2","value2"); | ||
394 | QByteArray result; | ||
395 | db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { | ||
396 | result = value; | ||
397 | }); | ||
398 | |||
399 | QCOMPARE(result, QByteArray("value2")); | ||
400 | } | ||
401 | |||
402 | void testFindLast() | ||
403 | { | ||
404 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | ||
405 | auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite); | ||
406 | auto db = transaction.openDatabase("test", nullptr, false); | ||
407 | db.write("sub2","value2"); | ||
408 | db.write("wub3","value3"); | ||
409 | QByteArray result; | ||
410 | db.findLatest("wub", [&](const QByteArray &key, const QByteArray &value) { | ||
411 | result = value; | ||
412 | }); | ||
413 | |||
414 | QCOMPARE(result, QByteArray("value3")); | ||
415 | } | ||
416 | |||
387 | void testRecordRevision() | 417 | void testRecordRevision() |
388 | { | 418 | { |
389 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | 419 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); |