Age Owner TLA Line data Source code
1 : /*--------------------------------------------------------------------------
2 : *
3 : * test_rbtree.c
4 : * Test correctness of red-black tree operations.
5 : *
6 : * Copyright (c) 2009-2023, 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 :
2037 tgl 21 CBC 1 : PG_MODULE_MAGIC;
22 :
23 :
24 : /*
25 : * Our test trees store an integer key, and nothing else.
26 : */
27 : typedef struct IntRBTreeNode
28 : {
29 : RBTNode rbtnode;
30 : int key;
31 : } IntRBTreeNode;
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
1615 39 1337692 : irbt_cmp(const RBTNode *a, const RBTNode *b, void *arg)
40 : {
2037 41 1337692 : const IntRBTreeNode *ea = (const IntRBTreeNode *) a;
42 1337692 : const IntRBTreeNode *eb = (const IntRBTreeNode *) b;
43 :
44 1337692 : 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
1615 52 6 : irbt_combine(RBTNode *existing, const RBTNode *newdata, void *arg)
53 : {
2037 54 6 : const IntRBTreeNode *eexist = (const IntRBTreeNode *) existing;
55 6 : const IntRBTreeNode *enew = (const IntRBTreeNode *) newdata;
56 :
57 6 : if (eexist->key != enew->key)
2037 tgl 58 UBC 0 : elog(ERROR, "red-black tree combines %d into %d",
59 : enew->key, eexist->key);
2037 tgl 60 CBC 6 : }
61 :
62 : /* Node allocator */
63 : static RBTNode *
1615 64 60000 : irbt_alloc(void *arg)
65 : {
66 60000 : return (RBTNode *) palloc(sizeof(IntRBTreeNode));
67 : }
68 :
69 : /* Node freer */
70 : static void
71 14989 : irbt_free(RBTNode *node, void *arg)
72 : {
2037 73 14989 : pfree(node);
74 14989 : }
75 :
76 : /*
77 : * Create a red-black tree using our support functions
78 : */
79 : static RBTree *
80 6 : create_int_rbtree(void)
81 : {
1615 82 6 : return rbt_create(sizeof(IntRBTreeNode),
83 : irbt_cmp,
84 : irbt_combine,
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 *
2037 94 6 : GetPermutation(int size)
95 : {
96 : int *permutation;
97 : int i;
98 :
99 6 : permutation = (int *) palloc(size * sizeof(int));
100 :
101 6 : 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 60000 : for (i = 1; i < size; i++)
111 : {
497 112 59994 : int j = pg_prng_uint64_range(&pg_global_prng_state, 0, i);
113 :
2037 114 59994 : if (j < i) /* avoid fetching undefined data if j=i */
115 59939 : permutation[i] = permutation[j];
116 59994 : permutation[j] = i;
117 : }
118 :
119 6 : 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
1615 127 6 : rbt_populate(RBTree *tree, int size, int step)
128 : {
2037 129 6 : int *permutation = GetPermutation(size);
130 : IntRBTreeNode node;
131 : bool isNew;
132 : int i;
133 :
134 : /* Insert values. We don't expect any collisions. */
135 60006 : for (i = 0; i < size; i++)
136 : {
137 60000 : node.key = step * permutation[i];
1615 138 60000 : rbt_insert(tree, (RBTNode *) &node, &isNew);
2037 139 60000 : if (!isNew)
1615 tgl 140 UBC 0 : 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 : */
2037 tgl 147 CBC 6 : if (size > 0)
148 : {
149 6 : node.key = step * permutation[0];
1615 150 6 : rbt_insert(tree, (RBTNode *) &node, &isNew);
2037 151 6 : if (isNew)
1615 tgl 152 UBC 0 : elog(ERROR, "unexpected isNew result from rbt_insert");
153 : }
154 :
2037 tgl 155 CBC 6 : pfree(permutation);
156 6 : }
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 1 : testleftright(int size)
165 : {
166 1 : RBTree *tree = create_int_rbtree();
167 : IntRBTreeNode *node;
168 : RBTreeIterator iter;
169 1 : int lastKey = -1;
170 1 : int count = 0;
171 :
172 : /* check iteration over empty tree */
1615 173 1 : rbt_begin_iterate(tree, LeftRightWalk, &iter);
174 1 : if (rbt_iterate(&iter) != NULL)
2037 tgl 175 UBC 0 : elog(ERROR, "left-right walk over empty tree produced an element");
176 :
177 : /* fill tree with consecutive natural numbers */
1615 tgl 178 CBC 1 : rbt_populate(tree, size, 1);
179 :
180 : /* iterate over the tree */
181 1 : rbt_begin_iterate(tree, LeftRightWalk, &iter);
182 :
183 10001 : while ((node = (IntRBTreeNode *) rbt_iterate(&iter)) != NULL)
184 : {
185 : /* check that order is increasing */
2037 186 10000 : if (node->key <= lastKey)
2037 tgl 187 UBC 0 : elog(ERROR, "left-right walk gives elements not in sorted order");
2037 tgl 188 CBC 10000 : lastKey = node->key;
189 10000 : count++;
190 : }
191 :
192 1 : if (lastKey != size - 1)
2037 tgl 193 UBC 0 : elog(ERROR, "left-right walk did not reach end");
2037 tgl 194 CBC 1 : if (count != size)
2037 tgl 195 UBC 0 : elog(ERROR, "left-right walk missed some elements");
2037 tgl 196 CBC 1 : }
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 1 : testrightleft(int size)
205 : {
206 1 : RBTree *tree = create_int_rbtree();
207 : IntRBTreeNode *node;
208 : RBTreeIterator iter;
209 1 : int lastKey = size;
210 1 : int count = 0;
211 :
212 : /* check iteration over empty tree */
1615 213 1 : rbt_begin_iterate(tree, RightLeftWalk, &iter);
214 1 : if (rbt_iterate(&iter) != NULL)
2037 tgl 215 UBC 0 : elog(ERROR, "right-left walk over empty tree produced an element");
216 :
217 : /* fill tree with consecutive natural numbers */
1615 tgl 218 CBC 1 : rbt_populate(tree, size, 1);
219 :
220 : /* iterate over the tree */
221 1 : rbt_begin_iterate(tree, RightLeftWalk, &iter);
222 :
223 10001 : while ((node = (IntRBTreeNode *) rbt_iterate(&iter)) != NULL)
224 : {
225 : /* check that order is decreasing */
2037 226 10000 : if (node->key >= lastKey)
2037 tgl 227 UBC 0 : elog(ERROR, "right-left walk gives elements not in sorted order");
2037 tgl 228 CBC 10000 : lastKey = node->key;
229 10000 : count++;
230 : }
231 :
232 1 : if (lastKey != 0)
2037 tgl 233 UBC 0 : elog(ERROR, "right-left walk did not reach end");
2037 tgl 234 CBC 1 : if (count != size)
2037 tgl 235 UBC 0 : elog(ERROR, "right-left walk missed some elements");
2037 tgl 236 CBC 1 : }
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 1 : testfind(int size)
244 : {
245 1 : RBTree *tree = create_int_rbtree();
246 : int i;
247 :
248 : /* Insert even integers from 0 to 2 * (size-1) */
1615 249 1 : rbt_populate(tree, size, 2);
250 :
251 : /* Check that all inserted elements can be found */
2037 252 10001 : for (i = 0; i < size; i++)
253 : {
254 : IntRBTreeNode node;
255 : IntRBTreeNode *resultNode;
256 :
257 10000 : node.key = 2 * i;
1615 258 10000 : resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
2037 259 10000 : if (resultNode == NULL)
2037 tgl 260 UBC 0 : elog(ERROR, "inserted element was not found");
2037 tgl 261 CBC 10000 : if (node.key != resultNode->key)
2037 tgl 262 UBC 0 : 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 : */
2037 tgl 269 CBC 10002 : for (i = -1; i <= 2 * size; i += 2)
270 : {
271 : IntRBTreeNode node;
272 : IntRBTreeNode *resultNode;
273 :
274 10001 : node.key = i;
1615 275 10001 : resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
2037 276 10001 : if (resultNode != NULL)
2037 tgl 277 UBC 0 : elog(ERROR, "not-inserted element was found");
278 : }
2037 tgl 279 CBC 1 : }
280 :
281 : /*
282 : * Check the correctness of the rbt_find_less() and rbt_find_great() functions
283 : * by searching for an equal key and iterating the lesser keys then the greater
284 : * keys.
285 : */
286 : static void
275 akorotkov 287 GNC 1 : testfindltgt(int size)
288 : {
289 1 : RBTree *tree = create_int_rbtree();
290 :
291 : /*
292 : * Using the size as the random key to search wouldn't allow us to get at
293 : * least one greater match, so we do size - 1
294 : */
295 1 : int randomKey = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
296 : bool keyDeleted;
297 1 : IntRBTreeNode searchNode = {.key = randomKey};
298 : IntRBTreeNode *lteNode;
299 : IntRBTreeNode *gteNode;
300 : IntRBTreeNode *node;
301 :
302 : /* Insert natural numbers */
303 1 : rbt_populate(tree, size, 1);
304 :
305 : /*
306 : * Since the search key is included in the naturals of the tree, we're
307 : * sure to find an equal match
308 : */
309 1 : lteNode = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
310 1 : gteNode = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
311 :
312 1 : if (lteNode == NULL || lteNode->key != searchNode.key)
275 akorotkov 313 UNC 0 : elog(ERROR, "rbt_find_less() didn't find the equal key");
314 :
275 akorotkov 315 GNC 1 : if (gteNode == NULL || gteNode->key != searchNode.key)
275 akorotkov 316 UNC 0 : elog(ERROR, "rbt_find_great() didn't find the equal key");
317 :
275 akorotkov 318 GNC 1 : if (lteNode != gteNode)
275 akorotkov 319 UNC 0 : elog(ERROR, "rbt_find_less() and rbt_find_great() found different equal keys");
320 :
321 : /* Find the rest of the naturals lesser than the search key */
275 akorotkov 322 GNC 1 : keyDeleted = false;
323 6465 : for (; searchNode.key > 0; searchNode.key--)
324 : {
325 : /*
326 : * Find the next key. If the current key is deleted, we can pass
327 : * equal_match == true and still find the next one.
328 : */
329 6464 : node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode,
330 : keyDeleted);
331 :
332 : /* ensure we find a lesser match */
333 6464 : if (!node || !(node->key < searchNode.key))
275 akorotkov 334 UNC 0 : elog(ERROR, "rbt_find_less() didn't find a lesser key");
335 :
336 : /* randomly delete the found key or leave it */
275 akorotkov 337 GNC 6464 : keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
338 6464 : if (keyDeleted)
339 3247 : rbt_delete(tree, (RBTNode *) node);
340 : }
341 :
342 : /* Find the rest of the naturals greater than the search key */
343 1 : keyDeleted = false;
344 3536 : for (searchNode.key = randomKey; searchNode.key < size - 1; searchNode.key++)
345 : {
346 : /*
347 : * Find the next key. If the current key is deleted, we can pass
348 : * equal_match == true and still find the next one.
349 : */
350 3535 : node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode,
351 : keyDeleted);
352 :
353 : /* ensure we find a greater match */
354 3535 : if (!node || !(node->key > searchNode.key))
275 akorotkov 355 UNC 0 : elog(ERROR, "rbt_find_great() didn't find a greater key");
356 :
357 : /* randomly delete the found key or leave it */
275 akorotkov 358 GNC 3535 : keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
359 3535 : if (keyDeleted)
360 1742 : rbt_delete(tree, (RBTNode *) node);
361 : }
362 :
363 : /* Check out of bounds searches find nothing */
364 1 : searchNode.key = -1;
365 1 : node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
366 1 : if (node != NULL)
275 akorotkov 367 UNC 0 : elog(ERROR, "rbt_find_less() found non-inserted element");
275 akorotkov 368 GNC 1 : searchNode.key = 0;
369 1 : node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, false);
370 1 : if (node != NULL)
275 akorotkov 371 UNC 0 : elog(ERROR, "rbt_find_less() found non-inserted element");
275 akorotkov 372 GNC 1 : searchNode.key = size;
373 1 : node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
374 1 : if (node != NULL)
275 akorotkov 375 UNC 0 : elog(ERROR, "rbt_find_great() found non-inserted element");
275 akorotkov 376 GNC 1 : searchNode.key = size - 1;
377 1 : node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, false);
378 1 : if (node != NULL)
275 akorotkov 379 UNC 0 : elog(ERROR, "rbt_find_great() found non-inserted element");
275 akorotkov 380 GNC 1 : }
381 :
382 : /*
383 : * Check the correctness of the rbt_leftmost operation.
384 : * This operation should always return the smallest element of the tree.
385 : */
386 : static void
2037 tgl 387 GIC 1 : testleftmost(int size)
2037 tgl 388 ECB : {
2037 tgl 389 GIC 1 : RBTree *tree = create_int_rbtree();
2037 tgl 390 ECB : IntRBTreeNode *result;
391 :
392 : /* Check that empty tree has no leftmost element */
1615 tgl 393 GIC 1 : if (rbt_leftmost(tree) != NULL)
2037 tgl 394 UIC 0 : elog(ERROR, "leftmost node of empty tree is not NULL");
395 :
2037 tgl 396 ECB : /* fill tree with consecutive natural numbers */
1615 tgl 397 GIC 1 : rbt_populate(tree, size, 1);
2037 tgl 398 ECB :
399 : /* Check that leftmost element is the smallest one */
1615 tgl 400 GIC 1 : result = (IntRBTreeNode *) rbt_leftmost(tree);
2037 401 1 : if (result == NULL || result->key != 0)
1615 tgl 402 UIC 0 : elog(ERROR, "rbt_leftmost gave wrong result");
2037 tgl 403 GIC 1 : }
2037 tgl 404 ECB :
405 : /*
406 : * Check the correctness of the rbt_delete operation.
407 : */
408 : static void
2037 tgl 409 GIC 1 : testdelete(int size, int delsize)
2037 tgl 410 ECB : {
2037 tgl 411 CBC 1 : RBTree *tree = create_int_rbtree();
412 : int *deleteIds;
2037 tgl 413 ECB : bool *chosen;
2037 tgl 414 EUB : int i;
415 :
2037 tgl 416 ECB : /* fill tree with consecutive natural numbers */
1615 tgl 417 GBC 1 : rbt_populate(tree, size, 1);
418 :
2037 tgl 419 ECB : /* Choose unique ids to delete */
2037 tgl 420 GBC 1 : deleteIds = (int *) palloc(delsize * sizeof(int));
2037 tgl 421 GIC 1 : chosen = (bool *) palloc0(size * sizeof(bool));
422 :
2037 tgl 423 CBC 1001 : for (i = 0; i < delsize; i++)
2037 tgl 424 ECB : {
497 tgl 425 GIC 1000 : int k = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
426 :
2037 427 1042 : while (chosen[k])
428 42 : k = (k + 1) % size;
429 1000 : deleteIds[i] = k;
2037 tgl 430 CBC 1000 : chosen[k] = true;
431 : }
432 :
433 : /* Delete elements */
434 1001 : for (i = 0; i < delsize; i++)
2037 tgl 435 EUB : {
436 : IntRBTreeNode find;
437 : IntRBTreeNode *node;
2037 tgl 438 ECB :
2037 tgl 439 CBC 1000 : find.key = deleteIds[i];
2037 tgl 440 ECB : /* Locate the node to be deleted */
1615 tgl 441 GIC 1000 : node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
2037 442 1000 : if (node == NULL || node->key != deleteIds[i])
2037 tgl 443 UIC 0 : elog(ERROR, "expected element was not found during deleting");
2037 tgl 444 ECB : /* Delete it */
1615 tgl 445 CBC 1000 : rbt_delete(tree, (RBTNode *) node);
446 : }
447 :
448 : /* Check that deleted elements are deleted */
2037 tgl 449 GIC 10001 : for (i = 0; i < size; i++)
450 : {
2037 tgl 451 ECB : IntRBTreeNode node;
452 : IntRBTreeNode *result;
453 :
2037 tgl 454 GIC 10000 : node.key = i;
1615 tgl 455 CBC 10000 : result = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
2037 tgl 456 GBC 10000 : if (chosen[i])
457 : {
458 : /* Deleted element should be absent */
2037 tgl 459 CBC 1000 : if (result != NULL)
2037 tgl 460 LBC 0 : elog(ERROR, "deleted element still present in the rbtree");
2037 tgl 461 ECB : }
462 : else
463 : {
464 : /* Else it should be present */
2037 tgl 465 CBC 9000 : if (result == NULL || result->key != i)
2037 tgl 466 LBC 0 : elog(ERROR, "delete operation removed wrong rbtree value");
2037 tgl 467 ECB : }
2037 tgl 468 EUB : }
2037 tgl 469 ECB :
470 : /* Delete remaining elements, so as to exercise reducing tree to empty */
2037 tgl 471 CBC 10001 : for (i = 0; i < size; i++)
2037 tgl 472 EUB : {
2037 tgl 473 ECB : IntRBTreeNode find;
474 : IntRBTreeNode *node;
475 :
2037 tgl 476 GBC 10000 : if (chosen[i])
2037 tgl 477 CBC 1000 : continue;
478 9000 : find.key = i;
2037 tgl 479 ECB : /* Locate the node to be deleted */
1615 tgl 480 GBC 9000 : node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
2037 tgl 481 CBC 9000 : if (node == NULL || node->key != i)
2037 tgl 482 UIC 0 : elog(ERROR, "expected element was not found during deleting");
483 : /* Delete it */
1615 tgl 484 GIC 9000 : rbt_delete(tree, (RBTNode *) node);
485 : }
486 :
487 : /* Tree should now be empty */
1615 tgl 488 CBC 1 : if (rbt_leftmost(tree) != NULL)
2037 tgl 489 UIC 0 : elog(ERROR, "deleting all elements failed");
2037 tgl 490 ECB :
2037 tgl 491 GIC 1 : pfree(deleteIds);
492 1 : pfree(chosen);
493 1 : }
2037 tgl 494 ECB :
2037 tgl 495 EUB : /*
496 : * SQL-callable entry point to perform all tests
497 : *
2037 tgl 498 ECB : * Argument is the number of entries to put in the trees
499 : */
2037 tgl 500 GIC 2 : PG_FUNCTION_INFO_V1(test_rb_tree);
2037 tgl 501 ECB :
502 : Datum
2037 tgl 503 GBC 1 : test_rb_tree(PG_FUNCTION_ARGS)
2037 tgl 504 ECB : {
2037 tgl 505 GIC 1 : int size = PG_GETARG_INT32(0);
506 :
507 1 : if (size <= 0 || size > MaxAllocSize / sizeof(int))
2037 tgl 508 UIC 0 : elog(ERROR, "invalid size for test_rb_tree: %d", size);
2037 tgl 509 GIC 1 : testleftright(size);
2037 tgl 510 CBC 1 : testrightleft(size);
2037 tgl 511 GIC 1 : testfind(size);
275 akorotkov 512 GNC 1 : testfindltgt(size);
2037 tgl 513 CBC 1 : testleftmost(size);
2037 tgl 514 GIC 1 : testdelete(size, Max(size / 10, 1));
515 1 : PG_RETURN_VOID();
516 : }
|