101 lines
3.2 KiB
C
101 lines
3.2 KiB
C
|
|
// Define callback function type
|
|
typedef bool (*lmdb_callback_t)(const MDB_val*, const MDB_val*);
|
|
|
|
|
|
int lmdb_foreach(MDB_txn *txn, MDB_dbi dbi, MDB_val *start, MDB_val *start_dup, callback_t cb, bool reverse) {
|
|
int success = 0;
|
|
MDB_cursor *cursor;
|
|
|
|
// Open a cursor on the provided transaction and database
|
|
int rc = mdb_cursor_open(txn, dbi, &cursor);
|
|
|
|
if (rc != 0)
|
|
return 0;
|
|
|
|
MDB_val k = *start, v = *start_dup;
|
|
MDB_cursor_op op = reverse ? MDB_PREV : MDB_NEXT;
|
|
|
|
// If we're scanning in reverse...
|
|
if (reverse) {
|
|
// Try to position the cursor at the first key-value pair where
|
|
// both the key and the value are greater than or equal to our
|
|
// starting point.
|
|
rc = mdb_cursor_get(cursor, &k, &v, MDB_GET_BOTH_RANGE);
|
|
if (rc == 0) {
|
|
if (v.mv_size != start_dup->mv_size ||
|
|
memcmp(v.mv_data, start_dup->mv_data, v.mv_size) != 0) {
|
|
// If the value doesn't match our starting
|
|
// point, step back to the previous record.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_PREV) != 0)
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
// If we couldn't find a record that matches both our
|
|
// starting key and value, try to find a record that
|
|
// matches just our starting key.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_SET) == 0) {
|
|
// If we find a match, move to the last value
|
|
// for this key, since we're scanning in
|
|
// reverse.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_LAST_DUP) != 0)
|
|
goto cleanup;
|
|
} else {
|
|
// If we can't find a record with our starting
|
|
// key, try to find the first record with a key
|
|
// greater than our starting key.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_SET_RANGE) == 0) {
|
|
// If we find such a record, step back
|
|
// to the previous record.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_PREV) != 0)
|
|
goto cleanup;
|
|
} else {
|
|
// If we can't even find a record with
|
|
// a key greater than our starting key,
|
|
// fall back to starting from the last
|
|
// record in the database.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_LAST) != 0)
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
// If we're not scanning in reverse...
|
|
else {
|
|
// Try to position the cursor at the first key-value
|
|
// pair where both the key and the value are greater
|
|
// than or equal to our starting point.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_SET) != 0) {
|
|
// If we couldn't find a record that matches
|
|
// both our starting key and value, try to find
|
|
// a record that matches just our starting key.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_SET_RANGE) != 0)
|
|
goto cleanup;
|
|
|
|
// If we can't find a record with our starting
|
|
// key, try to find the first record with a key
|
|
// greater than our starting key.
|
|
if (mdb_cursor_get(cursor, &k, &v, MDB_FIRST_DUP) != 0)
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Whether we're scanning forward or backward, start the actual
|
|
// iteration, moving one step at a time in the appropriate direction
|
|
// and calling the provided callback for each record.
|
|
do {
|
|
if (!cb(&k, &v))
|
|
goto cleanup;
|
|
} while (mdb_cursor_get(cursor, &k, &v, op) == 0);
|
|
|
|
// If we make it through the entire iteration without the callback
|
|
// returning false, return true to signal success.
|
|
success = 1;
|
|
|
|
cleanup:
|
|
mdb_cursor_close(cursor);
|
|
return success;
|
|
}
|
|
|
|
|