PostgreSQL Source Code  git master
test_rbtree.c
Go to the documentation of this file.
1 /*--------------------------------------------------------------------------
2  *
3  * test_rbtree.c
4  * Test correctness of red-black tree operations.
5  *
6  * Copyright (c) 2009-2022, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  * src/test/modules/test_rbtree/test_rbtree.c
10  *
11  * -------------------------------------------------------------------------
12  */
13 
14 #include "postgres.h"
15 
16 #include "common/pg_prng.h"
17 #include "fmgr.h"
18 #include "lib/rbtree.h"
19 #include "utils/memutils.h"
20 
22 
23 
24 /*
25  * Our test trees store an integer key, and nothing else.
26  */
27 typedef struct IntRBTreeNode
28 {
30  int key;
32 
33 
34 /*
35  * Node comparator. We don't worry about overflow in the subtraction,
36  * since none of our test keys are negative.
37  */
38 static int
39 irbt_cmp(const RBTNode *a, const RBTNode *b, void *arg)
40 {
41  const IntRBTreeNode *ea = (const IntRBTreeNode *) a;
42  const IntRBTreeNode *eb = (const IntRBTreeNode *) b;
43 
44  return ea->key - eb->key;
45 }
46 
47 /*
48  * Node combiner. For testing purposes, just check that library doesn't
49  * try to combine unequal keys.
50  */
51 static void
52 irbt_combine(RBTNode *existing, const RBTNode *newdata, void *arg)
53 {
54  const IntRBTreeNode *eexist = (const IntRBTreeNode *) existing;
55  const IntRBTreeNode *enew = (const IntRBTreeNode *) newdata;
56 
57  if (eexist->key != enew->key)
58  elog(ERROR, "red-black tree combines %d into %d",
59  enew->key, eexist->key);
60 }
61 
62 /* Node allocator */
63 static RBTNode *
65 {
66  return (RBTNode *) palloc(sizeof(IntRBTreeNode));
67 }
68 
69 /* Node freer */
70 static void
71 irbt_free(RBTNode *node, void *arg)
72 {
73  pfree(node);
74 }
75 
76 /*
77  * Create a red-black tree using our support functions
78  */
79 static RBTree *
81 {
82  return rbt_create(sizeof(IntRBTreeNode),
83  irbt_cmp,
85  irbt_alloc,
86  irbt_free,
87  NULL);
88 }
89 
90 /*
91  * Generate a random permutation of the integers 0..size-1
92  */
93 static int *
94 GetPermutation(int size)
95 {
96  int *permutation;
97  int i;
98 
99  permutation = (int *) palloc(size * sizeof(int));
100 
101  permutation[0] = 0;
102 
103  /*
104  * This is the "inside-out" variant of the Fisher-Yates shuffle algorithm.
105  * Notionally, we append each new value to the array and then swap it with
106  * a randomly-chosen array element (possibly including itself, else we
107  * fail to generate permutations with the last integer last). The swap
108  * step can be optimized by combining it with the insertion.
109  */
110  for (i = 1; i < size; i++)
111  {
113 
114  if (j < i) /* avoid fetching undefined data if j=i */
115  permutation[i] = permutation[j];
116  permutation[j] = i;
117  }
118 
119  return permutation;
120 }
121 
122 /*
123  * Populate an empty RBTree with "size" integers having the values
124  * 0, step, 2*step, 3*step, ..., inserting them in random order
125  */
126 static void
127 rbt_populate(RBTree *tree, int size, int step)
128 {
129  int *permutation = GetPermutation(size);
130  IntRBTreeNode node;
131  bool isNew;
132  int i;
133 
134  /* Insert values. We don't expect any collisions. */
135  for (i = 0; i < size; i++)
136  {
137  node.key = step * permutation[i];
138  rbt_insert(tree, (RBTNode *) &node, &isNew);
139  if (!isNew)
140  elog(ERROR, "unexpected !isNew result from rbt_insert");
141  }
142 
143  /*
144  * Re-insert the first value to make sure collisions work right. It's
145  * probably not useful to test that case over again for all the values.
146  */
147  if (size > 0)
148  {
149  node.key = step * permutation[0];
150  rbt_insert(tree, (RBTNode *) &node, &isNew);
151  if (isNew)
152  elog(ERROR, "unexpected isNew result from rbt_insert");
153  }
154 
155  pfree(permutation);
156 }
157 
158 /*
159  * Check the correctness of left-right traversal.
160  * Left-right traversal is correct if all elements are
161  * visited in increasing order.
162  */
163 static void
164 testleftright(int size)
165 {
166  RBTree *tree = create_int_rbtree();
167  IntRBTreeNode *node;
168  RBTreeIterator iter;
169  int lastKey = -1;
170  int count = 0;
171 
172  /* check iteration over empty tree */
173  rbt_begin_iterate(tree, LeftRightWalk, &iter);
174  if (rbt_iterate(&iter) != NULL)
175  elog(ERROR, "left-right walk over empty tree produced an element");
176 
177  /* fill tree with consecutive natural numbers */
178  rbt_populate(tree, size, 1);
179 
180  /* iterate over the tree */
181  rbt_begin_iterate(tree, LeftRightWalk, &iter);
182 
183  while ((node = (IntRBTreeNode *) rbt_iterate(&iter)) != NULL)
184  {
185  /* check that order is increasing */
186  if (node->key <= lastKey)
187  elog(ERROR, "left-right walk gives elements not in sorted order");
188  lastKey = node->key;
189  count++;
190  }
191 
192  if (lastKey != size - 1)
193  elog(ERROR, "left-right walk did not reach end");
194  if (count != size)
195  elog(ERROR, "left-right walk missed some elements");
196 }
197 
198 /*
199  * Check the correctness of right-left traversal.
200  * Right-left traversal is correct if all elements are
201  * visited in decreasing order.
202  */
203 static void
204 testrightleft(int size)
205 {
206  RBTree *tree = create_int_rbtree();
207  IntRBTreeNode *node;
208  RBTreeIterator iter;
209  int lastKey = size;
210  int count = 0;
211 
212  /* check iteration over empty tree */
213  rbt_begin_iterate(tree, RightLeftWalk, &iter);
214  if (rbt_iterate(&iter) != NULL)
215  elog(ERROR, "right-left walk over empty tree produced an element");
216 
217  /* fill tree with consecutive natural numbers */
218  rbt_populate(tree, size, 1);
219 
220  /* iterate over the tree */
221  rbt_begin_iterate(tree, RightLeftWalk, &iter);
222 
223  while ((node = (IntRBTreeNode *) rbt_iterate(&iter)) != NULL)
224  {
225  /* check that order is decreasing */
226  if (node->key >= lastKey)
227  elog(ERROR, "right-left walk gives elements not in sorted order");
228  lastKey = node->key;
229  count++;
230  }
231 
232  if (lastKey != 0)
233  elog(ERROR, "right-left walk did not reach end");
234  if (count != size)
235  elog(ERROR, "right-left walk missed some elements");
236 }
237 
238 /*
239  * Check the correctness of the rbt_find operation by searching for
240  * both elements we inserted and elements we didn't.
241  */
242 static void
243 testfind(int size)
244 {
245  RBTree *tree = create_int_rbtree();
246  int i;
247 
248  /* Insert even integers from 0 to 2 * (size-1) */
249  rbt_populate(tree, size, 2);
250 
251  /* Check that all inserted elements can be found */
252  for (i = 0; i < size; i++)
253  {
254  IntRBTreeNode node;
255  IntRBTreeNode *resultNode;
256 
257  node.key = 2 * i;
258  resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
259  if (resultNode == NULL)
260  elog(ERROR, "inserted element was not found");
261  if (node.key != resultNode->key)
262  elog(ERROR, "find operation in rbtree gave wrong result");
263  }
264 
265  /*
266  * Check that not-inserted elements can not be found, being sure to try
267  * values before the first and after the last element.
268  */
269  for (i = -1; i <= 2 * size; i += 2)
270  {
271  IntRBTreeNode node;
272  IntRBTreeNode *resultNode;
273 
274  node.key = i;
275  resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
276  if (resultNode != NULL)
277  elog(ERROR, "not-inserted element was found");
278  }
279 }
280 
281 /*
282  * Check the correctness of the rbt_leftmost operation.
283  * This operation should always return the smallest element of the tree.
284  */
285 static void
286 testleftmost(int size)
287 {
288  RBTree *tree = create_int_rbtree();
289  IntRBTreeNode *result;
290 
291  /* Check that empty tree has no leftmost element */
292  if (rbt_leftmost(tree) != NULL)
293  elog(ERROR, "leftmost node of empty tree is not NULL");
294 
295  /* fill tree with consecutive natural numbers */
296  rbt_populate(tree, size, 1);
297 
298  /* Check that leftmost element is the smallest one */
299  result = (IntRBTreeNode *) rbt_leftmost(tree);
300  if (result == NULL || result->key != 0)
301  elog(ERROR, "rbt_leftmost gave wrong result");
302 }
303 
304 /*
305  * Check the correctness of the rbt_delete operation.
306  */
307 static void
308 testdelete(int size, int delsize)
309 {
310  RBTree *tree = create_int_rbtree();
311  int *deleteIds;
312  bool *chosen;
313  int i;
314 
315  /* fill tree with consecutive natural numbers */
316  rbt_populate(tree, size, 1);
317 
318  /* Choose unique ids to delete */
319  deleteIds = (int *) palloc(delsize * sizeof(int));
320  chosen = (bool *) palloc0(size * sizeof(bool));
321 
322  for (i = 0; i < delsize; i++)
323  {
324  int k = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
325 
326  while (chosen[k])
327  k = (k + 1) % size;
328  deleteIds[i] = k;
329  chosen[k] = true;
330  }
331 
332  /* Delete elements */
333  for (i = 0; i < delsize; i++)
334  {
336  IntRBTreeNode *node;
337 
338  find.key = deleteIds[i];
339  /* Locate the node to be deleted */
340  node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
341  if (node == NULL || node->key != deleteIds[i])
342  elog(ERROR, "expected element was not found during deleting");
343  /* Delete it */
344  rbt_delete(tree, (RBTNode *) node);
345  }
346 
347  /* Check that deleted elements are deleted */
348  for (i = 0; i < size; i++)
349  {
350  IntRBTreeNode node;
351  IntRBTreeNode *result;
352 
353  node.key = i;
354  result = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
355  if (chosen[i])
356  {
357  /* Deleted element should be absent */
358  if (result != NULL)
359  elog(ERROR, "deleted element still present in the rbtree");
360  }
361  else
362  {
363  /* Else it should be present */
364  if (result == NULL || result->key != i)
365  elog(ERROR, "delete operation removed wrong rbtree value");
366  }
367  }
368 
369  /* Delete remaining elements, so as to exercise reducing tree to empty */
370  for (i = 0; i < size; i++)
371  {
373  IntRBTreeNode *node;
374 
375  if (chosen[i])
376  continue;
377  find.key = i;
378  /* Locate the node to be deleted */
379  node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
380  if (node == NULL || node->key != i)
381  elog(ERROR, "expected element was not found during deleting");
382  /* Delete it */
383  rbt_delete(tree, (RBTNode *) node);
384  }
385 
386  /* Tree should now be empty */
387  if (rbt_leftmost(tree) != NULL)
388  elog(ERROR, "deleting all elements failed");
389 
390  pfree(deleteIds);
391  pfree(chosen);
392 }
393 
394 /*
395  * SQL-callable entry point to perform all tests
396  *
397  * Argument is the number of entries to put in the trees
398  */
400 
401 Datum
403 {
404  int size = PG_GETARG_INT32(0);
405 
406  if (size <= 0 || size > MaxAllocSize / sizeof(int))
407  elog(ERROR, "invalid size for test_rb_tree: %d", size);
408  testleftright(size);
409  testrightleft(size);
410  testfind(size);
411  testleftmost(size);
412  testdelete(size, Max(size / 10, 1));
413  PG_RETURN_VOID();
414 }
#define Max(x, y)
Definition: c.h:980
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
int b
Definition: isn.c:70
int a
Definition: isn.c:69
int j
Definition: isn.c:74
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1175
void * palloc0(Size size)
Definition: mcxt.c:1099
void * palloc(Size size)
Definition: mcxt.c:1068
#define MaxAllocSize
Definition: memutils.h:40
void * arg
uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
Definition: pg_prng.c:138
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:28
uintptr_t Datum
Definition: postgres.h:411
RBTNode * rbt_iterate(RBTreeIterator *iter)
Definition: rbtree.c:764
RBTNode * rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew)
Definition: rbtree.c:391
void rbt_begin_iterate(RBTree *rbt, RBTOrderControl ctrl, RBTreeIterator *iter)
Definition: rbtree.c:740
RBTree * rbt_create(Size node_size, rbt_comparator comparator, rbt_combiner combiner, rbt_allocfunc allocfunc, rbt_freefunc freefunc, void *arg)
Definition: rbtree.c:102
RBTNode * rbt_leftmost(RBTree *rbt)
Definition: rbtree.c:173
void rbt_delete(RBTree *rbt, RBTNode *node)
Definition: rbtree.c:633
RBTNode * rbt_find(RBTree *rbt, const RBTNode *data)
Definition: rbtree.c:145
@ RightLeftWalk
Definition: rbtree.h:38
@ LeftRightWalk
Definition: rbtree.h:37
static int find(struct vars *, struct cnfa *, struct colormap *)
Definition: regexec.c:410
RBTNode rbtnode
Definition: test_rbtree.c:29
Definition: rbtree.h:24
Definition: rbtree.c:42
Datum test_rb_tree(PG_FUNCTION_ARGS)
Definition: test_rbtree.c:402
PG_MODULE_MAGIC
Definition: test_rbtree.c:21
static void irbt_free(RBTNode *node, void *arg)
Definition: test_rbtree.c:71
static int * GetPermutation(int size)
Definition: test_rbtree.c:94
static void testleftright(int size)
Definition: test_rbtree.c:164
static void irbt_combine(RBTNode *existing, const RBTNode *newdata, void *arg)
Definition: test_rbtree.c:52
PG_FUNCTION_INFO_V1(test_rb_tree)
static void rbt_populate(RBTree *tree, int size, int step)
Definition: test_rbtree.c:127
static RBTree * create_int_rbtree(void)
Definition: test_rbtree.c:80
static void testleftmost(int size)
Definition: test_rbtree.c:286
static void testdelete(int size, int delsize)
Definition: test_rbtree.c:308
struct IntRBTreeNode IntRBTreeNode
static int irbt_cmp(const RBTNode *a, const RBTNode *b, void *arg)
Definition: test_rbtree.c:39
static RBTNode * irbt_alloc(void *arg)
Definition: test_rbtree.c:64
static void testrightleft(int size)
Definition: test_rbtree.c:204
static void testfind(int size)
Definition: test_rbtree.c:243