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

This commit is contained in:
Howard Chu 2013-04-05 03:04:31 -07:00
commit 55d91653fd
3 changed files with 150 additions and 94 deletions

View File

@ -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);
* <li>EAGAIN - the environment was locked by another process.
* </ul>
*/
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()

View File

@ -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; i<txn->mt_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; i<txn->mt_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; i<txn->mt_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; 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);
}
}
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; i<txn->mt_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; i<txn->mt_numdbs; i++)
env->me_dbflags[i] = txn->mt_dbs[i].md_flags;
env->me_numdbs = i;
/* 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;
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; i<txn->mt_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; i<txn->mt_numdbs; i++)
env->me_dbflags[i] = txn->mt_dbs[i].md_flags;
env->me_numdbs = i;
/* 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;
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; i<NUMKEYS(mc->mc_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; j<ovpages; j++) {
mdb_midl_append(&mc->mc_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; i<mc->mc_top; i++)
mc->mc_ki[0] = 0;
for (i=1; i<mc->mc_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;

View File

@ -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<count;i++) {
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
rc = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE);
if (rc) j++;
if (rc) {
j++;
data.mv_size = sizeof(sval);
data.mv_data = sval;
}
}
if (j) printf("%d duplicates skipped\n", j);
rc = mdb_txn_commit(txn);
@ -97,25 +101,38 @@ int main(int argc,char * argv[])
(int) key.mv_size, (char *) key.mv_data,
(int) data.mv_size, (char *) data.mv_data);
}
printf("Cursor last\n");
rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST);
printf("key: %.*s, data: %.*s\n",
(int) key.mv_size, (char *) key.mv_data,
(int) data.mv_size, (char *) data.mv_data);
printf("Cursor prev\n");
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
printf("key: %.*s, data: %.*s\n",
(int) key.mv_size, (char *) key.mv_data,
(int) data.mv_size, (char *) data.mv_data);
}
#if 0
/* write ops aren't coordinated with cursors,
* this stuff all breaks
*/
printf("Cursor last/prev\n");
rc = mdb_cursor_get(cursor, &key, &data, MDB_LAST);
printf("key: %.*s, data: %.*s\n",
(int) key.mv_size, (char *) key.mv_data,
(int) data.mv_size, (char *) data.mv_data);
rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV);
printf("key: %.*s, data: %.*s\n",
(int) key.mv_size, (char *) key.mv_data,
(int) data.mv_size, (char *) data.mv_data);
mdb_txn_abort(txn);
printf("Deleting with cursor\n");
rc = mdb_txn_begin(env, NULL, 0, &txn);
rc = mdb_cursor_open(db, txn, &cur2);
rc = mdb_cursor_open(txn, dbi, &cur2);
for (i=0; i<50; i++) {
rc = mdb_cursor_get(cur2, &key, &data, MDB_NEXT);
printf("key: %p %.*s, data: %p %.*s\n",
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
rc = mdb_del(db, txn, &key, NULL);
rc = mdb_del(txn, dbi, &key, NULL);
}
printf("Restarting cursor in txn\n");
@ -131,9 +148,10 @@ int main(int argc,char * argv[])
}
mdb_cursor_close(cur2);
rc = mdb_txn_commit(txn);
mdb_cursor_close(cursor);
printf("Restarting cursor outside txn\n");
rc = mdb_cursor_open(db, NULL, &cursor);
rc = mdb_txn_begin(env, NULL, 0, &txn);
rc = mdb_cursor_open(txn, dbi, &cursor);
rc = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
printf("key: %p %.*s, data: %p %.*s\n",
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
@ -144,7 +162,6 @@ int main(int argc,char * argv[])
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
}
#endif
mdb_cursor_close(cursor);
mdb_close(env, dbi);