Another mdb_cursor_del() issue: If the next operation moves/peeks at the cursor position, it does not always reset the C_DEL flag:
* mdb_cursor_put(,&existing_key,, MDB_APPEND or MDB_NOOVERWRITE) works like mdb_cursor_get(, &existing_key,, MDB_SET) but does not clear C_DEL. So MDB_NEXT after that stays at &existing_key.
The enclosed program illustrates this: the MDB_NEXTs give different results after the cursor was positioned at "f".
* I'm guessing mdb_cursor_count() should reset C_DEL on the main cursor:
The mdb_cursor_del() semantics seems to be something like "the cursor moves to the next item, however if the next operation is an MDB_NEXT* operation then it also returns that item. So the manner you peek at the cursor position determines whether the the next item is "next" or "current".
But I haven't thought carefully about C_DEL vs. DUPSORT cursors.
BTW, mc->mc_flags &= ~C_DEL; would be a bit simpler than if (mc->mc_flags & C_DEL) mc->mc_flags ^= C_DEL;
################################################################
#include <stdio.h> #include <stdlib.h> #include "lmdb.h" #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
int main(void) { int rc, i; char ch, *act; MDB_env *env; MDB_txn *txn; MDB_dbi dbi; MDB_cursor *cursor; MDB_cursor_op op; remove("test.mdb"); E(mdb_env_create(&env)); E(mdb_env_open(env, "test.mdb", MDB_NOSUBDIR, 0664)); E(mdb_txn_begin(env, NULL, 0, &txn)); E(mdb_open(txn, NULL, 0, &dbi)); E(mdb_cursor_open(txn, dbi, &cursor));
for (op=MDB_NEXT, act="NEXT";; op=MDB_GET_CURRENT, act="GET_CURRENT") { MDB_val key, data = {0, ""}; for (ch = 'a'; ch <= 'h'; ch++) E(mdb_cursor_put(cursor, &(MDB_val){1, &ch}, &data, 0)); for (i = 0; i < 2; i++) { E(mdb_cursor_get(cursor, &key, NULL, MDB_FIRST)); E(mdb_cursor_del(cursor, 0)); if (i == 0) { E(mdb_cursor_get(cursor, &(MDB_val){1, "f"}, NULL, MDB_SET)); } else { rc = mdb_cursor_put(cursor, &(MDB_val){1, "f"}, &data, MDB_NOOVERWRITE); /* C_DEL vs. MDB_NEXT bug? */ CHECK(rc == MDB_KEYEXIST, "no-overwrite"); } rc = mdb_cursor_get(cursor, &key, &data, op); if (rc == MDB_SUCCESS) printf("MDB_%s = %c\n", act, *(char *)key.mv_data); else printf("MDB_%s = %s\n", act, mdb_strerror(rc)); } if (op == MDB_GET_CURRENT) break; }
mdb_txn_abort(txn); mdb_env_close(env); return 0; }