diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h index 280b43795c4793c1526f42d926fb58d98c8ff835..954ffde1cb2715a3288617331066f3a86be47807 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. * <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 diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c index 09fc3e29e8c9a85cb4850f5c172e800d14838904..80336831e14389c6bb596b0b1acc03e64871110d 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; 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 */ + 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; j<mc->mc_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; 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]; } } 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); } diff --git a/libraries/liblmdb/midl.c b/libraries/liblmdb/midl.c index 8020edc0ae91639980142f1683bf1582874953d8..57f1e049a27f6d86c1fe71b2fceea600818096bc 100644 --- a/libraries/liblmdb/midl.c +++ b/libraries/liblmdb/midl.c @@ -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,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 a284223ab3a2c12e560159561d95b6fc481dc302..9319fb590bf32fef302b700172b2fdedda696d66 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.