[Date Prev][Date Next]
[Chronological]
[Thread]
[Top]
Re: (ITS#7829) MDB mdb_cursor_del causes records to be skipped
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;
}