[Date Prev][Date Next] [Chronological] [Thread] [Top]

(ITS#7594) incomplete MDB subdb cursor cleanup

Full_Name: Hallvard B Furuseth
Version: mdb.master 0cdd9dffddf66c730a35f48db2bb02d8bb3e5731
OS: Linux x86_64
Submission from: (NULL) (
Submitted by: hallvard

If a cursor is at a clean subDB page, and its current item is deleted
by another cursor, then MDB_GET_CURRENT returns the deleted item.
Test program below, run with no args, the "m2/del" output line.

If the page was dirty, I instead got (key = old, data = size 0).
I don't know if it should give MDB_NOTFOUND instead.
That's the test program run with one arg.
I did not check what happens if the other cursor rearranged
the pages, e.g. split the item off to another page.

#include "lmdb.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
    char *fname = "test.mdb";
    MDB_env *env;
    MDB_txn *txn;
    MDB_dbi dbi;
    MDB_cursor *mc, *m2;
    MDB_val key, data;
    int rc;
#   define STR2VAL(s) (&(MDB_val){sizeof(s), s}) /* includes final \0 */
#   define SHOW(name) printf("%s:\t%s[%zd] -> %s[%zd]\n", name, \
        (char*)key.mv_data, key.mv_size, (char*)data.mv_data, data.mv_size)
#   define E(e) { rc = (e); if (rc) { fprintf(stderr, "%s:%d: %s: %s\n",\
        __FILE__, __LINE__, #e, mdb_strerror(rc)); abort(); } }

    E(mdb_env_open(env, fname, MDB_NOSUBDIR, 0666));
    E(mdb_txn_begin(env, NULL, 0, &txn));
    E(mdb_dbi_open(txn, NULL, MDB_DUPSORT, &dbi));

    E(mdb_cursor_open(txn, dbi, &mc));
    E(mdb_cursor_put(mc, STR2VAL("a"), STR2VAL("x"), 0));
    E(mdb_cursor_put(mc, STR2VAL("a"), STR2VAL("y"), 0));

    if (argc < 2) {
        E(mdb_txn_begin(env, NULL, 0, &txn));
        E(mdb_cursor_open(txn, dbi, &mc));
        puts("With clean page:");
    E(mdb_cursor_open(txn, dbi, &m2));

    E(mdb_cursor_get(mc, &key, &data, MDB_FIRST));
    E(mdb_cursor_get(m2, &key, &data, MDB_FIRST));
    SHOW("Name:\tKey  -> Data  (mv_data[mv_len]):\n"  "m2");

    E(mdb_cursor_del(mc, 0));
    E(mdb_cursor_get(m2, &key, &data, MDB_GET_CURRENT));
    SHOW("m2/del"); /* Should output the same as... */
    E(mdb_cursor_get(mc, &key, &data, MDB_GET_CURRENT));
    SHOW("mc/del"); /* ...this: "a[2] -> [0]". */

    return 0;