+ MDB_SET_KEY(leaf, key);
+ return MDB_SUCCESS;
+}
+
+int
+mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data,
+ MDB_cursor_op op)
+{
+ int rc;
+ int exact = 0;
+
+ assert(mc);
+
+ switch (op) {
+ case MDB_GET_BOTH:
+ case MDB_GET_BOTH_RANGE:
+ if (data == NULL || mc->mc_xcursor == NULL) {
+ rc = EINVAL;
+ break;
+ }
+ /* FALLTHRU */
+ case MDB_SET:
+ case MDB_SET_RANGE:
+ if (key == NULL || key->mv_size == 0 || key->mv_size > MAXKEYSIZE) {
+ rc = EINVAL;
+ } else if (op == MDB_SET_RANGE)
+ rc = mdb_cursor_set(mc, key, data, op, NULL);
+ else
+ rc = mdb_cursor_set(mc, key, data, op, &exact);
+ break;
+ case MDB_GET_MULTIPLE:
+ if (data == NULL ||
+ !(mc->mc_db->md_flags & MDB_DUPFIXED) ||
+ !(mc->mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ rc = MDB_SUCCESS;
+ if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) ||
+ (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF))
+ break;
+ goto fetchm;
+ case MDB_NEXT_MULTIPLE:
+ if (data == NULL ||
+ !(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ rc = EINVAL;
+ break;
+ }
+ if (!(mc->mc_flags & C_INITIALIZED))
+ rc = mdb_cursor_first(mc, key, data);
+ else
+ rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
+ if (rc == MDB_SUCCESS) {
+ if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
+ MDB_cursor *mx;
+fetchm:
+ mx = &mc->mc_xcursor->mx_cursor;
+ data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) *
+ mx->mc_db->md_pad;
+ data->mv_data = METADATA(mx->mc_pg[mx->mc_top]);
+ mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1;
+ } else {
+ rc = MDB_NOTFOUND;
+ }
+ }
+ break;
+ case MDB_NEXT:
+ case MDB_NEXT_DUP:
+ case MDB_NEXT_NODUP:
+ if (!(mc->mc_flags & C_INITIALIZED))
+ rc = mdb_cursor_first(mc, key, data);
+ else
+ rc = mdb_cursor_next(mc, key, data, op);
+ break;
+ case MDB_PREV:
+ case MDB_PREV_DUP:
+ case MDB_PREV_NODUP:
+ if (!(mc->mc_flags & C_INITIALIZED) || (mc->mc_flags & C_EOF))
+ rc = mdb_cursor_last(mc, key, data);
+ else
+ rc = mdb_cursor_prev(mc, key, data, op);
+ break;
+ case MDB_FIRST:
+ rc = mdb_cursor_first(mc, key, data);
+ break;
+ case MDB_FIRST_DUP:
+ if (data == NULL ||
+ !(mc->mc_db->md_flags & MDB_DUPSORT) ||
+ !(mc->mc_flags & C_INITIALIZED) ||
+ !(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
+ break;
+ case MDB_LAST:
+ rc = mdb_cursor_last(mc, key, data);
+ break;
+ case MDB_LAST_DUP:
+ if (data == NULL ||
+ !(mc->mc_db->md_flags & MDB_DUPSORT) ||
+ !(mc->mc_flags & C_INITIALIZED) ||
+ !(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL);
+ break;
+ default:
+ DPRINTF("unhandled/unimplemented cursor operation %u", op);
+ rc = EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/** Touch all the pages in the cursor stack.
+ * Makes sure all the pages are writable, before attempting a write operation.
+ * @param[in] mc The cursor to operate on.
+ */
+static int
+mdb_cursor_touch(MDB_cursor *mc)
+{
+ int rc;
+
+ if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
+ MDB_cursor mc2;
+ mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL);
+ rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 1);
+ if (rc)
+ return rc;
+ *mc->mc_dbflag = DB_DIRTY;
+ }
+ for (mc->mc_top = 0; mc->mc_top < mc->mc_snum; mc->mc_top++) {
+ rc = mdb_page_touch(mc);
+ if (rc)
+ return rc;
+ }
+ mc->mc_top = mc->mc_snum-1;
+ return MDB_SUCCESS;
+}
+
+int
+mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
+ unsigned int flags)
+{
+ MDB_node *leaf = NULL;
+ MDB_val xdata, *rdata, dkey;
+ MDB_page *fp;
+ MDB_db dummy;
+ int do_sub = 0;
+ unsigned int mcount = 0;
+ size_t nsize;
+ int rc, rc2;
+ MDB_pagebuf pbuf;
+ char dbuf[MAXKEYSIZE+1];
+ unsigned int nflags;
+ DKBUF;
+
+ if (F_ISSET(mc->mc_txn->mt_flags, MDB_TXN_RDONLY))
+ return EACCES;
+
+ DPRINTF("==> put db %u key [%s], size %zu, data size %zu",
+ mc->mc_dbi, DKEY(key), key ? key->mv_size:0, data->mv_size);
+
+ dkey.mv_size = 0;
+
+ if (flags == MDB_CURRENT) {
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+ rc = MDB_SUCCESS;
+ } else if (mc->mc_db->md_root == P_INVALID) {
+ MDB_page *np;
+ /* new database, write a root leaf page */
+ DPUTS("allocating new root leaf page");
+ if ((np = mdb_page_new(mc, P_LEAF, 1)) == NULL) {
+ return ENOMEM;
+ }
+ mc->mc_snum = 0;
+ mdb_cursor_push(mc, np);
+ mc->mc_db->md_root = np->mp_pgno;
+ mc->mc_db->md_depth++;
+ *mc->mc_dbflag = DB_DIRTY;
+ if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED))
+ == MDB_DUPFIXED)
+ np->mp_flags |= P_LEAF2;
+ mc->mc_flags |= C_INITIALIZED;
+ rc = MDB_NOTFOUND;
+ goto top;
+ } else {
+ int exact = 0;
+ MDB_val d2;
+ rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact);
+ if ((flags & MDB_NOOVERWRITE) && rc == 0) {
+ DPRINTF("duplicate key [%s]", DKEY(key));
+ *data = d2;
+ return MDB_KEYEXIST;
+ }
+ if (rc && rc != MDB_NOTFOUND)
+ return rc;
+ }
+
+ /* Cursor is positioned, now make sure all pages are writable */
+ rc2 = mdb_cursor_touch(mc);
+ if (rc2)
+ return rc2;
+
+top:
+ /* The key already exists */