Support multiple iterators in the Red-Black Tree implementation.
authorHeikki Linnakangas
Fri, 2 Sep 2016 05:39:39 +0000 (08:39 +0300)
committerHeikki Linnakangas
Fri, 2 Sep 2016 05:39:39 +0000 (08:39 +0300)
While we don't need multiple iterators at the moment, the interface is
nicer and less dangerous this way.

Aleksander Alekseev, with some changes by me.

src/backend/access/gin/ginbulk.c
src/backend/lib/rbtree.c
src/include/access/gin_private.h
src/include/lib/rbtree.h

index d6422ea91ebdcd625fdb75ab4edd84367d9f1a3e..71c64e468c99249cdf03dec03eac43c9028ac9b6 100644 (file)
@@ -255,7 +255,7 @@ qsortCompareItemPointers(const void *a, const void *b)
 void
 ginBeginBAScan(BuildAccumulator *accum)
 {
-   rb_begin_iterate(accum->tree, LeftRightWalk);
+   rb_begin_iterate(accum->tree, LeftRightWalk, &accum->tree_walk);
 }
 
 /*
@@ -271,7 +271,7 @@ ginGetBAEntry(BuildAccumulator *accum,
    GinEntryAccumulator *entry;
    ItemPointerData *list;
 
-   entry = (GinEntryAccumulator *) rb_iterate(accum->tree);
+   entry = (GinEntryAccumulator *) rb_iterate(&accum->tree_walk);
 
    if (entry == NULL)
        return NULL;            /* no more entries */
index 4fa8a1dd58a4da4e1f689e9beddac18adfd6e716..242e9803ccc54245046474671c174a3bb0e7937b 100644 (file)
 #include "lib/rbtree.h"
 
 
-/*
- * Values of RBNode.iteratorState
- *
- * Note that iteratorState has an undefined value except in nodes that are
- * currently being visited by an active iteration.
- */
-#define InitialState   (0)
-#define FirstStepDone  (1)
-#define SecondStepDone (2)
-#define ThirdStepDone  (3)
-
 /*
  * Colors of nodes (values of RBNode.color)
  */
@@ -53,10 +42,6 @@ struct RBTree
 {
    RBNode     *root;           /* root node, or RBNIL if tree is empty */
 
-   /* Iteration state */
-   RBNode     *cur;            /* current iteration node */
-   RBNode     *(*iterate) (RBTree *rb);
-
    /* Remaining fields are constant after rb_create */
 
    Size        node_size;      /* actual size of tree nodes */
@@ -75,8 +60,19 @@ struct RBTree
  */
 #define RBNIL (&sentinel)
 
-static RBNode sentinel = {InitialState, RBBLACK, RBNIL, RBNIL, NULL};
+static RBNode sentinel = {RBBLACK, RBNIL, RBNIL, NULL};
 
+/*
+ * Values used in the RBTreeIterator.next_state field, with an
+ * InvertedWalk iterator.
+ */
+typedef enum InvertedWalkNextStep
+{
+   NextStepBegin,
+   NextStepUp,
+   NextStepLeft,
+   NextStepRight
+} InvertedWalkNextStep;
 
 /*
  * rb_create: create an empty RBTree
@@ -123,8 +119,6 @@ rb_create(Size node_size,
    Assert(node_size > sizeof(RBNode));
 
    tree->root = RBNIL;
-   tree->cur = RBNIL;
-   tree->iterate = NULL;
    tree->node_size = node_size;
    tree->comparator = comparator;
    tree->combiner = combiner;
@@ -437,7 +431,6 @@ rb_insert(RBTree *rb, const RBNode *data, bool *isNew)
 
    x = rb->allocfunc (rb->arg);
 
-   x->iteratorState = InitialState;
    x->color = RBRED;
 
    x->left = RBNIL;
@@ -653,171 +646,194 @@ rb_delete(RBTree *rb, RBNode *node)
  *                       Traverse                                    *
  **********************************************************************/
 
-/*
- * The iterator routines were originally coded in tail-recursion style,
- * which is nice to look at, but is trouble if your compiler isn't smart
- * enough to optimize it.  Now we just use looping.
- */
-#define descend(next_node) \
-   do { \
-       (next_node)->iteratorState = InitialState; \
-       node = rb->cur = (next_node); \
-       goto restart; \
-   } while (0)
+static RBNode *
+rb_left_right_iterator(RBTreeIterator *iter)
+{
+   if (iter->last_visited == NULL)
+   {
+       iter->last_visited = iter->rb->root;
+       while (iter->last_visited->left != RBNIL)
+           iter->last_visited = iter->last_visited->left;
 
-#define ascend(next_node) \
-   do { \
-       node = rb->cur = (next_node); \
-       goto restart; \
-   } while (0)
+       return iter->last_visited;
+   }
 
+   if (iter->last_visited->right != RBNIL)
+   {
+       iter->last_visited = iter->last_visited->right;
+       while (iter->last_visited->left != RBNIL)
+           iter->last_visited = iter->last_visited->left;
 
-static RBNode *
-rb_left_right_iterator(RBTree *rb)
-{
-   RBNode     *node = rb->cur;
+       return iter->last_visited;
+   }
 
-restart:
-   switch (node->iteratorState)
+   for (;;)
    {
-       case InitialState:
-           if (node->left != RBNIL)
-           {
-               node->iteratorState = FirstStepDone;
-               descend(node->left);
-           }
-           /* FALL THROUGH */
-       case FirstStepDone:
-           node->iteratorState = SecondStepDone;
-           return node;
-       case SecondStepDone:
-           if (node->right != RBNIL)
-           {
-               node->iteratorState = ThirdStepDone;
-               descend(node->right);
-           }
-           /* FALL THROUGH */
-       case ThirdStepDone:
-           if (node->parent)
-               ascend(node->parent);
+       RBNode     *came_from = iter->last_visited;
+
+       iter->last_visited = iter->last_visited->parent;
+       if (iter->last_visited == NULL)
+       {
+           iter->is_over = true;
            break;
-       default:
-           elog(ERROR, "unrecognized rbtree node state: %d",
-                node->iteratorState);
+       }
+
+       if (iter->last_visited->left == came_from)
+           break;              /* came from left sub-tree, return current
+                                * node */
+
+       /* else - came from right sub-tree, continue to move up */
    }
 
-   return NULL;
+   return iter->last_visited;
 }
 
 static RBNode *
-rb_right_left_iterator(RBTree *rb)
+rb_right_left_iterator(RBTreeIterator *iter)
 {
-   RBNode     *node = rb->cur;
+   if (iter->last_visited == NULL)
+   {
+       iter->last_visited = iter->rb->root;
+       while (iter->last_visited->right != RBNIL)
+           iter->last_visited = iter->last_visited->right;
+
+       return iter->last_visited;
+   }
 
-restart:
-   switch (node->iteratorState)
+   if (iter->last_visited->left != RBNIL)
    {
-       case InitialState:
-           if (node->right != RBNIL)
-           {
-               node->iteratorState = FirstStepDone;
-               descend(node->right);
-           }
-           /* FALL THROUGH */
-       case FirstStepDone:
-           node->iteratorState = SecondStepDone;
-           return node;
-       case SecondStepDone:
-           if (node->left != RBNIL)
-           {
-               node->iteratorState = ThirdStepDone;
-               descend(node->left);
-           }
-           /* FALL THROUGH */
-       case ThirdStepDone:
-           if (node->parent)
-               ascend(node->parent);
+       iter->last_visited = iter->last_visited->left;
+       while (iter->last_visited->right != RBNIL)
+           iter->last_visited = iter->last_visited->right;
+
+       return iter->last_visited;
+   }
+
+   for (;;)
+   {
+       RBNode     *came_from = iter->last_visited;
+
+       iter->last_visited = iter->last_visited->parent;
+       if (iter->last_visited == NULL)
+       {
+           iter->is_over = true;
            break;
-       default:
-           elog(ERROR, "unrecognized rbtree node state: %d",
-                node->iteratorState);
+       }
+
+       if (iter->last_visited->right == came_from)
+           break;              /* came from right sub-tree, return current
+                                * node */
+
+       /* else - came from left sub-tree, continue to move up */
    }
 
-   return NULL;
+   return iter->last_visited;
 }
 
 static RBNode *
-rb_direct_iterator(RBTree *rb)
+rb_direct_iterator(RBTreeIterator *iter)
 {
-   RBNode     *node = rb->cur;
+   if (iter->last_visited == NULL)
+   {
+       iter->last_visited = iter->rb->root;
+       return iter->last_visited;
+   }
 
-restart:
-   switch (node->iteratorState)
+   if (iter->last_visited->left != RBNIL)
    {
-       case InitialState:
-           node->iteratorState = FirstStepDone;
-           return node;
-       case FirstStepDone:
-           if (node->left != RBNIL)
+       iter->last_visited = iter->last_visited->left;
+       return iter->last_visited;
+   }
+
+   do
+   {
+       if (iter->last_visited->right != RBNIL)
+       {
+           iter->last_visited = iter->last_visited->right;
+           break;
+       }
+
+       /* go up and one step right */
+       for (;;)
+       {
+           RBNode     *came_from = iter->last_visited;
+
+           iter->last_visited = iter->last_visited->parent;
+           if (iter->last_visited == NULL)
            {
-               node->iteratorState = SecondStepDone;
-               descend(node->left);
+               iter->is_over = true;
+               break;
            }
-           /* FALL THROUGH */
-       case SecondStepDone:
-           if (node->right != RBNIL)
+
+           if ((iter->last_visited->right != came_from) && (iter->last_visited->right != RBNIL))
            {
-               node->iteratorState = ThirdStepDone;
-               descend(node->right);
+               iter->last_visited = iter->last_visited->right;
+               return iter->last_visited;
            }
-           /* FALL THROUGH */
-       case ThirdStepDone:
-           if (node->parent)
-               ascend(node->parent);
-           break;
-       default:
-           elog(ERROR, "unrecognized rbtree node state: %d",
-                node->iteratorState);
+       }
    }
+   while (iter->last_visited != NULL);
 
-   return NULL;
+   return iter->last_visited;
 }
 
 static RBNode *
-rb_inverted_iterator(RBTree *rb)
+rb_inverted_iterator(RBTreeIterator *iter)
 {
-   RBNode     *node = rb->cur;
+   RBNode     *came_from;
 
-restart:
-   switch (node->iteratorState)
+loop:
+   switch ((InvertedWalkNextStep) iter->next_step)
    {
-       case InitialState:
-           if (node->left != RBNIL)
+           /* First call, begin from root */
+       case NextStepBegin:
+           iter->last_visited = iter->rb->root;
+           iter->next_step = NextStepLeft;
+           goto loop;
+
+       case NextStepLeft:
+           while (iter->last_visited->left != RBNIL)
+               iter->last_visited = iter->last_visited->left;
+
+           iter->next_step = NextStepRight;
+           goto loop;
+
+       case NextStepRight:
+           if (iter->last_visited->right != RBNIL)
            {
-               node->iteratorState = FirstStepDone;
-               descend(node->left);
+               iter->last_visited = iter->last_visited->right;
+               iter->next_step = NextStepLeft;
+               goto loop;
            }
-           /* FALL THROUGH */
-       case FirstStepDone:
-           if (node->right != RBNIL)
+           else    /* not moved - return current, then go up */
+               iter->next_step = NextStepUp;
+           break;
+
+       case NextStepUp:
+           for (;;)
            {
-               node->iteratorState = SecondStepDone;
-               descend(node->right);
+               came_from = iter->last_visited;
+               iter->last_visited = iter->last_visited->parent;
+               if (iter->last_visited == NULL)
+               {
+                   iter->is_over = true;
+                   break;      /* end of iteration */
+               }
+
+               if (came_from == iter->last_visited->right)
+               {
+                   /* return current, then continue to go up */
+                   break;
+               }
+
+               /* otherwise we came from the left */
+               iter->next_step = NextStepRight;
+               goto loop;
            }
-           /* FALL THROUGH */
-       case SecondStepDone:
-           node->iteratorState = ThirdStepDone;
-           return node;
-       case ThirdStepDone:
-           if (node->parent)
-               ascend(node->parent);
            break;
-       default:
-           elog(ERROR, "unrecognized rbtree node state: %d",
-                node->iteratorState);
    }
 
-   return NULL;
+   return iter->last_visited;
 }
 
 /*
@@ -827,33 +843,34 @@ restart:
  * returns NULL or the traversal stops being of interest.
  *
  * If the tree is changed during traversal, results of further calls to
- * rb_iterate are unspecified.
+ * rb_iterate are unspecified.  Multiple concurrent iterators on the same
+ * tree are allowed.
  *
- * Note: this used to return a separately palloc'd iterator control struct,
- * but that's a bit pointless since the data structure is incapable of
- * supporting multiple concurrent traversals.  Now we just keep the state
- * in RBTree.
+ * The iterator state is stored in the 'iter' struct.  The caller should
+ * treat it as opaque struct.
  */
 void
-rb_begin_iterate(RBTree *rb, RBOrderControl ctrl)
+rb_begin_iterate(RBTree *rb, RBOrderControl ctrl, RBTreeIterator *iter)
 {
-   rb->cur = rb->root;
-   if (rb->cur != RBNIL)
-       rb->cur->iteratorState = InitialState;
+   /* Common initialization for all traversal orders */
+   iter->rb = rb;
+   iter->last_visited = NULL;
+   iter->is_over = (rb->root == RBNIL);
 
    switch (ctrl)
    {
        case LeftRightWalk:     /* visit left, then self, then right */
-           rb->iterate = rb_left_right_iterator;
+           iter->iterate = rb_left_right_iterator;
            break;
        case RightLeftWalk:     /* visit right, then self, then left */
-           rb->iterate = rb_right_left_iterator;
+           iter->iterate = rb_right_left_iterator;
            break;
        case DirectWalk:        /* visit self, then left, then right */
-           rb->iterate = rb_direct_iterator;
+           iter->iterate = rb_direct_iterator;
            break;
        case InvertedWalk:      /* visit left, then right, then self */
-           rb->iterate = rb_inverted_iterator;
+           iter->iterate = rb_inverted_iterator;
+           iter->next_step = NextStepBegin;
            break;
        default:
            elog(ERROR, "unrecognized rbtree iteration order: %d", ctrl);
@@ -864,10 +881,10 @@ rb_begin_iterate(RBTree *rb, RBOrderControl ctrl)
  * rb_iterate: return the next node in traversal order, or NULL if no more
  */
 RBNode *
-rb_iterate(RBTree *rb)
+rb_iterate(RBTreeIterator *iter)
 {
-   if (rb->cur == RBNIL)
+   if (iter->is_over)
        return NULL;
 
-   return rb->iterate(rb);
+   return iter->iterate(iter);
 }
index 68cfe0c1ac0cf349da6f98801a3aa513c323d8bb..bf589ab7a1aedd068c7ea85f045e46474c4229f8 100644 (file)
@@ -919,6 +919,7 @@ typedef struct
    GinEntryAccumulator *entryallocator;
    uint32      eas_used;
    RBTree     *tree;
+   RBTreeIterator tree_walk;
 } BuildAccumulator;
 
 extern void ginInitBA(BuildAccumulator *accum);
index 73e83c367a198732281967d54957750921b7ed27..4537c587ac203b1978108058713e81497e110335 100644 (file)
@@ -22,7 +22,6 @@
  */
 typedef struct RBNode
 {
-   char        iteratorState;  /* workspace for iterating through tree */
    char color;                 /* node's current color, red or black */
    struct RBNode *left;        /* left child, or RBNIL if none */
    struct RBNode *right;       /* right child, or RBNIL if none */
@@ -41,6 +40,22 @@ typedef enum RBOrderControl
    InvertedWalk                /* postorder: left child, right child, node */
 } RBOrderControl;
 
+/*
+ * RBTreeIterator holds state while traversing a tree.  This is declared
+ * here so that callers can stack-allocate this, but must otherwise be
+ * treated as an opaque struct.
+ */
+typedef struct RBTreeIterator RBTreeIterator;
+
+struct RBTreeIterator
+{
+   RBTree     *rb;
+   RBNode     *(*iterate) (RBTreeIterator *iter);
+   RBNode     *last_visited;
+   char        next_step;
+   bool        is_over;
+};
+
 /* Support functions to be provided by caller */
 typedef int (*rb_comparator) (const RBNode *a, const RBNode *b, void *arg);
 typedef void (*rb_combiner) (RBNode *existing, const RBNode *newdata, void *arg);
@@ -60,7 +75,8 @@ extern RBNode *rb_leftmost(RBTree *rb);
 extern RBNode *rb_insert(RBTree *rb, const RBNode *data, bool *isNew);
 extern void rb_delete(RBTree *rb, RBNode *node);
 
-extern void rb_begin_iterate(RBTree *rb, RBOrderControl ctrl);
-extern RBNode *rb_iterate(RBTree *rb);
+extern void rb_begin_iterate(RBTree *rb, RBOrderControl ctrl,
+                RBTreeIterator *iter);
+extern RBNode *rb_iterate(RBTreeIterator *iter);
 
 #endif   /* RBTREE_H */