]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
ITS#8263 streamline prev patch
[openldap] / libraries / liblmdb / mdb.c
index 19705a0618f050659e63c74a3dde02176f453cdc..03c4429169a23a312e6eef624e77314bd79b9a3c 100644 (file)
@@ -446,12 +446,17 @@ static txnid_t mdb_debug_start;
        /**     The version number for a database's lockfile format. */
 #define MDB_LOCK_VERSION        1
 
-       /**     @brief The max size of a key we can write, or 0 for dynamic max.
+       /**     @brief The max size of a key we can write, or 0 for computed max.
         *
-        *      Define this as 0 to compute the max from the page size.  511
-        *      is default for backwards compat: liblmdb <= 0.9.10 can break
-        *      when modifying a DB with keys/dupsort data bigger than its max.
-        *      #MDB_DEVEL sets the default to 0.
+        *      This macro should normally be left alone or set to 0.
+        *      Note that a database with big keys or dupsort data cannot be
+        *      reliably modified by a liblmdb which uses a smaller max.
+        *      The default is 511 for backwards compat, or 0 when #MDB_DEVEL.
+        *
+        *      Other values are allowed, for backwards compat.  However:
+        *      A value bigger than the computed max can break if you do not
+        *      know what you are doing, and liblmdb <= 0.9.10 can break when
+        *      modifying a DB with keys/dupsort data bigger than its max.
         *
         *      Data items in an #MDB_DUPSORT database are also limited to
         *      this size, since they're actually keys of a sub-DB.  Keys and
@@ -1237,6 +1242,7 @@ static int        mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data);
 static void    mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx);
 static void    mdb_xcursor_init0(MDB_cursor *mc);
 static void    mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node);
+static void    mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int force);
 
 static int     mdb_drop0(MDB_cursor *mc, int subs);
 static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi);
@@ -1245,6 +1251,13 @@ static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi);
 static MDB_cmp_func    mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long;
 /** @endcond */
 
+/** Compare two items pointing at size_t's of unknown alignment. */
+#ifdef MISALIGNED_OK
+# define mdb_cmp_clong mdb_cmp_long
+#else
+# define mdb_cmp_clong mdb_cmp_cint
+#endif
+
 #ifdef _WIN32
 static SECURITY_DESCRIPTOR mdb_null_sd;
 static SECURITY_ATTRIBUTES mdb_all_sa;
@@ -1558,7 +1571,12 @@ mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
 int
 mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
 {
-       return txn->mt_dbxs[dbi].md_dcmp(a, b);
+       MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp;
+#if UINT_MAX < SIZE_MAX
+       if (dcmp == mdb_cmp_int && a->mv_size == sizeof(size_t))
+               dcmp = mdb_cmp_clong;
+#endif
+       return dcmp(a, b);
 }
 
 /** Allocate memory for a page.
@@ -2486,15 +2504,11 @@ mdb_txn_renew0(MDB_txn *txn)
        MDB_env *env = txn->mt_env;
        MDB_txninfo *ti = env->me_txns;
        MDB_meta *meta;
-       unsigned int i, nr;
+       unsigned int i, nr, flags = txn->mt_flags;
        uint16_t x;
        int rc, new_notls = 0;
 
-       if (txn->mt_flags & MDB_TXN_RDONLY) {
-               txn->mt_flags = MDB_TXN_RDONLY;
-               /* Setup db info */
-               txn->mt_numdbs = env->me_numdbs;
-               txn->mt_dbxs = env->me_dbxs;    /* mostly static anyway */
+       if ((flags &= MDB_TXN_RDONLY) != 0) {
                if (!ti) {
                        meta = env->me_metas[ mdb_env_pick_meta(env) ];
                        txn->mt_txnid = meta->mm_txnid;
@@ -2547,6 +2561,7 @@ mdb_txn_renew0(MDB_txn *txn)
                        txn->mt_u.reader = r;
                        meta = env->me_metas[txn->mt_txnid & 1];
                }
+               txn->mt_dbxs = env->me_dbxs;    /* mostly static anyway */
        } else {
                if (ti) {
                        LOCK_MUTEX_W(env);
@@ -2557,14 +2572,11 @@ mdb_txn_renew0(MDB_txn *txn)
                        meta = env->me_metas[ mdb_env_pick_meta(env) ];
                        txn->mt_txnid = meta->mm_txnid;
                }
-               /* Setup db info */
-               txn->mt_numdbs = env->me_numdbs;
                txn->mt_txnid++;
 #if MDB_DEBUG
                if (txn->mt_txnid == mdb_debug_start)
                        mdb_debug = 1;
 #endif
-               txn->mt_flags = 0;
                txn->mt_child = NULL;
                txn->mt_loose_pgs = NULL;
                txn->mt_loose_count = 0;
@@ -2584,6 +2596,10 @@ mdb_txn_renew0(MDB_txn *txn)
        /* Moved to here to avoid a data race in read TXNs */
        txn->mt_next_pgno = meta->mm_last_pg+1;
 
+       txn->mt_flags = flags;
+
+       /* Setup db info */
+       txn->mt_numdbs = env->me_numdbs;
        for (i=2; i<txn->mt_numdbs; i++) {
                x = env->me_dbflags[i];
                txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS;
@@ -2807,8 +2823,8 @@ mdb_txn_reset0(MDB_txn *txn, const char *act)
                }
 
                if (!txn->mt_parent) {
-                       if (mdb_midl_shrink(&txn->mt_free_pgs))
-                               env->me_free_pgs = txn->mt_free_pgs;
+                       mdb_midl_shrink(&txn->mt_free_pgs);
+                       env->me_free_pgs = txn->mt_free_pgs;
                        /* me_pgstate: */
                        env->me_pghead = NULL;
                        env->me_pglast = 0;
@@ -3380,7 +3396,8 @@ mdb_txn_commit(MDB_txn *txn)
                                        goto fail;
                                }
                                data.mv_data = &txn->mt_dbs[i];
-                               rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, 0);
+                               rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data,
+                                       F_SUBDATA);
                                if (rc)
                                        goto fail;
                        }
@@ -3393,8 +3410,8 @@ mdb_txn_commit(MDB_txn *txn)
 
        mdb_midl_free(env->me_pghead);
        env->me_pghead = NULL;
-       if (mdb_midl_shrink(&txn->mt_free_pgs))
-               env->me_free_pgs = txn->mt_free_pgs;
+       mdb_midl_shrink(&txn->mt_free_pgs);
+       env->me_free_pgs = txn->mt_free_pgs;
 
 #if (MDB_DEBUG) > 2
        mdb_audit(txn);
@@ -3537,6 +3554,9 @@ mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
        mdb_env_init_meta0(env, meta);
 
        p = calloc(2, psize);
+       if (!p)
+               return ENOMEM;
+
        p->mp_pgno = 0;
        p->mp_flags = P_META;
        *(MDB_meta *)METADATA(p) = *meta;
@@ -3632,7 +3652,6 @@ mdb_env_write_meta(MDB_txn *txn)
        /* Write to the SYNC fd */
        mfd = env->me_flags & (MDB_NOSYNC|MDB_NOMETASYNC) ?
                env->me_fd : env->me_mfd;
-retry_write:
 #ifdef _WIN32
        {
                memset(&ov, 0, sizeof(ov));
@@ -3641,12 +3660,15 @@ retry_write:
                        rc = -1;
        }
 #else
+retry_write:
        rc = pwrite(mfd, ptr, len, off);
 #endif
        if (rc != len) {
                rc = rc < 0 ? ErrCode() : EIO;
+#ifndef _WIN32
                if (rc == EINTR)
                        goto retry_write;
+#endif
                DPUTS("write failed, disk error?");
                /* On a failure, the pagecache still contains the new data.
                 * Write some old data back, to prevent it from being used.
@@ -4772,7 +4794,11 @@ mdb_cmp_long(const MDB_val *a, const MDB_val *b)
                *(size_t *)a->mv_data > *(size_t *)b->mv_data;
 }
 
-/** Compare two items pointing at aligned unsigned int's */
+/** Compare two items pointing at aligned unsigned int's.
+ *
+ *     This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp,
+ *     but #mdb_cmp_clong() is called instead if the data type is size_t.
+ */
 static int
 mdb_cmp_int(const MDB_val *a, const MDB_val *b)
 {
@@ -4810,13 +4836,6 @@ mdb_cmp_cint(const MDB_val *a, const MDB_val *b)
 #endif
 }
 
-/** Compare two items pointing at size_t's of unknown alignment. */
-#ifdef MISALIGNED_OK
-# define mdb_cmp_clong mdb_cmp_long
-#else
-# define mdb_cmp_clong mdb_cmp_cint
-#endif
-
 /** Compare two items lexically */
 static int
 mdb_cmp_memn(const MDB_val *a, const MDB_val *b)
@@ -5202,6 +5221,8 @@ mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags)
                                                &mc->mc_dbx->md_name, &exact);
                                        if (!exact)
                                                return MDB_NOTFOUND;
+                                       if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
+                                               return MDB_INCOMPATIBLE; /* not a named DB */
                                        rc = mdb_node_read(mc->mc_txn, leaf, &data);
                                        if (rc)
                                                return rc;
@@ -5713,8 +5734,10 @@ set2:
 
        if (leaf == NULL) {
                DPUTS("===> inexact leaf not found, goto sibling");
-               if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS)
+               if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) {
+                       mc->mc_flags |= C_EOF;
                        return rc;              /* no entries matched */
+               }
                mp = mc->mc_pg[mc->mc_top];
                mdb_cassert(mc, IS_LEAF(mp));
                leaf = NODEPTR(mp, 0);
@@ -5752,15 +5775,21 @@ set1:
                                        return rc;
                        }
                } else if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) {
-                       MDB_val d2;
-                       if ((rc = mdb_node_read(mc->mc_txn, leaf, &d2)) != MDB_SUCCESS)
+                       MDB_val olddata;
+                       MDB_cmp_func *dcmp;
+                       if ((rc = mdb_node_read(mc->mc_txn, leaf, &olddata)) != MDB_SUCCESS)
                                return rc;
-                       rc = mc->mc_dbx->md_dcmp(data, &d2);
+                       dcmp = mc->mc_dbx->md_dcmp;
+#if UINT_MAX < SIZE_MAX
+                       if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
+                               dcmp = mdb_cmp_clong;
+#endif
+                       rc = dcmp(data, &olddata);
                        if (rc) {
                                if (op == MDB_GET_BOTH || rc > 0)
                                        return MDB_NOTFOUND;
                                rc = 0;
-                               *data = d2;
+                               *data = olddata;
                        }
 
                } else {
@@ -6082,7 +6111,7 @@ mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
        enum { MDB_NO_ROOT = MDB_LAST_ERRCODE+10 }; /* internal code */
        MDB_env         *env;
        MDB_node        *leaf = NULL;
-       MDB_page        *fp, *mp;
+       MDB_page        *fp, *mp, *sub_root = NULL;
        uint16_t        fp_flags;
        MDB_val         xdata, *rdata, dkey, olddata;
        MDB_db dummy;
@@ -6270,16 +6299,17 @@ more:
 
                        /* Was a single item before, must convert now */
                        if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+                               MDB_cmp_func *dcmp;
                                /* Just overwrite the current item */
                                if (flags == MDB_CURRENT)
                                        goto current;
-
+                               dcmp = mc->mc_dbx->md_dcmp;
 #if UINT_MAX < SIZE_MAX
-                               if (mc->mc_dbx->md_dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
-                                       mc->mc_dbx->md_dcmp = mdb_cmp_clong;
+                               if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
+                                       dcmp = mdb_cmp_clong;
 #endif
                                /* does data match? */
-                               if (!mc->mc_dbx->md_dcmp(data, &olddata)) {
+                               if (!dcmp(data, &olddata)) {
                                        if (flags & MDB_NODUPDATA)
                                                return MDB_KEYEXIST;
                                        /* overwrite it */
@@ -6361,6 +6391,7 @@ prep_subDB:
                                        offset = env->me_psize - olddata.mv_size;
                                        flags |= F_DUPDATA|F_SUBDATA;
                                        dummy.md_root = mp->mp_pgno;
+                                       sub_root = mp;
                        }
                        if (mp != fp) {
                                mp->mp_flags = fp_flags | P_DIRTY;
@@ -6385,6 +6416,9 @@ prep_subDB:
                        goto new_sub;
                }
 current:
+               /* LMDB passes F_SUBDATA in 'flags' to write a DB record */
+               if ((leaf->mn_flags ^ flags) & F_SUBDATA)
+                       return MDB_INCOMPATIBLE;
                /* overflow page overwrites need special handling */
                if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
                        MDB_page *omp;
@@ -6504,7 +6538,7 @@ new_sub:
                 * DB are all zero size.
                 */
                if (do_sub) {
-                       int xflags;
+                       int xflags, new_dupdata;
                        size_t ecount;
 put_sub:
                        xdata.mv_size = 0;
@@ -6517,28 +6551,32 @@ put_sub:
                                xflags = (flags & MDB_NODUPDATA) ?
                                        MDB_NOOVERWRITE|MDB_NOSPILL : MDB_NOSPILL;
                        }
+                       if (sub_root)
+                               mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root;
+                       new_dupdata = (int)dkey.mv_size;
                        /* converted, write the original data first */
                        if (dkey.mv_size) {
                                rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags);
                                if (rc)
                                        goto bad_sub;
-                               {
-                                       /* Adjust other cursors pointing to mp */
-                                       MDB_cursor *m2;
-                                       unsigned i = mc->mc_top;
-                                       MDB_page *mp = mc->mc_pg[i];
-
-                                       for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
-                                               if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
-                                               if (!(m2->mc_flags & C_INITIALIZED)) continue;
-                                               if (m2->mc_pg[i] == mp && m2->mc_ki[i] == mc->mc_ki[i]) {
-                                                       mdb_xcursor_init1(m2, leaf);
-                                               }
-                                       }
-                               }
                                /* we've done our job */
                                dkey.mv_size = 0;
                        }
+                       if (!(leaf->mn_flags & F_SUBDATA) || sub_root) {
+                               /* Adjust other cursors pointing to mp */
+                               MDB_cursor *m2;
+                               MDB_xcursor *mx = mc->mc_xcursor;
+                               unsigned i = mc->mc_top;
+                               MDB_page *mp = mc->mc_pg[i];
+
+                               for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
+                                       if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
+                                       if (!(m2->mc_flags & C_INITIALIZED)) continue;
+                                       if (m2->mc_pg[i] == mp && m2->mc_ki[i] == mc->mc_ki[i]) {
+                                               mdb_xcursor_init2(m2, mx, new_dupdata);
+                                       }
+                               }
+                       }
                        ecount = mc->mc_xcursor->mx_db.md_entries;
                        if (flags & MDB_APPENDDUP)
                                xflags |= MDB_APPEND;
@@ -6655,6 +6693,11 @@ mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
                                goto fail;
                }
        }
+       /* LMDB passes F_SUBDATA in 'flags' to delete a DB record */
+       else if ((leaf->mn_flags ^ flags) & F_SUBDATA) {
+               rc = MDB_INCOMPATIBLE;
+               goto fail;
+       }
 
        /* add overflow pages to free list */
        if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
@@ -7076,15 +7119,45 @@ mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node)
                                mx->mx_db.md_flags |= MDB_INTEGERKEY;
                }
        }
+       DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
+               mx->mx_db.md_root));
+       mx->mx_dbflag = DB_VALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
 #if UINT_MAX < SIZE_MAX
-       if (mc->mc_dbx->md_dcmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t)) {
-               mc->mc_dbx->md_dcmp = mdb_cmp_clong;
+       if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t))
                mx->mx_dbx.md_cmp = mdb_cmp_clong;
-       }
 #endif
+}
+
+
+/** Fixup a sorted-dups cursor due to underlying update.
+ *     Sets up some fields that depend on the data from the main cursor.
+ *     Almost the same as init1, but skips initialization steps if the
+ *     xcursor had already been used.
+ * @param[in] mc The main cursor whose sorted-dups cursor is to be fixed up.
+ * @param[in] src_mx The xcursor of an up-to-date cursor.
+ * @param[in] new_dupdata True if converting from a non-#F_DUPDATA item.
+ */
+static void
+mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata)
+{
+       MDB_xcursor *mx = mc->mc_xcursor;
+
+       if (new_dupdata) {
+               mx->mx_cursor.mc_snum = 1;
+               mx->mx_cursor.mc_top = 0;
+               mx->mx_cursor.mc_flags |= C_INITIALIZED;
+               mx->mx_cursor.mc_ki[0] = 0;
+               mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
+#if UINT_MAX < SIZE_MAX
+               mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
+#endif
+       } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) {
+               return;
+       }
+       mx->mx_db = src_mx->mx_db;
+       mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0];
        DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
                mx->mx_db.md_root));
-       mx->mx_dbflag = DB_VALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
 }
 
 /** Initialize a cursor for a given transaction and database. */
@@ -7405,8 +7478,22 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
                /* Adjust other cursors pointing to mp */
                MDB_cursor *m2, *m3;
                MDB_dbi dbi = csrc->mc_dbi;
-               MDB_page *mp = csrc->mc_pg[csrc->mc_top];
+               MDB_page *mp;
 
+               mp = cdst->mc_pg[csrc->mc_top];
+               for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+                       if (csrc->mc_flags & C_SUB)
+                               m3 = &m2->mc_xcursor->mx_cursor;
+                       else
+                               m3 = m2;
+                       if (m3 == cdst) continue;
+                       if (m3->mc_pg[csrc->mc_top] == mp && m3->mc_ki[csrc->mc_top] >=
+                               cdst->mc_ki[csrc->mc_top]) {
+                               m3->mc_ki[csrc->mc_top]++;
+                       }
+               }
+
+               mp = csrc->mc_pg[csrc->mc_top];
                for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
                        if (csrc->mc_flags & C_SUB)
                                m3 = &m2->mc_xcursor->mx_cursor;
@@ -7608,9 +7695,9 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
                uint16_t depth = cdst->mc_db->md_depth;
                mdb_cursor_pop(cdst);
                rc = mdb_rebalance(cdst);
-               /* Did the tree shrink? */
-               if (depth > cdst->mc_db->md_depth)
-                       snum--;
+               /* Did the tree height change? */
+               if (depth != cdst->mc_db->md_depth)
+                       snum += cdst->mc_db->md_depth - depth;
                cdst->mc_snum = snum;
                cdst->mc_top = snum-1;
        }
@@ -7650,17 +7737,23 @@ mdb_rebalance(MDB_cursor *mc)
 {
        MDB_node        *node;
        int rc;
-       unsigned int ptop, minkeys;
+       unsigned int ptop, minkeys, thresh;
        MDB_cursor      mn;
        indx_t oldki;
 
-       minkeys = 1 + (IS_BRANCH(mc->mc_pg[mc->mc_top]));
+       if (IS_BRANCH(mc->mc_pg[mc->mc_top])) {
+               minkeys = 2;
+               thresh = 1;
+       } else {
+               minkeys = 1;
+               thresh = FILL_THRESHOLD;
+       }
        DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)",
            IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch",
            mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]),
                (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10));
 
-       if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= FILL_THRESHOLD &&
+       if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh &&
                NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) {
                DPRINTF(("no need to rebalance page %"Z"u, above fill threshold",
                    mdb_dbg_pgno(mc->mc_pg[mc->mc_top])));
@@ -7794,10 +7887,9 @@ mdb_rebalance(MDB_cursor *mc)
         * move one key from it. Otherwise we should try to merge them.
         * (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) {
+       if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) {
                rc = mdb_node_move(&mn, mc);
-               if (mc->mc_ki[ptop]) {
+               if (mc->mc_ki[mc->mc_top-1]) {
                        oldki++;
                }
        } else {
@@ -7837,16 +7929,35 @@ mdb_cursor_del0(MDB_cursor *mc)
        MDB_page *mp;
        indx_t ki;
        unsigned int nkeys;
+       MDB_cursor *m2, *m3;
+       MDB_dbi dbi = mc->mc_dbi;
 
        ki = mc->mc_ki[mc->mc_top];
+       mp = mc->mc_pg[mc->mc_top];
        mdb_node_del(mc, mc->mc_db->md_pad);
        mc->mc_db->md_entries--;
+       {
+               /* Adjust other cursors pointing to mp */
+               for (m2 = mc->mc_txn->mt_cursors[dbi]; 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;
+                       if (m3 == mc || m3->mc_snum < mc->mc_snum)
+                               continue;
+                       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]--;
+                                       else if (mc->mc_db->md_flags & MDB_DUPSORT)
+                                               m3->mc_xcursor->mx_cursor.mc_flags |= C_EOF;
+                               }
+                       }
+               }
+       }
        rc = mdb_rebalance(mc);
 
        if (rc == MDB_SUCCESS) {
-               MDB_cursor *m2, *m3;
-               MDB_dbi dbi = mc->mc_dbi;
-
                /* DB is totally empty now, just bail out.
                 * Other cursors adjustments were already done
                 * by mdb_rebalance and aren't needed here.
@@ -7857,30 +7968,15 @@ mdb_cursor_del0(MDB_cursor *mc)
                mp = mc->mc_pg[mc->mc_top];
                nkeys = NUMKEYS(mp);
 
-               /* if mc points past last node in page, find next sibling */
-               if (mc->mc_ki[mc->mc_top] >= nkeys) {
-                       rc = mdb_cursor_sibling(mc, 1);
-                       if (rc == MDB_NOTFOUND) {
-                               mc->mc_flags |= C_EOF;
-                               rc = MDB_SUCCESS;
-                       }
-               }
-
                /* Adjust other cursors pointing to mp */
                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;
-                       if (m3 == mc || m3->mc_snum < mc->mc_snum)
+                       if (m3->mc_snum < mc->mc_snum)
                                continue;
                        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]--;
-                                       else if (mc->mc_db->md_flags & MDB_DUPSORT)
-                                               m3->mc_xcursor->mx_cursor.mc_flags |= C_EOF;
-                               }
+                               /* if m3 points past last node in page, find next sibling */
                                if (m3->mc_ki[mc->mc_top] >= nkeys) {
                                        rc = mdb_cursor_sibling(m3, 1);
                                        if (rc == MDB_NOTFOUND) {
@@ -8010,8 +8106,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                mc->mc_ki[0] = 0;
                mc->mc_db->md_root = pp->mp_pgno;
                DPRINTF(("root split! new root = %"Z"u", pp->mp_pgno));
-               mc->mc_db->md_depth++;
-               new_root = 1;
+               new_root = mc->mc_db->md_depth++;
 
                /* Add left (implicit) pointer. */
                if ((rc = mdb_node_add(mc, 0, NULL, NULL, mp->mp_pgno, 0)) != MDB_SUCCESS) {
@@ -8132,7 +8227,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                psize = 0;
                                if (newindx <= split_indx || newindx >= nkeys) {
                                        i = 0; j = 1;
-                                       k = newindx >= nkeys ? nkeys : split_indx+2;
+                                       k = newindx >= nkeys ? nkeys : split_indx+1+IS_LEAF(mp);
                                } else {
                                        i = nkeys; j = -1;
                                        k = split_indx-1;
@@ -8324,7 +8419,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                        if (new_root) {
                                int k;
                                /* root split */
-                               for (k=m3->mc_top; k>=0; k--) {
+                               for (k=new_root; k>=0; k--) {
                                        m3->mc_ki[k+1] = m3->mc_ki[k];
                                        m3->mc_pg[k+1] = m3->mc_pg[k];
                                }
@@ -9152,7 +9247,7 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
        if (rc == MDB_SUCCESS) {
                /* make sure this is actually a DB */
                MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
-               if (!(node->mn_flags & F_SUBDATA))
+               if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
                        return MDB_INCOMPATIBLE;
        } else if (rc == MDB_NOTFOUND && (flags & MDB_CREATE)) {
                /* Create if requested */
@@ -9345,7 +9440,7 @@ int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
 
        /* Can't delete the main DB */
        if (del && dbi > MAIN_DBI) {
-               rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, 0);
+               rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA);
                if (!rc) {
                        txn->mt_dbflags[dbi] = DB_STALE;
                        mdb_dbi_close(txn->mt_env, dbi);