diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h
index b94bd2d3b8..5cf8bd75ec 100644
--- a/libraries/liblmdb/lmdb.h
+++ b/libraries/liblmdb/lmdb.h
@@ -137,6 +137,12 @@
extern "C" {
#endif
+#ifdef _MSC_VER
+typedef int mdb_mode_t;
+#else
+typedef mode_t mdb_mode_t;
+#endif
+
/** @defgroup mdb MDB API
* @{
* @brief OpenLDAP Lightning Memory-Mapped Database Manager
@@ -498,7 +504,7 @@ int mdb_env_create(MDB_env **env);
*
EAGAIN - the environment was locked by another process.
*
*/
-int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mode_t mode);
+int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode);
/** @brief Copy an MDB environment to the specified path.
*
@@ -761,14 +767,18 @@ int mdb_txn_renew(MDB_txn *txn);
/** @brief Open a database in the environment.
*
* The database handle may be discarded by calling #mdb_dbi_close().
- * It denotes the name and parameters of a database, independently of
- * whether such a database exists. It will not exist if the transaction
- * which created it aborted, nor if another process deleted it. The
- * database handle resides in the shared environment, it is not owned
- * by the given transaction. Only one thread should call this function;
- * it is not mutex-protected in a read-only transaction.
- * Preexisting transactions, other than the current transaction and
- * any parents, must not use the new handle. Nor must their children.
+ * The old database handle is returned if the database was already open.
+ * The handle must only be closed once.
+ * The database handle will be private to the current transaction until
+ * the transaction is successfully committed. If the transaction is
+ * aborted the handle will be closed automatically.
+ * After a successful commit the
+ * handle will reside in the shared environment, and may be used
+ * by other transactions. This function must not be called from
+ * multiple concurrent transactions. A transaction that uses this function
+ * must finish (either commit or abort) before any other transaction may
+ * use this function.
+ *
* To use named databases (with name != NULL), #mdb_env_set_maxdbs()
* must be called before opening the environment.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 7866e89cb6..809b39fab9 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -286,6 +286,8 @@ typedef MDB_ID txnid_t;
#endif
#if !(__STDC_VERSION__ >= 199901L || defined(__GNUC__))
+# undef MDB_DEBUG
+# define MDB_DEBUG 0
# define DPRINTF (void) /* Vararg macros may be unsupported */
#elif MDB_DEBUG
static int mdb_debug;
@@ -743,6 +745,11 @@ typedef struct MDB_db {
pgno_t md_root; /**< the root page of this tree */
} MDB_db;
+ /** mdb_dbi_open flags */
+#define PERSISTENT_FLAGS 0x7fff
+#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\
+ MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE)
+
/** Handle for the DB used to track free pages. */
#define FREE_DBI 0
/** Handle for the default DB. */
@@ -822,6 +829,9 @@ struct MDB_txn {
*/
#define DB_DIRTY 0x01 /**< DB was written in this txn */
#define DB_STALE 0x02 /**< DB record is older than txnID */
+#define DB_NEW 0x04 /**< DB handle opened in this txn */
+#define DB_VALID 0x08 /**< DB handle is valid */
+#define MDB_VALID 0x8000 /**< DB handle is valid, for me_dbflags */
/** @} */
/** In write txns, array of cursors for each DB */
MDB_cursor **mt_cursors;
@@ -1768,6 +1778,7 @@ mdb_txn_renew0(MDB_txn *txn)
{
MDB_env *env = txn->mt_env;
unsigned int i;
+ uint16_t x;
int rc;
/* Setup db info */
@@ -1832,11 +1843,11 @@ mdb_txn_renew0(MDB_txn *txn)
/* Copy the DB info and flags */
memcpy(txn->mt_dbs, env->me_metas[txn->mt_toggle]->mm_dbs, 2 * sizeof(MDB_db));
- for (i=2; imt_numdbs; i++)
- txn->mt_dbs[i].md_flags = env->me_dbflags[i];
- txn->mt_dbflags[0] = txn->mt_dbflags[1] = 0;
- if (txn->mt_numdbs > 2)
- memset(txn->mt_dbflags+2, DB_STALE, txn->mt_numdbs-2);
+ for (i=2; imt_numdbs; i++) {
+ txn->mt_dbs[i].md_flags = x = env->me_dbflags[i];
+ txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_STALE : 0;
+ }
+ txn->mt_dbflags[0] = txn->mt_dbflags[1] = DB_VALID;
if (env->me_maxpg < txn->mt_next_pgno) {
mdb_txn_reset0(txn);
@@ -1910,6 +1921,7 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
txn->mt_env = env;
if (parent) {
+ unsigned int i;
txn->mt_free_pgs = mdb_midl_alloc();
if (!txn->mt_free_pgs) {
free(txn);
@@ -1932,7 +1944,9 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
txn->mt_numdbs = parent->mt_numdbs;
txn->mt_dbxs = parent->mt_dbxs;
memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
- memcpy(txn->mt_dbflags, parent->mt_dbflags, txn->mt_numdbs);
+ /* Copy parent's mt_dbflags, but clear DB_NEW */
+ for (i=0; imt_numdbs; i++)
+ txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~DB_NEW;
rc = 0;
ntxn = (MDB_ntxn *)txn;
ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */
@@ -1971,13 +1985,23 @@ static void
mdb_txn_reset0(MDB_txn *txn)
{
MDB_env *env = txn->mt_env;
+ unsigned int i;
+
+ /* Close any DBI handles opened in this txn */
+ for (i=2; imt_numdbs; i++) {
+ if (txn->mt_dbflags[i] & DB_NEW) {
+ char *ptr = env->me_dbxs[i].md_name.mv_data;
+ env->me_dbxs[i].md_name.mv_data = NULL;
+ env->me_dbxs[i].md_name.mv_size = 0;
+ free(ptr);
+ }
+ }
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
if (!(env->me_flags & MDB_ROFS))
txn->mt_u.reader->mr_txnid = (txnid_t)-1;
} else {
MDB_page *dp;
- unsigned int i;
/* close(free) all cursors */
for (i=0; imt_numdbs; i++) {
@@ -2081,13 +2105,14 @@ mdb_txn_commit(MDB_txn *txn)
env = txn->mt_env;
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
- if (txn->mt_numdbs > env->me_numdbs) {
- /* update the DB flags */
- MDB_dbi i;
- for (i = env->me_numdbs; imt_numdbs; i++)
- env->me_dbflags[i] = txn->mt_dbs[i].md_flags;
- env->me_numdbs = i;
+ /* update the DB flags */
+ for (i = 2; imt_numdbs; i++) {
+ if (txn->mt_dbflags[i] & DB_NEW)
+ env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
}
+ if (txn->mt_numdbs > env->me_numdbs)
+ env->me_numdbs = txn->mt_numdbs;
+ txn->mt_numdbs = 2; /* so txn_abort() doesn't close any new handles */
mdb_txn_abort(txn);
return MDB_SUCCESS;
}
@@ -2120,8 +2145,14 @@ mdb_txn_commit(MDB_txn *txn)
/* Update parent's DB table. */
memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
- memcpy(parent->mt_dbflags, txn->mt_dbflags, txn->mt_numdbs);
txn->mt_parent->mt_numdbs = txn->mt_numdbs;
+ txn->mt_parent->mt_dbflags[0] = txn->mt_dbflags[0];
+ txn->mt_parent->mt_dbflags[1] = txn->mt_dbflags[1];
+ for (i=2; imt_numdbs; i++) {
+ /* preserve parent's DB_NEW status */
+ x = txn->mt_parent->mt_dbflags[i] & DB_NEW;
+ txn->mt_parent->mt_dbflags[i] = txn->mt_dbflags[i] | x;
+ }
dst = txn->mt_parent->mt_u.dirty_list;
src = txn->mt_u.dirty_list;
@@ -2464,13 +2495,13 @@ sync:
done:
env->me_pglast = 0;
env->me_txn = NULL;
- if (txn->mt_numdbs > env->me_numdbs) {
- /* update the DB flags */
- MDB_dbi i;
- for (i = env->me_numdbs; imt_numdbs; i++)
- env->me_dbflags[i] = txn->mt_dbs[i].md_flags;
- env->me_numdbs = i;
+ /* update the DB flags */
+ for (i = 2; imt_numdbs; i++) {
+ if (txn->mt_dbflags[i] & DB_NEW)
+ env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
}
+ if (txn->mt_numdbs > env->me_numdbs)
+ env->me_numdbs = txn->mt_numdbs;
UNLOCK_MUTEX_W(env);
free(txn);
@@ -3378,7 +3409,7 @@ fail:
#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY|MDB_WRITEMAP)
int
-mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mode_t mode)
+mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
{
int oflags, rc, len, excl;
char *lpath, *dpath;
@@ -3705,10 +3736,14 @@ void
mdb_env_close(MDB_env *env)
{
MDB_page *dp;
+ int i;
if (env == NULL)
return;
+ for (i = env->me_numdbs; --i > MAIN_DBI; )
+ free(env->me_dbxs[i].md_name.mv_data);
+
VGMEMP_DESTROY(env);
while ((dp = env->me_dpages) != NULL) {
VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next));
@@ -4124,13 +4159,14 @@ mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags)
/* The txn may not know this DBI, or another process may
* have dropped and recreated the DB with other flags.
*/
- if (mc->mc_db->md_flags != flags)
+ if ((mc->mc_db->md_flags & PERSISTENT_FLAGS) != flags)
return MDB_INCOMPATIBLE;
memcpy(mc->mc_db, data.mv_data, sizeof(MDB_db));
}
if (flags & MDB_PS_MODIFY)
dbflag = DB_DIRTY;
- *mc->mc_dbflag = dbflag;
+ *mc->mc_dbflag &= ~DB_STALE;
+ *mc->mc_dbflag |= dbflag;
}
}
root = mc->mc_db->md_root;
@@ -4208,7 +4244,7 @@ mdb_get(MDB_txn *txn, MDB_dbi dbi,
assert(data);
DPRINTF("===> get db %u key [%s]", dbi, DKEY(key));
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
if (key->mv_size == 0 || key->mv_size > MDB_MAXKEYSIZE) {
@@ -4267,6 +4303,8 @@ mdb_cursor_sibling(MDB_cursor *mc, int move_right)
return rc;
mdb_cursor_push(mc, mp);
+ if (!move_right)
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1;
return MDB_SUCCESS;
}
@@ -4661,9 +4699,9 @@ mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data)
}
assert(IS_LEAF(mc->mc_pg[mc->mc_top]));
- mc->mc_flags |= C_INITIALIZED|C_EOF;
mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1;
}
+ mc->mc_flags |= C_INITIALIZED|C_EOF;
leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
@@ -4791,7 +4829,7 @@ fetchm:
case MDB_PREV:
case MDB_PREV_DUP:
case MDB_PREV_NODUP:
- if (!(mc->mc_flags & C_INITIALIZED) || (mc->mc_flags & C_EOF)) {
+ if (!(mc->mc_flags & C_INITIALIZED)) {
rc = mdb_cursor_last(mc, key, data);
mc->mc_flags |= C_INITIALIZED;
mc->mc_ki[mc->mc_top]++;
@@ -4850,7 +4888,7 @@ mdb_cursor_touch(MDB_cursor *mc)
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
if (rc)
return rc;
- *mc->mc_dbflag = DB_DIRTY;
+ *mc->mc_dbflag |= DB_DIRTY;
}
for (mc->mc_top = 0; mc->mc_top < mc->mc_snum; mc->mc_top++) {
rc = mdb_page_touch(mc);
@@ -4912,7 +4950,7 @@ mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
mdb_cursor_push(mc, np);
mc->mc_db->md_root = np->mp_pgno;
mc->mc_db->md_depth++;
- *mc->mc_dbflag = DB_DIRTY;
+ *mc->mc_dbflag |= DB_DIRTY;
if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED))
== MDB_DUPFIXED)
np->mp_flags |= P_LEAF2;
@@ -4930,8 +4968,9 @@ mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
if (rc > 0) {
rc = MDB_NOTFOUND;
mc->mc_ki[mc->mc_top]++;
- } else {
- rc = 0;
+ } else if (rc < 0) {
+ /* new key is < last key */
+ rc = MDB_KEYEXIST;
}
}
} else {
@@ -5705,8 +5744,8 @@ mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node)
}
DPRINTF("Sub-db %u for db %u root page %zu", mx->mx_cursor.mc_dbi, mc->mc_dbi,
mx->mx_db.md_root);
- mx->mx_dbflag = (F_ISSET(mc->mc_pg[mc->mc_top]->mp_flags, P_DIRTY)) ?
- DB_DIRTY : 0;
+ mx->mx_dbflag = DB_VALID | (F_ISSET(mc->mc_pg[mc->mc_top]->mp_flags, P_DIRTY) ?
+ DB_DIRTY : 0);
mx->mx_dbx.md_name.mv_data = NODEKEY(node);
mx->mx_dbx.md_name.mv_size = node->mn_ksize;
#if UINT_MAX < SIZE_MAX
@@ -5752,7 +5791,7 @@ mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret)
MDB_xcursor *mx = NULL;
size_t size = sizeof(MDB_cursor);
- if (txn == NULL || ret == NULL || dbi >= txn->mt_numdbs)
+ if (txn == NULL || ret == NULL || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
/* Allow read access to the freelist */
@@ -6252,8 +6291,8 @@ static int
mdb_rebalance(MDB_cursor *mc)
{
MDB_node *node;
- int rc, minkeys;
- unsigned int ptop;
+ int rc;
+ unsigned int ptop, minkeys;
MDB_cursor mn;
#if MDB_DEBUG
@@ -6333,6 +6372,8 @@ mdb_rebalance(MDB_cursor *mc)
if (m3->mc_snum < mc->mc_snum) continue;
if (m3->mc_pg[0] == mp) {
m3->mc_pg[0] = mc->mc_pg[0];
+ m3->mc_snum = 1;
+ m3->mc_top = 0;
}
}
}
@@ -6446,7 +6487,7 @@ mdb_del(MDB_txn *txn, MDB_dbi dbi,
DPRINTF("====> delete db %u key [%s]", dbi, DKEY(key));
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
@@ -6897,7 +6938,7 @@ mdb_put(MDB_txn *txn, MDB_dbi dbi,
assert(key != NULL);
assert(data != NULL);
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
@@ -6908,7 +6949,7 @@ mdb_put(MDB_txn *txn, MDB_dbi dbi,
return EINVAL;
}
- if ((flags & (MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND)) != flags)
+ if ((flags & (MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP)) != flags)
return EINVAL;
mdb_cursor_init(&mc, txn, dbi, &mx);
@@ -7019,15 +7060,11 @@ mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi)
: ((f & MDB_REVERSEDUP) ? mdb_cmp_memnr : mdb_cmp_memn));
}
-#define PERSISTENT_FLAGS 0xffff
-#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\
- MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE)
int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
{
MDB_val key, data;
MDB_dbi i;
MDB_cursor mc;
- uint16_t mdflags;
int rc, dbflag, exact;
unsigned int unused = 0;
size_t len;
@@ -7078,7 +7115,7 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
return MDB_DBS_FULL;
/* Find the DB info */
- dbflag = 0;
+ dbflag = DB_NEW|DB_VALID;
exact = 0;
key.mv_size = len;
key.mv_data = (void *)name;
@@ -7098,7 +7135,7 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
dummy.md_root = P_INVALID;
dummy.md_flags = flags & PERSISTENT_FLAGS;
rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
- dbflag = DB_DIRTY;
+ dbflag |= DB_DIRTY;
}
/* OK, got info, add to table */
@@ -7110,18 +7147,10 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
txn->mt_dbflags[slot] = dbflag;
memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
*dbi = slot;
- txn->mt_env->me_dbflags[slot] = mdflags = txn->mt_dbs[slot].md_flags;
+ txn->mt_env->me_dbflags[slot] = txn->mt_dbs[slot].md_flags;
mdb_default_cmp(txn, slot);
if (!unused) {
txn->mt_numdbs++;
- txn->mt_env->me_numdbs++;
- }
- /* Open the DB in parent txns as well */
- while ((txn = txn->mt_parent) != NULL) {
- txn->mt_dbflags[slot] = DB_STALE;
- txn->mt_dbs[slot].md_flags = mdflags;
- if (!unused)
- txn->mt_numdbs++;
}
}
@@ -7164,7 +7193,7 @@ mdb_drop0(MDB_cursor *mc, int subs)
unsigned int i;
/* LEAF2 pages have no nodes, cannot have sub-DBs */
- if (!subs || IS_LEAF2(mc->mc_pg[mc->mc_top]))
+ if (IS_LEAF2(mc->mc_pg[mc->mc_top]))
mdb_cursor_pop(mc);
mdb_cursor_copy(mc, &mx);
@@ -7172,7 +7201,15 @@ mdb_drop0(MDB_cursor *mc, int subs)
if (IS_LEAF(mc->mc_pg[mc->mc_top])) {
for (i=0; imc_pg[mc->mc_top]); i++) {
ni = NODEPTR(mc->mc_pg[mc->mc_top], i);
- if (ni->mn_flags & F_SUBDATA) {
+ if (ni->mn_flags & F_BIGDATA) {
+ int j, ovpages = OVPAGES(NODEDSZ(ni), mc->mc_txn->mt_env->me_psize);
+ pgno_t pg;
+ memcpy(&pg, NODEDATA(ni), sizeof(pg));
+ for (j=0; jmc_txn->mt_free_pgs, pg);
+ pg++;
+ }
+ } else if (subs && (ni->mn_flags & F_SUBDATA)) {
mdb_xcursor_init1(mc, ni);
rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
if (rc)
@@ -7190,14 +7227,18 @@ mdb_drop0(MDB_cursor *mc, int subs)
}
if (!mc->mc_top)
break;
+ mc->mc_ki[mc->mc_top] = i;
rc = mdb_cursor_sibling(mc, 1);
if (rc) {
/* no more siblings, go back to beginning
* of previous level.
*/
mdb_cursor_pop(mc);
- for (i=1; imc_top; i++)
+ mc->mc_ki[0] = 0;
+ for (i=1; imc_snum; i++) {
+ mc->mc_ki[i] = 0;
mc->mc_pg[i] = mx.mc_pg[i];
+ }
}
}
/* free it */
@@ -7212,7 +7253,7 @@ int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
MDB_cursor *mc;
int rc;
- if (!txn || !dbi || dbi >= txn->mt_numdbs || (unsigned)del > 1)
+ if (!txn || !dbi || dbi >= txn->mt_numdbs || (unsigned)del > 1 || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
@@ -7243,19 +7284,7 @@ int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
txn->mt_dbs[dbi].md_entries = 0;
txn->mt_dbs[dbi].md_root = P_INVALID;
- if (!txn->mt_u.dirty_list[0].mid) {
- MDB_cursor m2;
- MDB_val key, data;
- /* make sure we have at least one dirty page in this txn
- * otherwise these changes will be ignored.
- */
- key.mv_size = sizeof(txnid_t);
- key.mv_data = &txn->mt_txnid;
- data.mv_size = sizeof(MDB_ID);
- data.mv_data = txn->mt_free_pgs;
- mdb_cursor_init(&m2, txn, FREE_DBI, NULL);
- rc = mdb_cursor_put(&m2, &key, &data, 0);
- }
+ txn->mt_flags |= MDB_TXN_DIRTY;
}
leave:
mdb_cursor_close(mc);
@@ -7264,7 +7293,7 @@ leave:
int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
{
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
txn->mt_dbxs[dbi].md_cmp = cmp;
@@ -7273,7 +7302,7 @@ int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
{
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
txn->mt_dbxs[dbi].md_dcmp = cmp;
@@ -7282,7 +7311,7 @@ int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel)
{
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
txn->mt_dbxs[dbi].md_rel = rel;
@@ -7291,7 +7320,7 @@ int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel)
int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx)
{
- if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
+ if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
txn->mt_dbxs[dbi].md_relctx = ctx;
diff --git a/libraries/liblmdb/mtest.c b/libraries/liblmdb/mtest.c
index 8c8dd57835..55cdd43c38 100644
--- a/libraries/liblmdb/mtest.c
+++ b/libraries/liblmdb/mtest.c
@@ -25,7 +25,7 @@ int main(int argc,char * argv[])
MDB_val key, data;
MDB_txn *txn;
MDB_stat mst;
- MDB_cursor *cursor;
+ MDB_cursor *cursor, *cur2;
int count;
int *values;
char sval[32];
@@ -54,7 +54,11 @@ int main(int argc,char * argv[])
for (i=0;i