]> git.sur5r.net Git - openldap/blobdiff - libraries/liblmdb/mdb.c
Tweak ITS#8722 fix: Use XCURSOR_REFRESH()
[openldap] / libraries / liblmdb / mdb.c
index 5c3989c6b500a53be220d2f9efce6da091e45333..e31867de8207a9f0476c0707185e107f6feee4fa 100644 (file)
@@ -5,7 +5,7 @@
  *     BerkeleyDB API, but much simplified.
  */
 /*
- * Copyright 2011-2016 Howard Chu, Symas Corp.
+ * Copyright 2011-2017 Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -113,6 +113,10 @@ typedef SSIZE_T    ssize_t;
 /* Most platforms have posix_memalign, older may only have memalign */
 #define HAVE_MEMALIGN  1
 #include <malloc.h>
+/* On Solaris, we need the POSIX sigwait function */
+#if defined (__sun)
+# define _POSIX_PTHREAD_SEMANTICS      1
+#endif
 #endif
 
 #if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER))
@@ -120,7 +124,7 @@ typedef SSIZE_T     ssize_t;
 #include <resolv.h>    /* defines BYTE_ORDER on HPUX and Solaris */
 #endif
 
-#if defined(__APPLE__) || defined (BSD)
+#if defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__)
 # define MDB_USE_POSIX_SEM     1
 # define MDB_FDATASYNC         fsync
 #elif defined(ANDROID)
@@ -129,6 +133,7 @@ typedef SSIZE_T     ssize_t;
 
 #ifndef _WIN32
 #include <pthread.h>
+#include <signal.h>
 #ifdef MDB_USE_POSIX_SEM
 # define MDB_USE_HASH          1
 #include <semaphore.h>
@@ -340,15 +345,15 @@ mdb_sem_wait(sem_t *sem)
 }
 
 #else  /* MDB_USE_POSIX_MUTEX: */
-       /** Shared mutex/semaphore as it is stored (mdb_mutex_t), and as
-        *      local variables keep it (mdb_mutexref_t).
+       /** Shared mutex/semaphore as the original is stored.
         *
-        *      When #mdb_mutexref_t is a pointer declaration and #mdb_mutex_t is
-        *      not, then it is array[size 1] so it can be assigned to a pointer.
-        *      @{
+        *      Not for copies.  Instead it can be assigned to an #mdb_mutexref_t.
+        *      When mdb_mutexref_t is a pointer and mdb_mutex_t is not, then it
+        *      is array[size 1] so it can be assigned to the pointer.
         */
-typedef pthread_mutex_t mdb_mutex_t[1], *mdb_mutexref_t;
-       /*      @} */
+typedef pthread_mutex_t mdb_mutex_t[1];
+       /** Reference to an #mdb_mutex_t */
+typedef pthread_mutex_t *mdb_mutexref_t;
        /** Lock the reader or writer mutex.
         *      Returns 0 or a code to give #mdb_mutex_failed(), as in #LOCK_MUTEX().
         */
@@ -873,19 +878,26 @@ typedef struct MDB_page {
        /** Header for a single key/data pair within a page.
         * Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2.
         * We guarantee 2-byte alignment for 'MDB_node's.
+        *
+        * #mn_lo and #mn_hi are used for data size on leaf nodes, and for child
+        * pgno on branch nodes.  On 64 bit platforms, #mn_flags is also used
+        * for pgno.  (Branch nodes have no flags).  Lo and hi are in host byte
+        * order in case some accesses can be optimized to 32-bit word access.
+        *
+        * Leaf node flags describe node contents.  #F_BIGDATA says the node's
+        * data part is the page number of an overflow page with actual data.
+        * #F_DUPDATA and #F_SUBDATA can be combined giving duplicate data in
+        * a sub-page/sub-database, and named databases (just #F_SUBDATA).
         */
 typedef struct MDB_node {
-       /** lo and hi are used for data size on leaf nodes and for
-        * child pgno on branch nodes. On 64 bit platforms, flags
-        * is also used for pgno. (Branch nodes have no flags).
-        * They are in host byte order in case that lets some
-        * accesses be optimized into a 32-bit word access.
-        */
+       /** part of data size or pgno
+        *      @{ */
 #if BYTE_ORDER == LITTLE_ENDIAN
-       unsigned short  mn_lo, mn_hi;   /**< part of data size or pgno */
+       unsigned short  mn_lo, mn_hi;
 #else
        unsigned short  mn_hi, mn_lo;
 #endif
+       /** @} */
 /** @defgroup mdb_node Node Flags
  *     @ingroup internal
  *     Flags for node headers.
@@ -1214,17 +1226,19 @@ typedef struct MDB_xcursor {
        unsigned char mx_dbflag;
 } MDB_xcursor;
 
-       /** Check if there is an inited xcursor, so #XCURSOR_REFRESH() is proper */
+       /** Check if there is an inited xcursor */
 #define XCURSOR_INITED(mc) \
        ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
 
-       /** Update sub-page pointer, if any, in \b mc->mc_xcursor.  Needed
+       /** Update the xcursor's sub-page pointer, if any, in \b mc.  Needed
         *      when the node which contains the sub-page may have moved.  Called
-        *      with \b mp = mc->mc_pg[mc->mc_top], \b ki = mc->mc_ki[mc->mc_top].
+        *      with leaf page \b mp = mc->mc_pg[\b top].
         */
-#define XCURSOR_REFRESH(mc, mp, ki) do { \
+#define XCURSOR_REFRESH(mc, top, mp) do { \
        MDB_page *xr_pg = (mp); \
-       MDB_node *xr_node = NODEPTR(xr_pg, ki); \
+       MDB_node *xr_node; \
+       if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \
+       xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \
        if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \
                (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \
 } while (0)
@@ -2477,8 +2491,8 @@ done:
                        if (m2 == mc) continue;
                        if (m2->mc_pg[mc->mc_top] == mp) {
                                m2->mc_pg[mc->mc_top] = np;
-                               if (XCURSOR_INITED(m2) && IS_LEAF(np))
-                                       XCURSOR_REFRESH(m2, np, m2->mc_ki[mc->mc_top]);
+                               if (IS_LEAF(np))
+                                       XCURSOR_REFRESH(m2, mc->mc_top, np);
                        }
                }
        }
@@ -5451,8 +5465,17 @@ mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags)
 
                if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) {
                        i = 0;
-                       if (flags & MDB_PS_LAST)
+                       if (flags & MDB_PS_LAST) {
                                i = NUMKEYS(mp) - 1;
+                               /* if already init'd, see if we're already in right place */
+                               if (mc->mc_flags & C_INITIALIZED) {
+                                       if (mc->mc_ki[mc->mc_top] == i) {
+                                               mc->mc_top = mc->mc_snum++;
+                                               mp = mc->mc_pg[mc->mc_top];
+                                               goto ready;
+                                       }
+                               }
+                       }
                } else {
                        int      exact;
                        node = mdb_node_search(mc, key, &exact);
@@ -5478,6 +5501,7 @@ mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags)
                if ((rc = mdb_cursor_push(mc, mp)))
                        return rc;
 
+ready:
                if (flags & MDB_PS_MODIFY) {
                        if ((rc = mdb_page_touch(mc)) != 0)
                                return rc;
@@ -5803,15 +5827,20 @@ mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
        MDB_node        *leaf;
        int rc;
 
-       if ((mc->mc_flags & C_EOF) ||
-               ((mc->mc_flags & C_DEL) && op == MDB_NEXT_DUP)) {
+       if ((mc->mc_flags & C_DEL && op == MDB_NEXT_DUP))
                return MDB_NOTFOUND;
-       }
+
        if (!(mc->mc_flags & C_INITIALIZED))
                return mdb_cursor_first(mc, key, data);
 
        mp = mc->mc_pg[mc->mc_top];
 
+       if (mc->mc_flags & C_EOF) {
+               if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mp)-1)
+                       return MDB_NOTFOUND;
+               mc->mc_flags ^= C_EOF;
+       }
+
        if (mc->mc_db->md_flags & MDB_DUPSORT) {
                leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
                if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
@@ -6216,16 +6245,13 @@ mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data)
        if (mc->mc_xcursor)
                mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
 
-       if (!(mc->mc_flags & C_EOF)) {
-
-               if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) {
-                       rc = mdb_page_search(mc, NULL, MDB_PS_LAST);
-                       if (rc != MDB_SUCCESS)
-                               return rc;
-               }
-               mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top]));
-
+       if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) {
+               rc = mdb_page_search(mc, NULL, MDB_PS_LAST);
+               if (rc != MDB_SUCCESS)
+                       return rc;
        }
+       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;
        mc->mc_flags |= C_INITIALIZED|C_EOF;
        leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
@@ -6402,6 +6428,11 @@ fetchm:
                        rc = MDB_INCOMPATIBLE;
                        break;
                }
+               if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) {
+                       mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+                       rc = MDB_NOTFOUND;
+                       break;
+               }
                {
                        MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
                        if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
@@ -6898,8 +6929,7 @@ new_sub:
                                if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) {
                                        m3->mc_ki[i]++;
                                }
-                               if (XCURSOR_INITED(m3))
-                                       XCURSOR_REFRESH(m3, mp, m3->mc_ki[i]);
+                               XCURSOR_REFRESH(m3, i, mp);
                        }
                }
        }
@@ -6941,7 +6971,6 @@ put_sub:
                                MDB_xcursor *mx = mc->mc_xcursor;
                                unsigned i = mc->mc_top;
                                MDB_page *mp = mc->mc_pg[i];
-                               int nkeys = NUMKEYS(mp);
 
                                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;
@@ -6949,8 +6978,8 @@ put_sub:
                                        if (m2->mc_pg[i] == mp) {
                                                if (m2->mc_ki[i] == mc->mc_ki[i]) {
                                                        mdb_xcursor_init2(m2, mx, new_dupdata);
-                                               } else if (!insert_key && m2->mc_ki[i] < nkeys) {
-                                                       XCURSOR_REFRESH(m2, mp, m2->mc_ki[i]);
+                                               } else if (!insert_key) {
+                                                       XCURSOR_REFRESH(m2, i, mp);
                                                }
                                        }
                                }
@@ -7055,12 +7084,7 @@ mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
                                                if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
                                                if (!(m2->mc_flags & C_INITIALIZED)) continue;
                                                if (m2->mc_pg[mc->mc_top] == mp) {
-                                                       MDB_node *n2 = leaf;
-                                                       if (m2->mc_ki[mc->mc_top] != mc->mc_ki[mc->mc_top]) {
-                                                               n2 = NODEPTR(mp, m2->mc_ki[mc->mc_top]);
-                                                               if (n2->mn_flags & F_SUBDATA) continue;
-                                                       }
-                                                       m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
+                                                       XCURSOR_REFRESH(m2, mc->mc_top, mp);
                                                }
                                        }
                                }
@@ -7636,9 +7660,15 @@ mdb_cursor_count(MDB_cursor *mc, size_t *countp)
        if (!(mc->mc_flags & C_INITIALIZED))
                return EINVAL;
 
-       if (!mc->mc_snum || (mc->mc_flags & C_EOF))
+       if (!mc->mc_snum)
                return MDB_NOTFOUND;
 
+       if (mc->mc_flags & C_EOF) {
+               if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top]))
+                       return MDB_NOTFOUND;
+               mc->mc_flags ^= C_EOF;
+       }
+
        leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
        if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
                *countp = 1;
@@ -7900,8 +7930,8 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
                                        m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
                                        m3->mc_ki[csrc->mc_top-1]++;
                                }
-                               if (XCURSOR_INITED(m3) && IS_LEAF(mps))
-                                       XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
+                               if (IS_LEAF(mps))
+                                       XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
                        }
                } else
                /* Adding on the right, bump others down */
@@ -7922,8 +7952,8 @@ mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
                                        } else {
                                                m3->mc_ki[csrc->mc_top]--;
                                        }
-                                       if (XCURSOR_INITED(m3) && IS_LEAF(mps))
-                                               XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
+                                       if (IS_LEAF(mps))
+                                               XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
                                }
                        }
                }
@@ -8124,8 +8154,8 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
                                m3->mc_ki[top-1] > csrc->mc_ki[top-1]) {
                                m3->mc_ki[top-1]--;
                        }
-                       if (XCURSOR_INITED(m3) && IS_LEAF(psrc))
-                               XCURSOR_REFRESH(m3, m3->mc_pg[top], m3->mc_ki[top]);
+                       if (IS_LEAF(psrc))
+                               XCURSOR_REFRESH(m3, top, m3->mc_pg[top]);
                }
        }
        {
@@ -8387,8 +8417,7 @@ mdb_cursor_del0(MDB_cursor *mc)
                                } else if (m3->mc_ki[mc->mc_top] > ki) {
                                        m3->mc_ki[mc->mc_top]--;
                                }
-                               if (XCURSOR_INITED(m3))
-                                       XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
+                               XCURSOR_REFRESH(m3, mc->mc_top, mp);
                        }
                }
        }
@@ -8425,15 +8454,20 @@ mdb_cursor_del0(MDB_cursor *mc)
                                        }
                                        if (mc->mc_db->md_flags & MDB_DUPSORT) {
                                                MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]);
-                                               /* If this node is a fake page, it needs to be reinited
-                                                * because its data has moved. But just reset mc_pg[0]
-                                                * if the xcursor is already live.
+                                               /* If this node has dupdata, it may need to be reinited
+                                                * because its data has moved.
+                                                * If the xcursor was not initd it must be reinited.
+                                                * Else if node points to a subDB, nothing is needed.
+                                                * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset.
                                                 */
-                                               if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) {
-                                                       if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)
-                                                               m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node);
-                                                       else
+                                               if (node->mn_flags & F_DUPDATA) {
+                                                       if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
+                                                               if (!(node->mn_flags & F_SUBDATA))
+                                                                       m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node);
+                                                       } else {
                                                                mdb_xcursor_init1(m3, node);
+                                                               m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL;
+                                                       }
                                                }
                                        }
                                }
@@ -8919,8 +8953,8 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno
                                m3->mc_ki[ptop] >= mc->mc_ki[ptop]) {
                                m3->mc_ki[ptop]++;
                        }
-                       if (XCURSOR_INITED(m3) && IS_LEAF(mp))
-                               XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
+                       if (IS_LEAF(mp))
+                               XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]);
                }
        }
        DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp)));
@@ -8996,6 +9030,13 @@ mdb_env_copythr(void *arg)
 #else
        int len;
 #define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0)
+#ifdef SIGPIPE
+       sigset_t set;
+       sigemptyset(&set);
+       sigaddset(&set, SIGPIPE);
+       if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
+               my->mc_error = rc;
+#endif
 #endif
 
        pthread_mutex_lock(&my->mc_mutex);
@@ -9012,6 +9053,15 @@ again:
                        DO_WRITE(rc, my->mc_fd, ptr, wsize, len);
                        if (!rc) {
                                rc = ErrCode();
+#if defined(SIGPIPE) && !defined(_WIN32)
+                               if (rc == EPIPE) {
+                                       /* Collect the pending SIGPIPE, otherwise at least OS X
+                                        * gives it to the process on thread-exit (ITS#8504).
+                                        */
+                                       int tmp;
+                                       sigwait(&set, &tmp);
+                               }
+#endif
                                break;
                        } else if (len > 0) {
                                rc = MDB_SUCCESS;
@@ -9709,8 +9759,11 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
                MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
                if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
                        return MDB_INCOMPATIBLE;
-       } else if (! (rc == MDB_NOTFOUND && (flags & MDB_CREATE))) {
-               return rc;
+       } else {
+               if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE))
+                       return rc;
+               if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+                       return EACCES;
        }
 
        /* Done here so we cannot fail after creating a new DB */
@@ -9724,7 +9777,8 @@ int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *db
                memset(&dummy, 0, sizeof(dummy));
                dummy.md_root = P_INVALID;
                dummy.md_flags = flags & PERSISTENT_FLAGS;
-               rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
+               WITH_CURSOR_TRACKING(mc,
+                       rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA));
                dbflag |= DB_DIRTY;
        }
 
@@ -10077,7 +10131,7 @@ mdb_reader_check(MDB_env *env, int *dead)
        return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS;
 }
 
-/** As #mdb_reader_check(). rlocked = <caller locked the reader mutex>. */
+/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */
 static int ESECT
 mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
 {