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

Re: (ITS#7515) Nested liblmdb transaction bugs

MDB can act on P_DIRTY without checking whether the page is
dirty in this txn's dirty_list, or only some ancestor txn's:

* mdb_cursor_put() line 5094, overwriting an F_BIGDATA node:
  Let a nested txn replace a big item whose page is dirty
  only in an ancestor txn.  The parent will see the new
  value if the old page had room for it, even if the child
  aborted.  Test program enclosed.

  Proposed fix: No need to search dirty_list, mdb_page_get()
  just did.  Make it return where the page was found instead
  of an error code: <0 not found, 0 in map, >0 txn nesting
  level of the dirty_list with the page.  Branch mdb/its7515
  in <http://folk.uio.no/hbf/OpenLDAP/openldap.git>, except
  the commit message may need tweaks.

* mdb_xcursor_init1(): Sets DB_DIRTY if mc_top has P_DIRTY.
  Seems to cancel some MDB_PS_MODIFY activity. Is that OK?

#include <lmdb.h>
#include <stdio.h>

int main(void)
    static char pTxt[9999] = "parent",  cTxt[9999] = "child";
    MDB_val key = {1,""}, pVal = {9999,pTxt}, cVal = {9999,cTxt}, val;
    MDB_env *env;
    MDB_txn *txn, *ctxn;
    MDB_dbi dbi;
    mdb_env_open(env, "test.mdb", MDB_NOSUBDIR, 0666);
    mdb_txn_begin(env, 0, 0, &txn);
    mdb_dbi_open(txn, 0, 0, &dbi);
    mdb_put(txn, dbi, &key, &pVal, 0);  /* put "parent" */
    mdb_txn_begin(env, txn, 0, &ctxn);
    mdb_put(ctxn, dbi, &key, &cVal, 0); /* overwrite with "child" */
    mdb_get(txn, dbi, &key, &val);      /* should get "parent" */
    puts(val.mv_data);                  /* ...but got "child" */
    return 0;