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

(ITS#7517) mdb_dbi_close(dbi updated in existing txn) breaks



Full_Name: Hallvard B Furuseth
Version: mdb.master 48dc782ea612f85e8356a50bfbafe22e5be121cf
OS: Linux x86_64
URL: 
Submission from: (NULL) (193.69.163.163)
Submitted by: hallvard


If a thread modifies a named MDB database and closes it before
committing, the changes are lost.  The mdb_dbi_close() doc only
says we must not close DBs which _other_ theads will reference.

I expect mdb_dbi_close() could check if the env has an active
write txn where the DB is dirty, and mdb_put() it first.  And
in any parent txns which have it open, dunno if that's doable.

If not, in this case maybe _close() could stash away the DB info
so mdb_txn_commit()/mdb_txn_reset0() can save the updates.

Note that another mdb_dbi_open() can reuse the closed DBI, so
carelessness could e.g. save the changes with the wrong DB name.

Test case (no nested txns):

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

int main(void) {
	char *dbname = "test.mdb";
	MDB_env *env;
	MDB_txn *txn;
	MDB_dbi dbi;
	int rdonly, rc;
	MDB_val key = {3, "foo"}, data = {3, "abc"}, rdata;

#	define E(e) { rc = (e); if (rc) { fprintf(stderr, "%s:%d: %s: %s\n", \
		__FILE__, __LINE__, #e, mdb_strerror(rc)); abort(); } }

	remove(dbname);
	for (rdonly = 0; rdonly <= MDB_RDONLY; rdonly += MDB_RDONLY) {
		E(mdb_env_create(&env));
		E(mdb_env_set_maxdbs(env, 1));
		E(mdb_env_open(env, dbname, MDB_NOSYNC|MDB_NOSUBDIR, 0666));
		E(mdb_txn_begin(env, NULL, rdonly, &txn));
		E(mdb_dbi_open(txn, NULL, rdonly ? 0 : MDB_CREATE, &dbi));
		if (!rdonly) {
			E(mdb_put(txn, dbi, &key, &data, 0));
			mdb_dbi_close(env, dbi);
		} else {
			E(mdb_get(txn, dbi, &key, &rdata));
		}
		E(mdb_txn_commit(txn));
		mdb_env_close(env);
	}
	return 0;
}