MDB_ppage mc_stack[CURSOR_STACK]; /* stack of parent pages */
unsigned int mc_snum; /* number of pushed pages */
MDB_dbi mc_dbi;
- short mc_initialized; /* 1 if initialized */
- short mc_eof; /* 1 if end is reached */
+ unsigned int mc_flags;
+#define C_INITIALIZED 0x01
+#define C_EOF 0x02
+#define C_XDIRTY 0x04
struct MDB_xcursor *mc_xcursor;
};
static void mdb_xcursor_init0(MDB_cursor *mc);
static void mdb_xcursor_init1(MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx,
MDB_page *mp, MDB_node *node);
-static void mdb_xcursor_fini(MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx);
+static void mdb_xcursor_fini(MDB_cursor *mc);
static size_t mdb_leaf_size(MDB_env *env, MDB_val *key,
MDB_val *data);
if (!txn->mt_env->me_pghead && dbi != FREE_DBI &&
txn->mt_dbs[FREE_DBI].md_root != P_INVALID) {
/* See if there's anything in the free DB */
+ MDB_cursor mc;
MDB_pageparent mpp;
MDB_node *leaf;
ULONG *kptr, oldest;
mpp.mp_parent = NULL;
mpp.mp_pi = 0;
- mdb_search_page(txn, FREE_DBI, NULL, NULL, 0, &mpp);
+ mc.mc_txn = txn;
+ mc.mc_dbi = dbi;
+ mc.mc_snum = 0;
+ mc.mc_flags = 0;
+ mdb_search_page(txn, FREE_DBI, NULL, &mc, 0, &mpp);
leaf = NODEPTR(mpp.mp_page, 0);
kptr = (ULONG *)NODEKEY(leaf);
/* It's usable, grab it.
*/
MDB_oldpages *mop;
+ MDB_ppage *top;
MDB_val data;
pgno_t *idl;
}
#endif
/* drop this IDL from the DB */
- mpp.mp_parent = NULL;
- mpp.mp_pi = 0;
- mdb_search_page(txn, FREE_DBI, NULL, NULL, 1, &mpp);
- leaf = NODEPTR(mpp.mp_page, 0);
- mdb_del0(txn, FREE_DBI, 0, &mpp, leaf);
+ top = CURSOR_TOP(&mc);
+ top->mp_ki = 0;
+ mc.mc_flags = C_INITIALIZED;
+ mdb_cursor_del(&mc, 0);
}
}
if (txn->mt_env->me_pghead) {
mc.mc_txn = txn;
mc.mc_dbi = dbi;
- mc.mc_initialized = 0;
+ mc.mc_flags = 0;
if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
mc.mc_xcursor = &mx;
mdb_xcursor_init0(&mc);
MDB_node *leaf;
int rc;
- if (cursor->mc_eof) {
+ if (cursor->mc_flags & C_EOF) {
return MDB_NOTFOUND;
}
- assert(cursor->mc_initialized);
+ assert(cursor->mc_flags & C_INITIALIZED);
top = CURSOR_TOP(cursor);
mp = top->mp_page;
return rc;
}
} else {
- cursor->mc_xcursor->mx_cursor.mc_initialized = 0;
+ cursor->mc_xcursor->mx_cursor.mc_flags = 0;
if (op == MDB_NEXT_DUP)
return MDB_NOTFOUND;
}
if (top->mp_ki + 1 >= NUMKEYS(mp)) {
DPUTS("=====> move to next sibling page");
if (mdb_sibling(cursor, 1) != MDB_SUCCESS) {
- cursor->mc_eof = 1;
+ cursor->mc_flags |= C_EOF;
return MDB_NOTFOUND;
}
top = CURSOR_TOP(cursor);
MDB_node *leaf;
int rc;
- assert(cursor->mc_initialized);
+ assert(cursor->mc_flags & C_INITIALIZED);
top = CURSOR_TOP(cursor);
mp = top->mp_page;
if (op != MDB_PREV || rc == MDB_SUCCESS)
return rc;
} else {
- cursor->mc_xcursor->mx_cursor.mc_initialized = 0;
+ cursor->mc_xcursor->mx_cursor.mc_flags = 0;
if (op == MDB_PREV_DUP)
return MDB_NOTFOUND;
}
if (top->mp_ki == 0) {
DPUTS("=====> move to prev sibling page");
if (mdb_sibling(cursor, 0) != MDB_SUCCESS) {
- cursor->mc_initialized = 0;
+ cursor->mc_flags &= ~C_INITIALIZED;
return MDB_NOTFOUND;
}
top = CURSOR_TOP(cursor);
} else
top->mp_ki--;
- cursor->mc_eof = 0;
+ cursor->mc_flags &= ~C_EOF;
DPRINTF("==> cursor points to page %lu with %u keys, key index %u",
mp->mp_pgno, NUMKEYS(mp), top->mp_ki);
assert(key->mv_size > 0);
/* See if we're already on the right page */
- if (cursor->mc_initialized) {
+ if (cursor->mc_flags & C_INITIALIZED) {
MDB_val nodekey;
top = CURSOR_TOP(cursor);
/* Don't try this for LEAF2 pages. Maybe support that later. */
leaf = NODEPTR(mpp.mp_page, 0);
}
- cursor->mc_initialized = 1;
- cursor->mc_eof = 0;
+ cursor->mc_flags |= C_INITIALIZED;
+ cursor->mc_flags &= ~C_EOF;
if (IS_LEAF2(mpp.mp_page)) {
key->mv_size = cursor->mc_txn->mt_dbs[cursor->mc_dbi].md_pad;
assert(IS_LEAF(mpp.mp_page));
leaf = NODEPTR(mpp.mp_page, 0);
- cursor->mc_initialized = 1;
- cursor->mc_eof = 0;
+ cursor->mc_flags |= C_INITIALIZED;
+ cursor->mc_flags &= ~C_EOF;
if (IS_LEAF2(mpp.mp_page)) {
key->mv_size = cursor->mc_txn->mt_dbs[cursor->mc_dbi].md_pad;
return rc;
} else {
if (cursor->mc_xcursor)
- cursor->mc_xcursor->mx_cursor.mc_initialized = 0;
+ cursor->mc_xcursor->mx_cursor.mc_flags = 0;
if ((rc = mdb_read_data(cursor->mc_txn, leaf, data)) != MDB_SUCCESS)
return rc;
}
assert(IS_LEAF(mpp.mp_page));
leaf = NODEPTR(mpp.mp_page, NUMKEYS(mpp.mp_page)-1);
- cursor->mc_initialized = 1;
- cursor->mc_eof = 0;
+ cursor->mc_flags |= C_INITIALIZED;
+ cursor->mc_flags &= ~C_EOF;
top = CURSOR_TOP(cursor);
top->mp_ki = NUMKEYS(top->mp_page) - 1;
case MDB_GET_MULTIPLE:
if (data == NULL ||
!(cursor->mc_txn->mt_dbs[cursor->mc_dbi].md_flags & MDB_DUPFIXED) ||
- !cursor->mc_initialized) {
+ !(cursor->mc_flags & C_INITIALIZED)) {
rc = EINVAL;
break;
}
rc = MDB_SUCCESS;
- if (!cursor->mc_xcursor->mx_cursor.mc_initialized || cursor->mc_xcursor->mx_cursor.mc_eof)
+ if (!(cursor->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) ||
+ (cursor->mc_xcursor->mx_cursor.mc_flags & C_EOF))
break;
goto fetchm;
case MDB_NEXT_MULTIPLE:
rc = EINVAL;
break;
}
- if (!cursor->mc_initialized)
+ if (!(cursor->mc_flags & C_INITIALIZED))
rc = mdb_cursor_first(cursor, key, data);
else
rc = mdb_cursor_next(cursor, key, data, MDB_NEXT_DUP);
if (rc == MDB_SUCCESS) {
- if (cursor->mc_xcursor->mx_cursor.mc_initialized) {
+ if (cursor->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
MDB_ppage *top;
fetchm:
top = CURSOR_TOP(&cursor->mc_xcursor->mx_cursor);
case MDB_NEXT:
case MDB_NEXT_DUP:
case MDB_NEXT_NODUP:
- if (!cursor->mc_initialized)
+ if (!(cursor->mc_flags & C_INITIALIZED))
rc = mdb_cursor_first(cursor, key, data);
else
rc = mdb_cursor_next(cursor, key, data, op);
case MDB_PREV:
case MDB_PREV_DUP:
case MDB_PREV_NODUP:
- if (!cursor->mc_initialized || cursor->mc_eof)
+ if (!(cursor->mc_flags & C_INITIALIZED) || (cursor->mc_flags & C_EOF))
rc = mdb_cursor_last(cursor, key, data);
else
rc = mdb_cursor_prev(cursor, key, data, op);
case MDB_FIRST_DUP:
if (data == NULL ||
!(cursor->mc_txn->mt_dbs[cursor->mc_dbi].md_flags & MDB_DUPSORT) ||
- !cursor->mc_initialized ||
- !cursor->mc_xcursor->mx_cursor.mc_initialized) {
+ !(cursor->mc_flags & C_INITIALIZED) ||
+ !(cursor->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
rc = EINVAL;
break;
}
case MDB_LAST_DUP:
if (data == NULL ||
!(cursor->mc_txn->mt_dbs[cursor->mc_dbi].md_flags & MDB_DUPSORT) ||
- !cursor->mc_initialized ||
- !cursor->mc_xcursor->mx_cursor.mc_initialized) {
+ !(cursor->mc_flags & C_INITIALIZED) ||
+ !(cursor->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
rc = EINVAL;
break;
}
mpp.mp_parent = mpp.mp_page;
mpp.mp_pi = cursor->mc_stack[i].mp_ki;
}
- if (cursor->mc_xcursor)
- return mdb_cursor_touch(&cursor->mc_xcursor->mx_cursor);
return MDB_SUCCESS;
}
MDB_node *leaf;
int rc, exact;
+ if (F_ISSET(cursor->mc_txn->mt_flags, MDB_TXN_RDONLY))
+ return EACCES;
+
if (flags == MDB_NODUPDATA) {
rc = mdb_cursor_set(cursor, key, data, MDB_GET_BOTH, &exact);
if (rc == 0)
return MDB_KEYEXIST;
if (rc != MDB_NOTFOUND)
return rc;
- } else if (!cursor->mc_initialized) {
+ } else if (!(cursor->mc_flags & C_INITIALIZED)) {
return EINVAL;
}
/* Cursor is positioned, now make sure all pages are writable */
rc = mdb_cursor_touch(cursor);
if (rc) return rc;
+ if (cursor->mc_xcursor) {
+ rc = mdb_cursor_touch(&cursor->mc_xcursor->mx_cursor);
+ if (rc) return rc;
+ cursor->mc_flags |= C_XDIRTY;
+ }
top = CURSOR_TOP(cursor);
+ return rc;
+}
+
+int
+mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
+{
+ MDB_pageparent mpp;
+ MDB_ppage *top;
+ MDB_node *leaf;
+ int rc;
+
+ if (F_ISSET(mc->mc_txn->mt_flags, MDB_TXN_RDONLY))
+ return EACCES;
+
+ if (!mc->mc_flags & C_INITIALIZED)
+ return EINVAL;
+
+ rc = mdb_cursor_touch(mc);
+ if (rc) return rc;
+ if (mc->mc_xcursor) {
+ rc = mdb_cursor_touch(&mc->mc_xcursor->mx_cursor);
+ if (rc) return rc;
+ }
+
+ top = CURSOR_TOP(mc);
+ leaf = NODEPTR(top->mp_page, top->mp_ki);
+
+ if (!IS_LEAF2(top->mp_page) && F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ MDB_pageparent mp2;
+
+ if (flags != MDB_NODUPDATA) {
+ rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, 0);
+ mdb_xcursor_fini(mc);
+ /* If sub-DB still has entries, we're done */
+ if (mc->mc_xcursor->mx_txn.mt_dbs[mc->mc_xcursor->mx_cursor.mc_dbi].md_root
+ != P_INVALID) {
+ memcpy(NODEDATA(leaf),
+ &mc->mc_xcursor->mx_txn.mt_dbs[mc->mc_xcursor->mx_cursor.mc_dbi],
+ sizeof(MDB_db));
+ mc->mc_txn->mt_dbs[mc->mc_dbi].md_entries--;
+ return rc;
+ }
+ /* otherwise fall thru and delete the sub-DB */
+ } else {
+ /* add all the child DB's pages to the free list */
+ rc = mdb_search_page(&mx.mx_txn, mx.mx_cursor.mc_dbi,
+ NULL, &mx.mx_cursor, 0, &mp2);
+ if (rc == MDB_SUCCESS) {
+ MDB_ppage *top, *parent;
+ MDB_node *ni;
+ unsigned int i;
+
+ cursor_pop_page(&mx.mx_cursor);
+ if (mx.mx_cursor.mc_snum) {
+ top = CURSOR_TOP(&mx.mx_cursor);
+ while (mx.mx_cursor.mc_snum > 1) {
+ parent = CURSOR_PARENT(&mx.mx_cursor);
+ for (i=0; i<NUMKEYS(top->mp_page); i++) {
+ ni = NODEPTR(top->mp_page, i);
+ mdb_midl_insert(txn->mt_free_pgs, NODEPGNO(ni));
+ }
+ parent->mp_ki++;
+ if (parent->mp_ki >= NUMKEYS(parent->mp_page)) {
+ cursor_pop_page(&mx.mx_cursor);
+ top = parent;
+ } else {
+ ni = NODEPTR(parent->mp_page, parent->mp_ki);
+ rc = mdb_get_page(&mx.mx_txn, NODEPGNO(ni), &top->mp_page);
+ }
+ }
+ }
+ mdb_midl_insert(txn->mt_free_pgs, mx.mx_txn.mt_dbs[mx.mx_cursor.mc_dbi].md_root);
+ }
+ }
+ }
+
+ return mdb_del0(txn, dbi, ki, &mpp, leaf);
+ return rc;
}
/* Allocate a page and initialize it
mx->mx_dbxs[dbn].md_name.mv_size = node->mn_ksize;
mx->mx_txn.mt_next_pgno = txn->mt_next_pgno;
mx->mx_txn.mt_u = txn->mt_u;
- mx->mx_cursor.mc_initialized = 0;
- mx->mx_cursor.mc_eof = 0;
+ mx->mx_cursor.mc_flags = 0;
}
static void
-mdb_xcursor_fini(MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx)
+mdb_xcursor_fini(MDB_cursor *mc)
{
- txn->mt_next_pgno = mx->mx_txn.mt_next_pgno;
- txn->mt_u = mx->mx_txn.mt_u;
- txn->mt_dbs[0] = mx->mx_dbs[0];
- txn->mt_dbs[1] = mx->mx_dbs[1];
- txn->mt_dbxs[0].md_dirty = mx->mx_dbxs[0].md_dirty;
- txn->mt_dbxs[1].md_dirty = mx->mx_dbxs[1].md_dirty;
- if (dbi > 1) {
- txn->mt_dbs[dbi] = mx->mx_dbs[2];
- txn->mt_dbxs[dbi].md_dirty = mx->mx_dbxs[2].md_dirty;
+ MDB_xcursor *mx = mc->mc_xcursor;
+ mc->mc_txn->mt_next_pgno = mx->mx_txn.mt_next_pgno;
+ mc->mc_txn->mt_u = mx->mx_txn.mt_u;
+ mc->mc_txn->mt_dbs[0] = mx->mx_dbs[0];
+ mc->mc_txn->mt_dbs[1] = mx->mx_dbs[1];
+ mc->mc_txn->mt_dbxs[0].md_dirty = mx->mx_dbxs[0].md_dirty;
+ mc->mc_txn->mt_dbxs[1].md_dirty = mx->mx_dbxs[1].md_dirty;
+ if (mc->mc_dbi > 1) {
+ mc->mc_txn->mt_dbs[mc->mc_dbi] = mx->mx_dbs[2];
+ mc->mc_txn->mt_dbxs[mc->mc_dbi].md_dirty = mx->mx_dbxs[2].md_dirty;
}
}
if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
*countp = 1;
} else {
- if (!mc->mc_xcursor->mx_cursor.mc_initialized)
+ if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
return EINVAL;
*countp = mc->mc_xcursor->mx_txn.mt_dbs[mc->mc_xcursor->mx_cursor.mc_dbi].md_entries;
mc.mc_txn = txn;
mc.mc_dbi = dbi;
- mc.mc_initialized = 0;
+ mc.mc_flags = 0;
mc.mc_xcursor = &mx;
mdb_xcursor_init0(&mc);
mdb_xcursor_init1(txn, dbi, &mx, mpp.mp_page, leaf);
if (data) {
rc = mdb_del(&mx.mx_txn, mx.mx_cursor.mc_dbi, data, NULL);
- mdb_xcursor_fini(txn, dbi, &mx);
+ mdb_xcursor_fini(&mc);
/* If sub-DB still has entries, we're done */
if (mx.mx_txn.mt_dbs[mx.mx_cursor.mc_dbi].md_root != P_INVALID) {
memcpy(NODEDATA(leaf), &mx.mx_txn.mt_dbs[mx.mx_cursor.mc_dbi],
put_sub:
mc.mc_txn = txn;
mc.mc_dbi = dbi;
- mc.mc_initialized = 0;
+ mc.mc_flags = 0;
mc.mc_xcursor = &mx;
mdb_xcursor_init0(&mc);
mdb_xcursor_init1(txn, dbi, &mx, mpp.mp_page, leaf);
leaf->mn_flags |= F_DUPDATA;
}
rc = mdb_put0(&mx.mx_txn, mx.mx_cursor.mc_dbi, data, &xdata, flags);
- mdb_xcursor_fini(txn, dbi, &mx);
+ mdb_xcursor_fini(&mc);
memcpy(NODEDATA(leaf), &mx.mx_txn.mt_dbs[mx.mx_cursor.mc_dbi],
sizeof(MDB_db));
}
mc.mc_txn = txn;
mc.mc_dbi = dbi;
mc.mc_snum = 0;
- mc.mc_initialized = 0;
- mc.mc_eof = 0;
+ mc.mc_flags = 0;
if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
mc.mc_xcursor = &mx;
mdb_xcursor_init0(&mc);
if (mc.mc_xcursor) {
MDB_ppage *top;
MDB_node *leaf;
- mdb_xcursor_fini(txn, dbi, &mx);
+ mdb_xcursor_fini(&mc);
top = CURSOR_TOP(&mc);
leaf = NODEPTR(top->mp_page, top->mp_ki);
memcpy(NODEDATA(leaf), &mx.mx_txn.mt_dbs[mx.mx_cursor.mc_dbi],