[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;
}