From 427b1d38d870e6876e10349b1081b56ff3390dec Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 23 May 2018 17:20:34 +0200 Subject: Add and test findAllInRange for ranged lookups --- common/storage_lmdb.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'common/storage_lmdb.cpp') diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 5fb1d0f..bd9bdce 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp @@ -558,6 +558,68 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi return; } +int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, + const std::function &resultHandler, + const std::function &errorHandler) const +{ + if (!d || !d->transaction) { + // Not an error. We rely on this to read nothing from non-existing databases. + return 0; + } + + MDB_cursor *cursor; + if (int rc = mdb_cursor_open(d->transaction, d->dbi, &cursor)) { + // Invalid arguments can mean that the transaction doesn't contain the db dbi + Error error(d->name.toLatin1() + d->db, getErrorCode(rc), + QByteArray("Error during mdb_cursor_open: ") + QByteArray(mdb_strerror(rc)) + + ". Lower bound: " + lowerBound + " Upper bound: " + upperBound); + errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); + return 0; + } + + int numberOfRetrievedValues = 0; + + MDB_val firstKey = { .mv_size = (size_t)lowerBound.size(), .mv_data = (void *)lowerBound.constData() }, + idealLastKey = { .mv_size = (size_t)upperBound.size(), .mv_data = (void *)upperBound.constData() }, + lastKey = idealLastKey, currentKey; + MDB_val reference; + + // Find the last key past the range + int rc = mdb_cursor_get(cursor, &lastKey, &reference, MDB_SET_RANGE); + + // If only equal, move forward one spot + if (rc == MDB_SUCCESS && mdb_cmp(d->transaction, d->dbi, &lastKey, &idealLastKey) == 0) { + rc = mdb_cursor_get(cursor, &lastKey, &reference, MDB_NEXT); + } + + bool untilEnd = false; + if (rc != MDB_SUCCESS) { + // Nothing is greater than the upper bound, meaning search until the end + untilEnd = true; + } + + // Find the first key in the range + rc = mdb_cursor_get(cursor, &firstKey, &reference, MDB_SET_RANGE); + + if (rc != MDB_SUCCESS) { + // Nothing is greater or equal than the lower bound, meaning no result + return 0; + } + + currentKey = firstKey; + + do { + // If we have a stopping point and this is the stopping point + if (!untilEnd && mdb_cmp(d->transaction, d->dbi, ¤tKey, &lastKey) == 0) { + break; + } + + const auto currentBAKey = QByteArray::fromRawData((char *)currentKey.mv_data, currentKey.mv_size); + const auto currentBAValue = QByteArray::fromRawData((char *)reference.mv_data, reference.mv_size); + resultHandler(currentBAKey, currentBAValue); + } while (mdb_cursor_get(cursor, ¤tKey, &reference, MDB_NEXT) == MDB_SUCCESS); +} + qint64 DataStore::NamedDatabase::getSize() { if (!d || !d->transaction) { -- cgit v1.2.3