]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#7829 more for prev commit
[openldap] / libraries / liblmdb / mdb.c
index ff8fb88ad2f16c969e501e6d3daed56a23072501..10a8358449d2333086daf00579f5b7dcc36fbc12 100644 (file)
@@ -936,7 +936,7 @@ struct MDB_txn {
  *     @{
  */
 #define MDB_TXN_RDONLY         0x01            /**< read-only transaction */
-#define MDB_TXN_ERROR          0x02            /**< an error has occurred */
+#define MDB_TXN_ERROR          0x02            /**< txn is unusable after an error */
 #define MDB_TXN_DIRTY          0x04            /**< must write, even if dirty list is empty */
 #define MDB_TXN_SPILLS         0x08            /**< txn or a parent has spilled pages */
 /** @} */
@@ -1145,7 +1145,8 @@ static int        mdb_update_key(MDB_cursor *mc, MDB_val *key);
 static void    mdb_cursor_pop(MDB_cursor *mc);
 static int     mdb_cursor_push(MDB_cursor *mc, MDB_page *mp);
 
-static int     mdb_cursor_del0(MDB_cursor *mc, MDB_node *leaf);
+static int     mdb_cursor_del0(MDB_cursor *mc);
+static int     mdb_del0(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned flags);
 static int     mdb_cursor_sibling(MDB_cursor *mc, int move_right);
 static int     mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op);
 static int     mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op);
@@ -1396,6 +1397,7 @@ static void mdb_audit(MDB_txn *txn)
        mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
        while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0)
                freecount += *(MDB_ID *)data.mv_data;
+       mdb_tassert(txn, rc == MDB_NOTFOUND);
 
        count = 0;
        for (i = 0; i<txn->mt_numdbs; i++) {
@@ -1409,8 +1411,8 @@ static void mdb_audit(MDB_txn *txn)
                        txn->mt_dbs[i].md_leaf_pages +
                        txn->mt_dbs[i].md_overflow_pages;
                if (txn->mt_dbs[i].md_flags & MDB_DUPSORT) {
-                       mdb_page_search(&mc, NULL, MDB_PS_FIRST);
-                       do {
+                       rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST);
+                       for (; rc == MDB_SUCCESS; rc = mdb_cursor_sibling(&mc, 1)) {
                                unsigned j;
                                MDB_page *mp;
                                mp = mc.mc_pg[mc.mc_top];
@@ -1424,7 +1426,7 @@ static void mdb_audit(MDB_txn *txn)
                                        }
                                }
                        }
-                       while (mdb_cursor_sibling(&mc, 1) == 0);
+                       mdb_tassert(txn, rc == MDB_NOTFOUND);
                }
        }
        if (freecount + count + 2 /* metapages */ != txn->mt_next_pgno) {
@@ -2104,6 +2106,7 @@ done:
                        if (m2->mc_pg[mc->mc_top] == mp) {
                                m2->mc_pg[mc->mc_top] = np;
                                if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
+                                       IS_LEAF(np) &&
                                        m2->mc_ki[mc->mc_top] == mc->mc_ki[mc->mc_top])
                                {
                                        MDB_node *leaf = NODEPTR(np, mc->mc_ki[mc->mc_top]);
@@ -2767,23 +2770,20 @@ mdb_freelist_save(MDB_txn *txn)
                mop += mop_len;
                rc = mdb_cursor_first(&mc, &key, &data);
                for (; !rc; rc = mdb_cursor_next(&mc, &key, &data, MDB_NEXT)) {
-                       unsigned flags = MDB_CURRENT;
                        txnid_t id = *(txnid_t *)key.mv_data;
                        ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1;
                        MDB_ID save;
 
                        mdb_tassert(txn, len >= 0 && id <= env->me_pglast);
+                       key.mv_data = &id;
                        if (len > mop_len) {
                                len = mop_len;
                                data.mv_size = (len + 1) * sizeof(MDB_ID);
-                               /* Drop MDB_CURRENT when changing the data size */
-                               key.mv_data = &id;
-                               flags = 0;
                        }
                        data.mv_data = mop -= len;
                        save = mop[0];
                        mop[0] = len;
-                       rc = mdb_cursor_put(&mc, &key, &data, flags);
+                       rc = mdb_cursor_put(&mc, &key, &data, MDB_CURRENT);
                        mop[0] = save;
                        if (rc || !(mop_len -= len))
                                break;
@@ -5804,7 +5804,7 @@ mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
        unsigned int nflags;
        DKBUF;
 
-       if (mc == NULL)
+       if (mc == NULL || key == NULL)
                return EINVAL;
 
        env = mc->mc_txn->mt_env;
@@ -5825,16 +5825,8 @@ mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
        if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_ERROR))
                return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
 
-       if (flags != MDB_CURRENT) {
-               if (key == NULL)
-                       return EINVAL;
-               if (key->mv_size-1 >= ENV_MAXKEY(env))
-                       return MDB_BAD_VALSIZE;
-       } else {
-               /* Ignore key except in sub-cursor, where key holds the data */
-               if (!(mc->mc_flags & C_SUB))
-                       key = NULL;
-       }
+       if (flags != MDB_CURRENT && key->mv_size-1 >= ENV_MAXKEY(env))
+               return MDB_BAD_VALSIZE;
 
 #if SIZE_MAX > MAXDATASIZE
        if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE))
@@ -6127,8 +6119,8 @@ current:
                                                return ENOMEM;
                                        id2.mid = pg;
                                        id2.mptr = np;
-                                       rc = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2);
-                                       mdb_cassert(mc, rc == 0);
+                                       rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2);
+                                       mdb_cassert(mc, rc2 == 0);
                                        if (!(flags & MDB_RESERVE)) {
                                                /* Copy end of page, adjusting alignment so
                                                 * compiler may copy words instead of bytes.
@@ -6165,7 +6157,6 @@ current:
                        goto done;
                }
                mdb_node_del(mc, 0);
-               mc->mc_db->md_entries--;
        }
 
        rdata = data;
@@ -6226,8 +6217,10 @@ put_sub:
                        /* converted, write the original data first */
                        if (dkey.mv_size) {
                                rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags);
-                               if (rc)
-                                       return rc;
+                               if (rc) {
+                                       mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+                                       return rc == MDB_KEYEXIST ? MDB_CORRUPTED : rc;
+                               }
                                {
                                        /* Adjust other cursors pointing to mp */
                                        MDB_cursor *m2;
@@ -6256,7 +6249,7 @@ put_sub:
                /* sub-writes might have failed so check rc again.
                 * Don't increment count if we just replaced an existing item.
                 */
-               if (!rc && !(flags & MDB_CURRENT))
+               if (!rc && insert)
                        mc->mc_db->md_entries++;
                if (flags & MDB_MULTIPLE) {
                        if (!rc) {
@@ -6304,14 +6297,18 @@ mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
                return rc;
 
        mp = mc->mc_pg[mc->mc_top];
+       if (IS_LEAF2(mp))
+               goto del_key;
        leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
 
-       if (!IS_LEAF2(mp) && F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+       if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
                if (!(flags & MDB_NODUPDATA)) {
                        if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
                                mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
                        }
                        rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, MDB_NOSPILL);
+                       if (rc)
+                               return rc;
                        /* If sub-DB still has entries, we're done */
                        if (mc->mc_xcursor->mx_db.md_entries) {
                                if (leaf->mn_flags & F_SUBDATA) {
@@ -6342,14 +6339,29 @@ mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
                if (leaf->mn_flags & F_SUBDATA) {
                        /* add all the child DB's pages to the free list */
                        rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
-                       if (rc == MDB_SUCCESS) {
-                               mc->mc_db->md_entries -=
-                                       mc->mc_xcursor->mx_db.md_entries;
-                       }
+                       if (rc)
+                               goto fail;
+                       mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries;
                }
        }
 
-       return mdb_cursor_del0(mc, leaf);
+       /* add overflow pages to free list */
+       if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
+               MDB_page *omp;
+               pgno_t pg;
+
+               memcpy(&pg, NODEDATA(leaf), sizeof(pg));
+               if ((rc = mdb_page_get(mc->mc_txn, pg, &omp, NULL)) ||
+                       (rc = mdb_ovpage_free(mc, omp)))
+                       goto fail;
+       }
+
+del_key:
+       return mdb_cursor_del0(mc);
+
+fail:
+       mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+       return rc;
 }
 
 /** Allocate and initialize new pages for a database.
@@ -7011,7 +7023,9 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                        unsigned int snum = csrc->mc_snum;
                        MDB_node *s2;
                        /* must find the lowest key below src */
-                       mdb_page_search_lowest(csrc);
+                       rc = mdb_page_search_lowest(csrc);
+                       if (rc)
+                               return rc;
                        if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
                                key.mv_size = csrc->mc_db->md_pad;
                                key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size);
@@ -7034,7 +7048,9 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                MDB_node *s2;
                MDB_val bkey;
                /* must find the lowest key below dst */
-               mdb_page_search_lowest(cdst);
+               rc = mdb_page_search_lowest(cdst);
+               if (rc)
+                       return rc;
                if (IS_LEAF2(cdst->mc_pg[cdst->mc_top])) {
                        bkey.mv_size = cdst->mc_db->md_pad;
                        bkey.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, bkey.mv_size);
@@ -7196,7 +7212,9 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
                                unsigned int snum = csrc->mc_snum;
                                MDB_node *s2;
                                /* must find the lowest key below src */
-                               mdb_page_search_lowest(csrc);
+                               rc = mdb_page_search_lowest(csrc);
+                               if (rc)
+                                       return rc;
                                if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
                                        key.mv_size = csrc->mc_db->md_pad;
                                        key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size);
@@ -7305,6 +7323,7 @@ mdb_rebalance(MDB_cursor *mc)
        int rc;
        unsigned int ptop, minkeys;
        MDB_cursor      mn;
+       indx_t oldki;
 
        minkeys = 1 + (IS_BRANCH(mc->mc_pg[mc->mc_top]));
        DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)",
@@ -7409,6 +7428,7 @@ mdb_rebalance(MDB_cursor *mc)
        mdb_cursor_copy(mc, &mn);
        mn.mc_xcursor = NULL;
 
+       oldki = mc->mc_ki[mc->mc_top];
        if (mc->mc_ki[ptop] == 0) {
                /* We're the leftmost leaf in our parent.
                 */
@@ -7442,49 +7462,44 @@ mdb_rebalance(MDB_cursor *mc)
         * (A branch page must never have less than 2 keys.)
         */
        minkeys = 1 + (IS_BRANCH(mn.mc_pg[mn.mc_top]));
-       if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= FILL_THRESHOLD && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys)
-               return mdb_node_move(&mn, mc);
-       else {
-               if (mc->mc_ki[ptop] == 0)
+       if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= FILL_THRESHOLD && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) {
+               rc = mdb_node_move(&mn, mc);
+               if (mc->mc_ki[ptop] == 0) {
+                       mc->mc_ki[mc->mc_top] = oldki;
+               } else {
+                       mc->mc_ki[mc->mc_top] = oldki + 1;
+               }
+       } else {
+               if (mc->mc_ki[ptop] == 0) {
                        rc = mdb_page_merge(&mn, mc);
-               else {
+                       mc->mc_ki[mc->mc_top] = oldki;
+               } else {
+                       unsigned int nkeys = NUMKEYS(mn.mc_pg[mn.mc_top]);
                        mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1;
                        rc = mdb_page_merge(mc, &mn);
-                       mdb_cursor_copy(&mn, mc);
+                       mc->mc_pg[mc->mc_top] = mn.mc_pg[mn.mc_top];
+                       mc->mc_ki[mc->mc_top] = oldki + nkeys;
                }
-               mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
+               mc->mc_flags &= ~C_EOF;
        }
        return rc;
 }
 
 /** Complete a delete operation started by #mdb_cursor_del(). */
 static int
-mdb_cursor_del0(MDB_cursor *mc, MDB_node *leaf)
+mdb_cursor_del0(MDB_cursor *mc)
 {
        int rc;
        MDB_page *mp;
        indx_t ki;
        unsigned int nkeys;
 
-       mp = mc->mc_pg[mc->mc_top];
        ki = mc->mc_ki[mc->mc_top];
-
-       /* add overflow pages to free list */
-       if (!IS_LEAF2(mp) && F_ISSET(leaf->mn_flags, F_BIGDATA)) {
-               MDB_page *omp;
-               pgno_t pg;
-
-               memcpy(&pg, NODEDATA(leaf), sizeof(pg));
-               if ((rc = mdb_page_get(mc->mc_txn, pg, &omp, NULL)) ||
-                       (rc = mdb_ovpage_free(mc, omp)))
-                       return rc;
-       }
        mdb_node_del(mc, mc->mc_db->md_pad);
        mc->mc_db->md_entries--;
        rc = mdb_rebalance(mc);
-       if (rc != MDB_SUCCESS)
-               mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
-       else {
+
+       if (rc == MDB_SUCCESS) {
                MDB_cursor *m2, *m3;
                MDB_dbi dbi = mc->mc_dbi;
 
@@ -7492,11 +7507,14 @@ mdb_cursor_del0(MDB_cursor *mc, MDB_node *leaf)
                nkeys = NUMKEYS(mp);
 
                /* if mc points past last node in page, find next sibling */
-               if (mc->mc_ki[mc->mc_top] >= nkeys)
-                       mdb_cursor_sibling(mc, 1);
+               if (mc->mc_ki[mc->mc_top] >= nkeys) {
+                       rc = mdb_cursor_sibling(mc, 1);
+                       if (rc == MDB_NOTFOUND)
+                               rc = MDB_SUCCESS;
+               }
 
                /* Adjust other cursors pointing to mp */
-               for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+               for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2=m2->mc_next) {
                        m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2;
                        if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED))
                                continue;
@@ -7508,13 +7526,18 @@ mdb_cursor_del0(MDB_cursor *mc, MDB_node *leaf)
                                        if (m3->mc_ki[mc->mc_top] > ki)
                                                m3->mc_ki[mc->mc_top]--;
                                }
-                               if (m3->mc_ki[mc->mc_top] >= nkeys)
-                                       mdb_cursor_sibling(m3, 1);
+                               if (m3->mc_ki[mc->mc_top] >= nkeys) {
+                                       rc = mdb_cursor_sibling(m3, 1);
+                                       if (rc == MDB_NOTFOUND)
+                                               rc = MDB_SUCCESS;
+                               }
                        }
                }
                mc->mc_flags |= C_DEL;
        }
 
+       if (rc)
+               mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
        return rc;
 }
 
@@ -7522,28 +7545,35 @@ int
 mdb_del(MDB_txn *txn, MDB_dbi dbi,
     MDB_val *key, MDB_val *data)
 {
-       MDB_cursor mc;
-       MDB_xcursor mx;
-       MDB_cursor_op op;
-       MDB_val rdata, *xdata;
-       int              rc, exact;
-       DKBUF;
-
-       DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key)));
-
        if (!key || dbi == FREE_DBI || !TXN_DBI_EXIST(txn, dbi))
                return EINVAL;
 
        if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_ERROR))
                return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
 
-       mdb_cursor_init(&mc, txn, dbi, &mx);
-
-       exact = 0;
        if (!F_ISSET(txn->mt_dbs[dbi].md_flags, MDB_DUPSORT)) {
                /* must ignore any data */
                data = NULL;
        }
+
+       return mdb_del0(txn, dbi, key, data, 0);
+}
+
+static int
+mdb_del0(MDB_txn *txn, MDB_dbi dbi,
+       MDB_val *key, MDB_val *data, unsigned flags)
+{
+       MDB_cursor mc;
+       MDB_xcursor mx;
+       MDB_cursor_op op;
+       MDB_val rdata, *xdata;
+       int              rc, exact = 0;
+       DKBUF;
+
+       DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key)));
+
+       mdb_cursor_init(&mc, txn, dbi, &mx);
+
        if (data) {
                op = MDB_GET_BOTH;
                rdata = *data;
@@ -7551,6 +7581,7 @@ mdb_del(MDB_txn *txn, MDB_dbi dbi,
        } else {
                op = MDB_SET;
                xdata = NULL;
+               flags |= MDB_NODUPDATA;
        }
        rc = mdb_cursor_set(&mc, key, xdata, op, &exact);
        if (rc == 0) {
@@ -7565,7 +7596,7 @@ mdb_del(MDB_txn *txn, MDB_dbi dbi,
                mc.mc_flags |= C_UNTRACK;
                mc.mc_next = txn->mt_cursors[dbi];
                txn->mt_cursors[dbi] = &mc;
-               rc = mdb_cursor_del(&mc, data ? 0 : MDB_NODUPDATA);
+               rc = mdb_cursor_del(&mc, flags);
                txn->mt_cursors[dbi] = mc.mc_next;
        }
        return rc;
@@ -7614,7 +7645,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
 
        if (mc->mc_snum < 2) {
                if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp)))
-                       return rc;
+                       goto done;
                /* shift current top to make room for new parent */
                mc->mc_pg[1] = mc->mc_pg[0];
                mc->mc_ki[1] = mc->mc_ki[0];
@@ -7632,7 +7663,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        mc->mc_ki[0] = mc->mc_ki[1];
                        mc->mc_db->md_root = mp->mp_pgno;
                        mc->mc_db->md_depth--;
-                       return rc;
+                       goto done;
                }
                mc->mc_snum = 2;
                mc->mc_top = 1;
@@ -7661,7 +7692,6 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        int x;
                        unsigned int lsize, rsize, ksize;
                        /* Move half of the keys to the right sibling */
-                       copy = NULL;
                        x = mc->mc_ki[mc->mc_top] - split_indx;
                        ksize = mc->mc_db->md_pad;
                        split = LEAF2KEY(mp, split_indx, ksize);
@@ -7708,8 +7738,10 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
 
                        /* grab a page to hold a temporary copy */
                        copy = mdb_page_malloc(mc->mc_txn, 1);
-                       if (copy == NULL)
-                               return ENOMEM;
+                       if (copy == NULL) {
+                               rc = ENOMEM;
+                               goto done;
+                       }
                        copy->mp_pgno  = mp->mp_pgno;
                        copy->mp_flags = mp->mp_flags;
                        copy->mp_lower = PAGEHDRSZ;
@@ -7789,6 +7821,8 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                mn.mc_top--;
                did_split = 1;
                rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0);
+               if (rc)
+                       goto done;
 
                /* root split? */
                if (mn.mc_snum == mc->mc_snum) {
@@ -7825,14 +7859,14 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
        }
        mc->mc_flags ^= C_SPLITTING;
        if (rc != MDB_SUCCESS) {
-               return rc;
+               goto done;
        }
        if (nflags & MDB_APPEND) {
                mc->mc_pg[mc->mc_top] = rp;
                mc->mc_ki[mc->mc_top] = 0;
                rc = mdb_node_add(mc, 0, newkey, newdata, newpgno, nflags);
                if (rc)
-                       return rc;
+                       goto done;
                for (i=0; i<mc->mc_top; i++)
                        mc->mc_ki[i] = mn.mc_ki[i];
        } else if (!IS_LEAF2(mp)) {
@@ -7870,11 +7904,8 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        }
 
                        rc = mdb_node_add(mc, j, &rkey, rdata, pgno, flags);
-                       if (rc) {
-                               /* return tmp page to freelist */
-                               mdb_page_free(env, copy);
-                               return rc;
-                       }
+                       if (rc)
+                               goto done;
                        if (i == nkeys) {
                                i = 0;
                                j = 0;
@@ -7914,8 +7945,6 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                }
                        }
                }
-               /* return tmp page to freelist */
-               mdb_page_free(env, copy);
        }
 
        {
@@ -7966,6 +7995,12 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                }
        }
        DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp)));
+
+done:
+       if (copy)                                       /* tmp page */
+               mdb_page_free(env, copy);
+       if (rc)
+               mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
        return rc;
 }
 
@@ -8305,22 +8340,22 @@ mdb_drop0(MDB_cursor *mc, int subs)
                                                memcpy(&pg, NODEDATA(ni), sizeof(pg));
                                                rc = mdb_page_get(txn, pg, &omp, NULL);
                                                if (rc != 0)
-                                                       return rc;
+                                                       goto done;
                                                mdb_cassert(mc, IS_OVERFLOW(omp));
                                                rc = mdb_midl_append_range(&txn->mt_free_pgs,
                                                        pg, omp->mp_pages);
                                                if (rc)
-                                                       return rc;
+                                                       goto done;
                                        } else if (subs && (ni->mn_flags & F_SUBDATA)) {
                                                mdb_xcursor_init1(mc, ni);
                                                rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
                                                if (rc)
-                                                       return rc;
+                                                       goto done;
                                        }
                                }
                        } else {
                                if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0)
-                                       return rc;
+                                       goto done;
                                for (i=0; i<n; i++) {
                                        pgno_t pg;
                                        ni = NODEPTR(mp, i);
@@ -8334,6 +8369,8 @@ mdb_drop0(MDB_cursor *mc, int subs)
                        mc->mc_ki[mc->mc_top] = i;
                        rc = mdb_cursor_sibling(mc, 1);
                        if (rc) {
+                               if (rc != MDB_NOTFOUND)
+                                       goto done;
                                /* no more siblings, go back to beginning
                                 * of previous level.
                                 */
@@ -8347,6 +8384,9 @@ mdb_drop0(MDB_cursor *mc, int subs)
                }
                /* free it */
                rc = mdb_midl_append(&txn->mt_free_pgs, mc->mc_db->md_root);
+done:
+               if (rc)
+                       txn->mt_flags |= MDB_TXN_ERROR;
        } else if (rc == MDB_NOTFOUND) {
                rc = MDB_SUCCESS;
        }
@@ -8377,10 +8417,12 @@ int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
 
        /* Can't delete the main DB */
        if (del && dbi > MAIN_DBI) {
-               rc = mdb_del(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL);
+               rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, 0);
                if (!rc) {
                        txn->mt_dbflags[dbi] = DB_STALE;
                        mdb_dbi_close(txn->mt_env, dbi);
+               } else {
+                       txn->mt_flags |= MDB_TXN_ERROR;
                }
        } else {
                /* reset the DB record, mark it dirty */