diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h
index 280b43795c..954ffde1cb 100644
--- a/libraries/liblmdb/lmdb.h
+++ b/libraries/liblmdb/lmdb.h
@@ -206,6 +206,9 @@ typedef struct MDB_cursor MDB_cursor;
* #MDB_MAXKEYSIZE inclusive. This currently defaults to 511. The
* same applies to data sizes in databases with the #MDB_DUPSORT flag.
* Other data items can in theory be from 0 to 0xffffffff bytes long.
+ *
+ * Values returned from the database are valid only until a subsequent
+ * update operation, or the end of the transaction.
*/
typedef struct MDB_val {
size_t mv_size; /**< size of the data item */
@@ -691,8 +694,7 @@ int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs);
* @note A transaction and its cursors must only be used by a single
* thread, and a thread may only have a single transaction at a time.
* If #MDB_NOTLS is in use, this does not apply to read-only transactions.
- * @note Cursors may not span transactions; each cursor must be opened and closed
- * within a single transaction.
+ * @note Cursors may not span transactions.
* @param[in] env An environment handle returned by #mdb_env_create()
* @param[in] parent If this parameter is non-NULL, the new transaction
* will be a nested transaction, with the transaction indicated by \b parent
@@ -723,8 +725,10 @@ int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **
/** @brief Commit all the operations of a transaction into the database.
*
- * All cursors opened within the transaction will be closed by this call. The cursors
- * and transaction handle will be freed and must not be used again after this call.
+ * The transaction handle is freed. It and its cursors must not be used
+ * again after this call, except with #mdb_cursor_renew().
+ * @note Earlier documentation incorrectly said all cursors would be freed.
+ * Only write-transactions free cursors.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @return A non-zero error value on failure and 0 on success. Some possible
* errors are:
@@ -739,8 +743,10 @@ int mdb_txn_commit(MDB_txn *txn);
/** @brief Abandon all the operations of the transaction instead of saving them.
*
- * All cursors opened within the transaction will be closed by this call. The cursors
- * and transaction handle will be freed and must not be used again after this call.
+ * The transaction handle is freed. It and its cursors must not be used
+ * again after this call, except with #mdb_cursor_renew().
+ * @note Earlier documentation incorrectly said all cursors would be freed.
+ * Only write-transactions free cursors.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
*/
void mdb_txn_abort(MDB_txn *txn);
@@ -754,8 +760,8 @@ void mdb_txn_abort(MDB_txn *txn);
* lock is released, but the table slot stays tied to its thread or
* #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free
* its lock table slot if MDB_NOTLS is in use.
- * All cursors opened within the transaction must be closed before the transaction
- * is reset.
+ * Cursors opened within the transaction must not be used
+ * again after this call, except with #mdb_cursor_renew().
* Reader locks generally don't interfere with writers, but they keep old
* versions of database pages allocated. Thus they prevent the old pages
* from being reused when writers commit new data, and so under heavy load
@@ -787,6 +793,8 @@ int mdb_txn_renew(MDB_txn *txn);
/** @brief Open a database in the environment.
*
+ * A database handle denotes the name and parameters of a database,
+ * independently of whether such a database exists.
* The database handle may be discarded by calling #mdb_dbi_close().
* The old database handle is returned if the database was already open.
* The handle must only be closed once.
@@ -978,6 +986,8 @@ int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx);
* database. The caller need not dispose of the memory, and may not
* modify it in any way. For values returned in a read-only transaction
* any modification attempts will cause a SIGSEGV.
+ * @note Values returned from the database are valid only until a
+ * subsequent update operation, or the end of the transaction.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @param[in] dbi A database handle returned by #mdb_dbi_open()
* @param[in] key The key to search for in the database
@@ -1017,7 +1027,8 @@ int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
* parameter will be set to point to the existing item.
*
#MDB_RESERVE - reserve space for data of the given size, but
* don't copy the given data. Instead, return a pointer to the
- * reserved space, which the caller can fill in later. This saves
+ * reserved space, which the caller can fill in later - before
+ * the next update operation or the transaction ends. This saves
* an extra memcpy if the data is being generated later.
* #MDB_APPEND - append the given key/data pair to the end of the
* database. No key comparisons are performed. This option allows
@@ -1065,7 +1076,16 @@ int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
/** @brief Create a cursor handle.
*
* A cursor is associated with a specific transaction and database.
- * It must be closed before its transaction ends.
+ * A cursor cannot be used when its database handle is closed. Nor
+ * when its transaction has ended, except with #mdb_cursor_renew().
+ * It can be discarded with #mdb_cursor_close().
+ * A cursor in a write-transaction can be closed before its transaction
+ * ends, and will otherwise be closed when its transaction ends.
+ * A cursor in a read-only transaction must be closed explicitly, before
+ * or after its transaction ends. It can be reused with
+ * #mdb_cursor_renew() before finally closing it.
+ * @note Earlier documentation said that cursors in every transaction
+ * were closed when the transaction committed or aborted.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @param[in] dbi A database handle returned by #mdb_dbi_open()
* @param[out] cursor Address where the new #MDB_cursor handle will be stored
@@ -1080,6 +1100,7 @@ int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor);
/** @brief Close a cursor handle.
*
* The cursor handle will be freed and must not be used again after this call.
+ * Its transaction must still be live if it is a write-transaction.
* @param[in] cursor A cursor handle returned by #mdb_cursor_open()
*/
void mdb_cursor_close(MDB_cursor *cursor);
@@ -1087,11 +1108,11 @@ void mdb_cursor_close(MDB_cursor *cursor);
/** @brief Renew a cursor handle.
*
* A cursor is associated with a specific transaction and database.
- * It must be closed before its transaction ends.
* Cursors that are only used in read-only
* transactions may be re-used, to avoid unnecessary malloc/free overhead.
* The cursor may be associated with a new read-only transaction, and
* referencing the same database handle as it was created with.
+ * This may be done whether the previous transaction is live or dead.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @param[in] cursor A cursor handle returned by #mdb_cursor_open()
* @return A non-zero error value on failure and 0 on success. Some possible
@@ -1121,6 +1142,7 @@ MDB_dbi mdb_cursor_dbi(MDB_cursor *cursor);
* case of the #MDB_SET option, in which the \b key object is unchanged), and
* the address and length of the data are returned in the object to which \b data
* refers.
+ * See #mdb_get() for restrictions on using the output values.
* @param[in] cursor A cursor handle returned by #mdb_cursor_open()
* @param[in,out] key The key for a retrieved item
* @param[in,out] data The data of a retrieved item
@@ -1230,7 +1252,7 @@ int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b);
/** @brief Compare two data items according to a particular database.
*
* This returns a comparison as if the two items were data items of
- * a sorted duplicates #MDB_DUPSORT database.
+ * the specified database. The database must have the #MDB_DUPSORT flag.
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @param[in] dbi A database handle returned by #mdb_dbi_open()
* @param[in] a The first item to compare
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 09fc3e29e8..80336831e1 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -900,6 +900,7 @@ struct MDB_cursor {
#define C_SHADOW 0x08 /**< Cursor is a dup from a parent txn */
#define C_ALLOCD 0x10 /**< Cursor was malloc'd */
#define C_SPLITTING 0x20 /**< Cursor is in page_split */
+#define C_UNTRACK 0x40 /**< Un-track cursor when closing */
/** @} */
unsigned int mc_flags; /**< @ref mdb_cursor */
MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */
@@ -1013,6 +1014,9 @@ static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata,
static int mdb_env_read_header(MDB_env *env, MDB_meta *meta);
static int mdb_env_pick_meta(const MDB_env *env);
static int mdb_env_write_meta(MDB_txn *txn);
+#if !(defined(_WIN32) || defined(MDB_USE_POSIX_SEM)) /* Drop unused excl arg */
+# define mdb_env_close0(env, excl) mdb_env_close1(env)
+#endif
static void mdb_env_close0(MDB_env *env, int excl);
static MDB_node *mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp);
@@ -1135,7 +1139,7 @@ mdb_dkey(MDB_val *key, char *buf)
}
/** Display all the keys in the page. */
-static void
+void
mdb_page_list(MDB_page *mp)
{
MDB_node *node;
@@ -1202,9 +1206,8 @@ static void mdb_audit(MDB_txn *txn)
count = 0;
for (i = 0; imt_numdbs; i++) {
- MDB_xcursor mx, *mxp;
- mxp = (txn->mt_dbs[i].md_flags & MDB_DUPSORT) ? &mx : NULL;
- mdb_cursor_init(&mc, txn, i, mxp);
+ MDB_xcursor mx;
+ mdb_cursor_init(&mc, txn, i, &mx);
if (txn->mt_dbs[i].md_root == P_INVALID)
continue;
count += txn->mt_dbs[i].md_branch_pages +
@@ -1245,10 +1248,7 @@ mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
int
mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
{
- if (txn->mt_dbxs[dbi].md_dcmp)
- return txn->mt_dbxs[dbi].md_dcmp(a, b);
- else
- return EINVAL; /* too bad you can't distinguish this from a valid result */
+ return txn->mt_dbxs[dbi].md_dcmp(a, b);
}
/** Allocate a single page.
@@ -1398,6 +1398,7 @@ none:
mdb_cursor_init(&m2, txn, FREE_DBI, NULL);
do {
+#ifdef MDB_PARANOID /* Seems like we can ignore this now */
/* If on freelist, don't try to read more. If what we have
* right now isn't enough just use new pages.
* TODO: get all of this working. Many circular dependencies...
@@ -1406,6 +1407,7 @@ none:
retry = 0;
readit = 0;
}
+#endif
if (readit) {
MDB_val key, data;
pgno_t *idl, *mop2;
@@ -1704,7 +1706,7 @@ mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst)
mc->mc_dbflag = &dst->mt_dbflags[i];
mc->mc_snum = m2->mc_snum;
mc->mc_top = m2->mc_top;
- mc->mc_flags = m2->mc_flags | C_SHADOW;
+ mc->mc_flags = m2->mc_flags | (C_SHADOW|C_ALLOCD);
for (j=0; jmc_snum; j++) {
mc->mc_pg[j] = m2->mc_pg[j];
mc->mc_ki[j] = m2->mc_ki[j];
@@ -1740,30 +1742,34 @@ mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst)
return MDB_SUCCESS;
}
-/** Merge shadow cursors back into parent's */
+/** Close this write txn's cursors, after optionally merging its shadow
+ * cursors back into parent's.
+ * @param[in] txn the transaction handle.
+ * @param[in] merge 0 to not merge cursors, C_SHADOW to merge.
+ * @return 0 on success, non-zero on failure.
+ */
static void
-mdb_cursor_merge(MDB_txn *txn)
+mdb_cursors_close(MDB_txn *txn, unsigned merge)
{
- MDB_dbi i;
- for (i=0; imt_numdbs; i++) {
- if (txn->mt_cursors[i]) {
- MDB_cursor *mc;
- while ((mc = txn->mt_cursors[i])) {
- txn->mt_cursors[i] = mc->mc_next;
- if (mc->mc_flags & C_SHADOW) {
+ MDB_cursor **cursors = txn->mt_cursors, *mc, *next;
+ int i, j;
+
+ for (i = txn->mt_numdbs; --i >= 0; ) {
+ for (mc = cursors[i]; mc; mc = next) {
+ next = mc->mc_next;
+ if (mc->mc_flags & merge) {
MDB_cursor *m2 = mc->mc_orig;
- unsigned int j;
m2->mc_snum = mc->mc_snum;
m2->mc_top = mc->mc_top;
- for (j=0; jmc_snum; j++) {
+ for (j = mc->mc_snum; --j >= 0; ) {
m2->mc_pg[j] = mc->mc_pg[j];
m2->mc_ki[j] = mc->mc_ki[j];
}
}
if (mc->mc_flags & C_ALLOCD)
free(mc);
- }
}
+ cursors[i] = NULL;
}
}
@@ -1780,7 +1786,7 @@ mdb_txn_renew0(MDB_txn *txn)
MDB_env *env = txn->mt_env;
unsigned int i;
uint16_t x;
- int rc;
+ int rc, new_notls = 0;
/* Setup db info */
txn->mt_numdbs = env->me_numdbs;
@@ -1817,9 +1823,9 @@ mdb_txn_renew0(MDB_txn *txn)
env->me_numreaders = env->me_txns->mti_numreaders;
UNLOCK_MUTEX_R(env);
r = &env->me_txns->mti_readers[i];
- if (!(env->me_flags & MDB_NOTLS) &&
- (rc = pthread_setspecific(env->me_txkey, r)) != 0) {
- env->me_txns->mti_readers[i].mr_pid = 0;
+ new_notls = (env->me_flags & MDB_NOTLS);
+ if (!new_notls && (rc=pthread_setspecific(env->me_txkey, r))) {
+ r->mr_pid = 0;
return rc;
}
}
@@ -1858,6 +1864,10 @@ mdb_txn_renew0(MDB_txn *txn)
if (env->me_maxpg < txn->mt_next_pgno) {
mdb_txn_reset0(txn);
+ if (new_notls) {
+ txn->mt_u.reader->mr_pid = 0;
+ txn->mt_u.reader = NULL;
+ }
return MDB_MAP_RESIZED;
}
@@ -1869,7 +1879,7 @@ mdb_txn_renew(MDB_txn *txn)
{
int rc;
- if (!txn || txn->mt_numdbs || !(txn->mt_flags & MDB_TXN_RDONLY))
+ if (!txn || txn->mt_dbxs) /* A reset txn has mt_dbxs==NULL */
return EINVAL;
if (txn->mt_env->me_flags & MDB_FATAL_ERROR) {
@@ -1929,14 +1939,11 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
if (parent) {
unsigned int i;
- txn->mt_free_pgs = mdb_midl_alloc();
- if (!txn->mt_free_pgs) {
- free(txn);
- return ENOMEM;
- }
txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE);
- if (!txn->mt_u.dirty_list) {
- free(txn->mt_free_pgs);
+ if (!txn->mt_u.dirty_list ||
+ !(txn->mt_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)))
+ {
+ free(txn->mt_u.dirty_list);
free(txn);
return ENOMEM;
}
@@ -1985,6 +1992,32 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
return rc;
}
+/** Export or close DBI handles opened in this txn. */
+static void
+mdb_dbis_update(MDB_txn *txn, int keep)
+{
+ int i;
+ MDB_dbi n = txn->mt_numdbs;
+ MDB_env *env = txn->mt_env;
+ unsigned char *tdbflags = txn->mt_dbflags;
+
+ for (i = n; --i >= 2;) {
+ if (tdbflags[i] & DB_NEW) {
+ if (keep) {
+ env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
+ } else {
+ 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;
+ env->me_dbflags[i] = 0;
+ free(ptr);
+ }
+ }
+ }
+ if (keep && env->me_numdbs < n)
+ env->me_numdbs = n;
+}
+
/** Common code for #mdb_txn_reset() and #mdb_txn_abort().
* May be called twice for readonly txns: First reset it, then abort.
* @param[in] txn the transaction handle to reset
@@ -1996,14 +2029,7 @@ mdb_txn_reset0(MDB_txn *txn)
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);
- }
- }
+ mdb_dbis_update(txn, 0);
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
if (txn->mt_u.reader) {
@@ -2011,21 +2037,12 @@ mdb_txn_reset0(MDB_txn *txn)
if (!(env->me_flags & MDB_NOTLS))
txn->mt_u.reader = NULL; /* txn does not own reader */
}
- txn->mt_numdbs = 0; /* mark txn as reset, do not close DBs again */
+ txn->mt_numdbs = 0; /* close nothing if called again */
+ txn->mt_dbxs = NULL; /* mark txn as reset */
} else {
MDB_page *dp;
- /* close(free) all cursors */
- for (i=0; imt_numdbs; i++) {
- if (txn->mt_cursors[i]) {
- MDB_cursor *mc;
- while ((mc = txn->mt_cursors[i])) {
- txn->mt_cursors[i] = mc->mc_next;
- if (mc->mc_flags & C_ALLOCD)
- free(mc);
- }
- }
- }
+ mdb_cursors_close(txn, 0);
if (!(env->me_flags & MDB_WRITEMAP)) {
/* return all dirty pages to dpage list */
@@ -2118,20 +2135,18 @@ mdb_txn_commit(MDB_txn *txn)
assert(txn->mt_env != NULL);
if (txn->mt_child) {
- mdb_txn_commit(txn->mt_child);
+ rc = mdb_txn_commit(txn->mt_child);
txn->mt_child = NULL;
+ if (rc) {
+ mdb_txn_abort(txn);
+ return rc;
+ }
}
env = txn->mt_env;
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
- /* 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;
+ mdb_dbis_update(txn, 1);
txn->mt_numdbs = 2; /* so txn_abort() doesn't close any new handles */
mdb_txn_abort(txn);
return MDB_SUCCESS;
@@ -2160,8 +2175,8 @@ mdb_txn_commit(MDB_txn *txn)
parent->mt_next_pgno = txn->mt_next_pgno;
parent->mt_flags = txn->mt_flags;
- /* Merge (and close) our cursors with parent's */
- mdb_cursor_merge(txn);
+ /* Merge our cursors into parent's and close them */
+ mdb_cursors_close(txn, C_SHADOW);
/* Update parent's DB table. */
memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
@@ -2220,6 +2235,8 @@ mdb_txn_commit(MDB_txn *txn)
return EINVAL;
}
+ mdb_cursors_close(txn, 0);
+
if (!txn->mt_u.dirty_list[0].mid && !(txn->mt_flags & MDB_TXN_DIRTY))
goto done;
@@ -2515,13 +2532,7 @@ sync:
done:
env->me_pglast = 0;
env->me_txn = NULL;
- /* 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;
+ mdb_dbis_update(txn, 1);
UNLOCK_MUTEX_W(env);
free(txn);
@@ -3461,7 +3472,7 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
/* silently ignore WRITEMAP when we're only getting read access */
flags &= ~MDB_WRITEMAP;
} else {
- if (!((env->me_free_pgs = mdb_midl_alloc()) &&
+ if (!((env->me_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)) &&
(env->me_dirty_list = calloc(MDB_IDL_UM_SIZE, sizeof(MDB_ID2)))))
rc = ENOMEM;
}
@@ -3538,7 +3549,7 @@ leave:
return rc;
}
-/** Destroy resources from mdb_env_open() and clear our readers */
+/** Destroy resources from mdb_env_open(), clear our readers & DBIs */
static void
mdb_env_close0(MDB_env *env, int excl)
{
@@ -3547,6 +3558,10 @@ mdb_env_close0(MDB_env *env, int excl)
if (!(env->me_flags & MDB_ENV_ACTIVE))
return;
+ /* Doing this here since me_dbxs may not exist during mdb_env_close */
+ for (i = env->me_maxdbs; --i > MAIN_DBI; )
+ free(env->me_dbxs[i].md_name.mv_data);
+
free(env->me_dbflags);
free(env->me_dbxs);
free(env->me_path);
@@ -3741,9 +3756,9 @@ mdb_env_copy(MDB_env *env, const char *path)
ptr += wres;
}
#endif
- mdb_txn_abort(txn);
leave:
+ mdb_txn_abort(txn);
if (newfd != INVALID_HANDLE_VALUE)
close(newfd);
@@ -3754,14 +3769,10 @@ 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));
@@ -4922,8 +4933,7 @@ mdb_cursor_touch(MDB_cursor *mc)
if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
MDB_cursor mc2;
MDB_xcursor mcx;
- mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI,
- mc->mc_txn->mt_dbs[MAIN_DBI].md_flags & MDB_DUPSORT ? &mcx : NULL);
+ mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx);
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
if (rc)
return rc;
@@ -5827,7 +5837,6 @@ int
mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret)
{
MDB_cursor *mc;
- MDB_xcursor *mx = NULL;
size_t size = sizeof(MDB_cursor);
if (txn == NULL || ret == NULL || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
@@ -5841,13 +5850,11 @@ mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret)
size += sizeof(MDB_xcursor);
if ((mc = malloc(size)) != NULL) {
- if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
- mx = (MDB_xcursor *)(mc + 1);
- }
- mdb_cursor_init(mc, txn, dbi, mx);
+ mdb_cursor_init(mc, txn, dbi, (MDB_xcursor *)(mc + 1));
if (txn->mt_cursors) {
mc->mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = mc;
+ mc->mc_flags |= C_UNTRACK;
}
mc->mc_flags |= C_ALLOCD;
} else {
@@ -5867,7 +5874,7 @@ mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc)
if (txn == NULL || mc == NULL || mc->mc_dbi >= txn->mt_numdbs)
return EINVAL;
- if (txn->mt_cursors)
+ if ((mc->mc_flags & C_UNTRACK) || txn->mt_cursors)
return EINVAL;
flags = mc->mc_flags;
@@ -5907,7 +5914,7 @@ mdb_cursor_close(MDB_cursor *mc)
{
if (mc != NULL) {
/* remove from txn, if tracked */
- if (mc->mc_txn->mt_cursors) {
+ if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) {
MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
while (*prev && *prev != mc) prev = &(*prev)->mc_next;
if (*prev == mc)
@@ -7224,6 +7231,7 @@ void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
ptr = env->me_dbxs[dbi].md_name.mv_data;
env->me_dbxs[dbi].md_name.mv_data = NULL;
env->me_dbxs[dbi].md_name.mv_size = 0;
+ env->me_dbflags[dbi] = 0;
free(ptr);
}
diff --git a/libraries/liblmdb/midl.c b/libraries/liblmdb/midl.c
index 8020edc0ae..57f1e049a2 100644
--- a/libraries/liblmdb/midl.c
+++ b/libraries/liblmdb/midl.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
#include "midl.h"
@@ -117,16 +118,18 @@ int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
}
#endif
-MDB_IDL mdb_midl_alloc(void)
+MDB_IDL mdb_midl_alloc(int num)
{
- MDB_IDL ids = malloc((MDB_IDL_UM_MAX+1) * sizeof(MDB_ID));
- *ids++ = MDB_IDL_UM_MAX;
+ MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
+ if (ids)
+ *ids++ = num;
return ids;
}
void mdb_midl_free(MDB_IDL ids)
{
- free(ids-1);
+ if (ids)
+ free(ids-1);
}
int mdb_midl_shrink( MDB_IDL *idp )
@@ -141,19 +144,26 @@ int mdb_midl_shrink( MDB_IDL *idp )
return 0;
}
+int mdb_midl_grow( MDB_IDL *idp, int num )
+{
+ MDB_IDL idn = *idp-1;
+ /* grow it */
+ idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID));
+ if (!idn)
+ return ENOMEM;
+ *idn++ += num;
+ *idp = idn;
+ return 0;
+}
+
int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
{
MDB_IDL ids = *idp;
/* Too big? */
if (ids[0] >= ids[-1]) {
- MDB_IDL idn = ids-1;
- /* grow it */
- idn = realloc(idn, (*idn + MDB_IDL_UM_MAX + 1) * sizeof(MDB_ID));
- if (!idn)
- return -1;
- *idn++ += MDB_IDL_UM_MAX;
- ids = idn;
- *idp = ids;
+ if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
+ return ENOMEM;
+ ids = *idp;
}
ids[0]++;
ids[ids[0]] = id;
@@ -165,14 +175,9 @@ int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
MDB_IDL ids = *idp;
/* Too big? */
if (ids[0] + app[0] >= ids[-1]) {
- MDB_IDL idn = ids-1;
- /* grow it */
- idn = realloc(idn, (*idn + app[-1]) * sizeof(MDB_ID));
- if (!idn)
- return -1;
- *idn++ += app[-1];
- ids = idn;
- *idp = ids;
+ if (mdb_midl_grow(idp, app[0]))
+ return ENOMEM;
+ ids = *idp;
}
memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
ids[0] += app[0];
diff --git a/libraries/liblmdb/midl.h b/libraries/liblmdb/midl.h
index a284223ab3..9319fb590b 100644
--- a/libraries/liblmdb/midl.h
+++ b/libraries/liblmdb/midl.h
@@ -115,10 +115,10 @@ int mdb_midl_insert( MDB_IDL ids, MDB_ID id );
#endif
/** Allocate an IDL.
- * Allocates memory for an IDL of a default size.
+ * Allocates memory for an IDL of the given size.
* @return IDL on success, NULL on failure.
*/
-MDB_IDL mdb_midl_alloc(void);
+MDB_IDL mdb_midl_alloc(int num);
/** Free an IDL.
* @param[in] ids The IDL to free.
@@ -132,6 +132,14 @@ void mdb_midl_free(MDB_IDL ids);
*/
int mdb_midl_shrink(MDB_IDL *idp);
+ /** Grow an IDL.
+ * Add room for num additional elements.
+ * @param[in,out] idp Address of the IDL to grow.
+ * @param[i] num Number of elements to add.
+ * @return 0 on success, -1 on failure.
+ */
+int mdb_midl_grow(MDB_IDL *idp, int num);
+
/** Append an ID onto an IDL.
* @param[in,out] idp Address of the IDL to append to.
* @param[in] id The ID to append.