/** @defgroup internal MDB Internals
* @{
*/
-/** @defgroup compat Windows Compatibility Macros
+/** @defgroup compat Compatibility Macros
* A bunch of macros to minimize the amount of platform-specific ifdefs
* needed throughout the rest of the code. When the features this library
* needs are similar enough to POSIX to be hidden in a one-or-two line
* replacement, this macro approach is used.
* @{
*/
+
+ /** Wrapper around __func__, which is a C99 feature */
+#if __STDC_VERSION__ >= 199901L
+# define mdb_func_ __func__
+#elif __GNUC__ >= 2 || _MSC_VER >= 1300
+# define mdb_func_ __FUNCTION__
+#else
+/* If a debug message says <mdb_unknown>(), update the #if statements above */
+# define mdb_func_ "<mdb_unknown>"
+#endif
+
#ifdef _WIN32
#define MDB_USE_HASH 1
#define MDB_PIDLOCK 0
*/
# define DPRINTF(args) ((void) ((mdb_debug) && DPRINTF0 args))
# define DPRINTF0(fmt, ...) \
- fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, __VA_ARGS__)
+ fprintf(stderr, "%s:%d " fmt "\n", mdb_func_, __LINE__, __VA_ARGS__)
#else
# define DPRINTF(args) ((void) 0)
#endif
sem_t *me_rmutex; /* Shared mutexes are not supported */
sem_t *me_wmutex;
#endif
+ void *me_userctx; /**< User-settable context */
+ MDB_assert_func *me_assert_func; /**< Callback for assertion failures */
};
/** Nested transaction */
return strerror(err);
}
+/** assert(3) variant in cursor context */
+#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr)
+/** assert(3) variant in transaction context */
+#define mdb_tassert(mc, expr) mdb_assert0((txn)->mt_env, expr, #expr)
+/** assert(3) variant in environment context */
+#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr)
+
+#ifndef NDEBUG
+# define mdb_assert0(env, expr, expr_txt) ((expr) ? (void)0 : \
+ mdb_assert_fail(env, expr_txt, mdb_func_, __FILE__, __LINE__))
+
+static void
+mdb_assert_fail(MDB_env *env, const char *expr_txt,
+ const char *func, const char *file, int line)
+{
+ char buf[400];
+ sprintf(buf, "%.100s:%d: Assertion '%.200s' failed in %.40s()",
+ file, line, expr_txt, func);
+ if (env->me_assert_func)
+ env->me_assert_func(env, buf);
+ fprintf(stderr, "%s\n", buf);
+ abort();
+}
+#else
+# define mdb_assert0(env, expr, expr_txt) ((void) 0)
+#endif /* NDEBUG */
+
#if MDB_DEBUG
/** Return the page number of \b mp which may be sub-page, for debug output */
static pgno_t
off = sz - psize;
}
if ((ret = malloc(sz)) != NULL) {
+ VGMEMP_ALLOC(env, ret, sz);
if (!(env->me_flags & MDB_NOMEMINIT)) {
memset((char *)ret + off, 0, psize);
ret->mp_pad = 0;
}
- VGMEMP_ALLOC(env, ret, sz);
+ } else {
+ txn->mt_flags |= MDB_TXN_ERROR;
}
return ret;
}
*mp = NULL;
/* If our dirty list is already full, we can't do anything */
- if (txn->mt_dirty_room == 0)
- return MDB_TXN_FULL;
+ if (txn->mt_dirty_room == 0) {
+ rc = MDB_TXN_FULL;
+ goto fail;
+ }
for (op = MDB_FIRST;; op = MDB_NEXT) {
MDB_val key, data;
if (rc) {
if (rc == MDB_NOTFOUND)
break;
- return rc;
+ goto fail;
}
last = *(txnid_t*)key.mv_data;
if (oldest <= last)
idl = (MDB_ID *) data.mv_data;
i = idl[0];
if (!mop) {
- if (!(env->me_pghead = mop = mdb_midl_alloc(i)))
- return ENOMEM;
+ if (!(env->me_pghead = mop = mdb_midl_alloc(i))) {
+ rc = ENOMEM;
+ goto fail;
+ }
} else {
if ((rc = mdb_midl_need(&env->me_pghead, i)) != 0)
- return rc;
+ goto fail;
mop = env->me_pghead;
}
env->me_pglast = last;
pgno = txn->mt_next_pgno;
if (pgno + num >= env->me_maxpg) {
DPUTS("DB size maxed out");
- return MDB_MAP_FULL;
+ rc = MDB_MAP_FULL;
+ goto fail;
}
search_done:
if (env->me_flags & MDB_WRITEMAP) {
np = (MDB_page *)(env->me_map + env->me_psize * pgno);
} else {
- if (!(np = mdb_page_malloc(txn, num)))
- return ENOMEM;
+ if (!(np = mdb_page_malloc(txn, num))) {
+ rc = ENOMEM;
+ goto fail;
+ }
}
if (i) {
mop[0] = mop_len -= num;
*mp = np;
return MDB_SUCCESS;
+
+fail:
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
}
/** Copy the used portions of a non-overflow page.
np = NULL;
rc = mdb_page_unspill(txn, mp, &np);
if (rc)
- return rc;
+ goto fail;
if (np)
goto done;
}
if ((rc = mdb_midl_need(&txn->mt_free_pgs, 1)) ||
(rc = mdb_page_alloc(mc, 1, &np)))
- return rc;
+ goto fail;
pgno = np->mp_pgno;
DPRINTF(("touched db %d page %"Z"u -> %"Z"u", DDBI(mc),
mp->mp_pgno, pgno));
- assert(mp->mp_pgno != pgno);
+ mdb_cassert(mc, mp->mp_pgno != pgno);
mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
/* Update the parent page, if any, to point to the new page */
if (mc->mc_top) {
if (x <= dl[0].mid && dl[x].mid == pgno) {
if (mp != dl[x].mptr) { /* bad cursor? */
mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
+ txn->mt_flags |= MDB_TXN_ERROR;
return MDB_CORRUPTED;
}
return 0;
}
}
- assert(dl[0].mid < MDB_IDL_UM_MAX);
+ mdb_cassert(mc, dl[0].mid < MDB_IDL_UM_MAX);
/* No - copy it */
np = mdb_page_malloc(txn, 1);
if (!np)
}
}
return 0;
+
+fail:
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
}
int
return rc;
pglast = head_id = *(txnid_t *)key.mv_data;
total_room = head_room = 0;
- assert(pglast <= env->me_pglast);
+ mdb_tassert(txn, pglast <= env->me_pglast);
rc = mdb_cursor_del(&mc, 0);
if (rc)
return rc;
ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1;
MDB_ID save;
- assert(len >= 0 && id <= env->me_pglast);
+ mdb_tassert(txn, len >= 0 && id <= env->me_pglast);
key.mv_data = &id;
if (len > mop_len) {
len = mop_len;
unsigned int i;
MDB_env *env;
- assert(txn != NULL);
- assert(txn->mt_env != NULL);
+ if (txn == NULL || txn->mt_env == NULL)
+ return EINVAL;
if (txn->mt_child) {
rc = mdb_txn_commit(txn->mt_child);
if (yp == dst[x].mid)
free(dst[x--].mptr);
}
- assert(i == x);
+ mdb_tassert(txn, i == x);
dst[0].mid = len;
free(txn->mt_u.dirty_list);
parent->mt_dirty_room = txn->mt_dirty_room;
int r2;
#endif
- assert(txn != NULL);
- assert(txn->mt_env != NULL);
-
toggle = txn->mt_txnid & 1;
DPRINTF(("writing meta page %d for root page %"Z"u",
toggle, txn->mt_dbs[MAIN_DBI].md_root));
DDBI(mc), (void *) mc));
if (mc->mc_snum >= CURSOR_STACK) {
- assert(mc->mc_snum < CURSOR_STACK);
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
return MDB_CURSOR_FULL;
}
p = (MDB_page *)(env->me_map + env->me_psize * pgno);
} else {
DPRINTF(("page %"Z"u not found", pgno));
- assert(p != NULL);
+ txn->mt_flags |= MDB_TXN_ERROR;
return MDB_PAGE_NOTFOUND;
}
indx_t i;
DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp)));
- assert(NUMKEYS(mp) > 1);
+ mdb_cassert(mc, NUMKEYS(mp) > 1);
DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0))));
if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) {
else {
i = mc->mc_ki[mc->mc_top];
if (!exact) {
- assert(i > 0);
+ mdb_cassert(mc, i > 0);
i--;
}
}
DPRINTF(("following index %u for key [%s]", i, DKEY(key)));
}
- assert(i < NUMKEYS(mp));
+ mdb_cassert(mc, i < NUMKEYS(mp));
node = NODEPTR(mp, i);
if ((rc = mdb_page_get(mc->mc_txn, NODEPGNO(node), &mp, NULL)) != 0)
if (!IS_LEAF(mp)) {
DPRINTF(("internal error, index points to a %02X page!?",
mp->mp_flags));
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
return MDB_CORRUPTED;
}
}
}
- assert(root > 1);
+ mdb_cassert(mc, root > 1);
if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root)
if ((rc = mdb_page_get(mc->mc_txn, root, &mc->mc_pg[0], NULL)) != 0)
return rc;
iy = dl[x];
dl[x] = ix;
} else {
- assert(x > 1);
+ mdb_cassert(mc, x > 1);
j = ++(dl[0].mid);
dl[j] = ix; /* Unsorted. OK when MDB_TXN_ERROR. */
txn->mt_flags |= MDB_TXN_ERROR;
int exact = 0;
DKBUF;
- assert(key);
- assert(data);
+ if (key == NULL || data == NULL)
+ return EINVAL;
+
DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key)));
if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
DPRINTF(("just moving to %s index key %u",
move_right ? "right" : "left", mc->mc_ki[mc->mc_top]));
}
- assert(IS_BRANCH(mc->mc_pg[mc->mc_top]));
+ mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top]));
indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
if ((rc = mdb_page_get(mc->mc_txn, NODEPGNO(indx), &mp, NULL)) != 0) {
return MDB_NOTFOUND;
}
- assert(mc->mc_flags & C_INITIALIZED);
+ mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
mp = mc->mc_pg[mc->mc_top];
return MDB_SUCCESS;
}
- assert(IS_LEAF(mp));
+ mdb_cassert(mc, IS_LEAF(mp));
leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
MDB_node *leaf;
int rc;
- assert(mc->mc_flags & C_INITIALIZED);
+ mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
mp = mc->mc_pg[mc->mc_top];
return MDB_SUCCESS;
}
- assert(IS_LEAF(mp));
+ mdb_cassert(mc, IS_LEAF(mp));
leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
DKBUF;
assert(mc);
- assert(key);
+ mdb_cassert(mc, key);
if (key->mv_size == 0)
return MDB_BAD_VALSIZE;
return rc;
mp = mc->mc_pg[mc->mc_top];
- assert(IS_LEAF(mp));
+ mdb_cassert(mc, IS_LEAF(mp));
set2:
leaf = mdb_node_search(mc, key, exactp);
if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS)
return rc; /* no entries matched */
mp = mc->mc_pg[mc->mc_top];
- assert(IS_LEAF(mp));
+ mdb_cassert(mc, IS_LEAF(mp));
leaf = NODEPTR(mp, 0);
}
if (rc != MDB_SUCCESS)
return rc;
}
- assert(IS_LEAF(mc->mc_pg[mc->mc_top]));
+ mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top]));
leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0);
mc->mc_flags |= C_INITIALIZED;
if (rc != MDB_SUCCESS)
return rc;
}
- assert(IS_LEAF(mc->mc_pg[mc->mc_top]));
+ mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top]));
}
mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1;
int exact = 0;
int (*mfunc)(MDB_cursor *mc, MDB_val *key, MDB_val *data);
- assert(mc);
+ if (mc == NULL)
+ return EINVAL;
if (mc->mc_txn->mt_flags & MDB_TXN_ERROR)
return MDB_BAD_TXN;
MDB_page *ofp = NULL; /* overflow page */
DKBUF;
- assert(mp->mp_upper >= mp->mp_lower);
+ mdb_cassert(mc, mp->mp_upper >= mp->mp_lower);
DPRINTF(("add to %s %spage %"Z"u index %i, data size %"Z"u key size %"Z"u [%s]",
IS_LEAF(mp) ? "leaf" : "branch",
if (key != NULL)
node_size += key->mv_size;
if (IS_LEAF(mp)) {
- assert(data);
+ mdb_cassert(mc, data);
if (F_ISSET(flags, F_BIGDATA)) {
/* Data already on overflow page. */
node_size += sizeof(pgno_t);
/* Adjust free space offsets. */
ofs = mp->mp_upper - node_size;
- assert(ofs >= mp->mp_lower + sizeof(indx_t));
+ mdb_cassert(mc, ofs >= mp->mp_lower + sizeof(indx_t));
mp->mp_ptrs[indx] = ofs;
mp->mp_upper = ofs;
mp->mp_lower += sizeof(indx_t);
memcpy(NODEKEY(node), key->mv_data, key->mv_size);
if (IS_LEAF(mp)) {
- assert(key);
+ mdb_cassert(mc, key);
if (ofp == NULL) {
if (F_ISSET(flags, F_BIGDATA))
memcpy(node->mn_data + key->mv_size, data->mv_data,
mdb_dbg_pgno(mp), NUMKEYS(mp)));
DPRINTF(("upper-lower = %u - %u = %"Z"d", mp->mp_upper,mp->mp_lower,room));
DPRINTF(("node size = %"Z"u", node_size));
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
return MDB_PAGE_FULL;
}
mc->mc_pg[0] = 0;
mc->mc_flags = 0;
if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
- assert(mx != NULL);
+ mdb_tassert(txn, mx != NULL);
mc->mc_xcursor = mx;
mdb_xcursor_init0(mc);
} else {
flags = 0;
} else {
srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top]);
- assert(!((size_t)srcnode&1));
+ mdb_cassert(csrc, !((size_t)srcnode & 1));
srcpg = NODEPGNO(srcnode);
flags = srcnode->mn_flags;
if (csrc->mc_ki[csrc->mc_top] == 0 && IS_BRANCH(csrc->mc_pg[csrc->mc_top])) {
csrc->mc_ki[csrc->mc_top] = 0;
rc = mdb_update_key(csrc, &nullkey);
csrc->mc_ki[csrc->mc_top] = ix;
- assert(rc == MDB_SUCCESS);
+ mdb_cassert(csrc, rc == MDB_SUCCESS);
}
}
cdst->mc_ki[cdst->mc_top] = 0;
rc = mdb_update_key(cdst, &nullkey);
cdst->mc_ki[cdst->mc_top] = ix;
- assert(rc == MDB_SUCCESS);
+ mdb_cassert(csrc, rc == MDB_SUCCESS);
}
}
DPRINTF(("merging page %"Z"u into %"Z"u", csrc->mc_pg[csrc->mc_top]->mp_pgno,
cdst->mc_pg[cdst->mc_top]->mp_pgno));
- assert(csrc->mc_snum > 1); /* can't merge root page */
- assert(cdst->mc_snum > 1);
+ mdb_cassert(csrc, csrc->mc_snum > 1); /* can't merge root page */
+ mdb_cassert(csrc, cdst->mc_snum > 1);
/* Mark dst as dirty. */
if ((rc = mdb_page_touch(cdst)))
* otherwise the tree is invalid.
*/
ptop = mc->mc_top-1;
- assert(NUMKEYS(mc->mc_pg[ptop]) > 1);
+ mdb_cassert(mc, NUMKEYS(mc->mc_pg[ptop]) > 1);
/* Leaf page fill factor is below the threshold.
* Try to move keys from left or right neighbor, or
if (rc != MDB_SUCCESS)
mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
else {
- MDB_cursor *m2;
+ MDB_cursor *m2, *m3;
MDB_dbi dbi = mc->mc_dbi;
mp = mc->mc_pg[mc->mc_top];
/* Adjust other cursors pointing to mp */
for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
- if (m2 == mc || m2->mc_snum < mc->mc_snum)
+ m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2;
+ if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED))
continue;
- if (!(m2->mc_flags & C_INITIALIZED))
+ if (m3 == mc || m3->mc_snum < mc->mc_snum)
continue;
- if (m2->mc_pg[mc->mc_top] == mp) {
- if (m2->mc_ki[mc->mc_top] >= ki) {
- m2->mc_flags |= C_DEL;
- if (m2->mc_ki[mc->mc_top] > ki)
- m2->mc_ki[mc->mc_top]--;
+ if (m3->mc_pg[mc->mc_top] == mp) {
+ if (m3->mc_ki[mc->mc_top] >= ki) {
+ m3->mc_flags |= C_DEL;
+ if (m3->mc_ki[mc->mc_top] > ki)
+ m3->mc_ki[mc->mc_top]--;
}
- if (m2->mc_ki[mc->mc_top] >= nkeys)
- mdb_cursor_sibling(m2, 1);
+ if (m3->mc_ki[mc->mc_top] >= nkeys)
+ mdb_cursor_sibling(m3, 1);
}
}
mc->mc_flags |= C_DEL;
int rc, exact;
DKBUF;
- assert(key != NULL);
+ if (key == NULL)
+ return EINVAL;
DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key)));
MDB_cursor mc;
MDB_xcursor mx;
- assert(key != NULL);
- assert(data != NULL);
+ if (key == NULL || data == NULL)
+ return EINVAL;
if (txn == NULL || !dbi || dbi >= txn->mt_numdbs || !(txn->mt_dbflags[dbi] & DB_VALID))
return EINVAL;
return MDB_SUCCESS;
}
+int
+mdb_env_set_userctx(MDB_env *env, void *ctx)
+{
+ if (!env)
+ return EINVAL;
+ env->me_userctx = ctx;
+ return MDB_SUCCESS;
+}
+
+void *
+mdb_env_get_userctx(MDB_env *env)
+{
+ return env ? env->me_userctx : NULL;
+}
+
+int
+mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
+{
+ if (!env)
+ return EINVAL;
+#ifndef NDEBUG
+ env->me_assert_func = func;
+#endif
+ return MDB_SUCCESS;
+}
+
int
mdb_env_get_path(MDB_env *env, const char **arg)
{
rc = mdb_page_get(txn, pg, &omp, NULL);
if (rc != 0)
return rc;
- assert(IS_OVERFLOW(omp));
+ mdb_cassert(mc, IS_OVERFLOW(omp));
rc = mdb_midl_append_range(&txn->mt_free_pgs,
pg, omp->mp_pages);
if (rc)
mr = env->me_txns->mti_readers;
for (i=0; i<rdrs; i++) {
if (mr[i].mr_pid) {
- size_t tid;
- tid = mr[i].mr_tid;
- if (mr[i].mr_txnid == (txnid_t)-1) {
- sprintf(buf, "%10d %"Z"x -\n", mr[i].mr_pid, tid);
- } else {
- sprintf(buf, "%10d %"Z"x %"Z"u\n", mr[i].mr_pid, tid, mr[i].mr_txnid);
- }
+ txnid_t txnid = mr[i].mr_txnid;
+ sprintf(buf, txnid == (txnid_t)-1 ?
+ "%10d %"Z"x -\n" : "%10d %"Z"x %"Z"u\n",
+ (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid);
if (first) {
first = 0;
rc = func(" pid thread txnid\n", ctx);