Merge remote-tracking branch 'origin/mdb.master'

This commit is contained in:
Howard Chu 2013-05-10 13:04:44 -07:00
commit 4a6adbf589
4 changed files with 163 additions and 120 deletions

View file

@ -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.
* <li>#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.
* <li>#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

View file

@ -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; i<txn->mt_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 */
}
/** 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; j<mc->mc_snum; j++) {
mc->mc_pg[j] = m2->mc_pg[j];
mc->mc_ki[j] = m2->mc_ki[j];
@ -1740,22 +1742,26 @@ 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; i<txn->mt_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; j<mc->mc_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];
}
@ -1763,7 +1769,7 @@ mdb_cursor_merge(MDB_txn *txn)
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; i<txn->mt_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; i<txn->mt_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; i<txn->mt_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; i<txn->mt_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);
}

View file

@ -18,6 +18,7 @@
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <assert.h>
#include "midl.h"
@ -117,15 +118,17 @@ 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)
{
if (ids)
free(ids-1);
}
@ -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];

View file

@ -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.