1/*--------------------------------------------------------------------------
4 * Test correctness of red-black tree operations.
6 * Copyright (c) 2009-2025, PostgreSQL Global Development Group
9 * src/test/modules/test_rbtree/test_rbtree.c
11 * -------------------------------------------------------------------------
25 * Our test trees store an integer key, and nothing else.
35 * Node comparator. We don't worry about overflow in the subtraction,
36 * since none of our test keys are negative.
48 * Node combiner. For testing purposes, just check that library doesn't
49 * try to combine unequal keys.
57 if (eexist->
key != enew->
key)
58 elog(
ERROR,
"red-black tree combines %d into %d",
77 * Create a red-black tree using our support functions
91 * Generate a random permutation of the integers 0..size-1
99 permutation = (
int *)
palloc(size *
sizeof(
int));
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.
110 for (
i = 1;
i < size;
i++)
114 if (
j <
i)
/* avoid fetching undefined data if j=i */
115 permutation[
i] = permutation[
j];
123 * Populate an empty RBTree with "size" integers having the values
124 * 0, step, 2*step, 3*step, ..., inserting them in random order
134 /* Insert values. We don't expect any collisions. */
135 for (
i = 0;
i < size;
i++)
137 node.
key = step * permutation[
i];
140 elog(
ERROR,
"unexpected !isNew result from rbt_insert");
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.
149 node.
key = step * permutation[0];
152 elog(
ERROR,
"unexpected isNew result from rbt_insert");
159 * Check the correctness of left-right traversal.
160 * Left-right traversal is correct if all elements are
161 * visited in increasing order.
172 /* check iteration over empty tree */
175 elog(
ERROR,
"left-right walk over empty tree produced an element");
177 /* fill tree with consecutive natural numbers */
180 /* iterate over the tree */
185 /* check that order is increasing */
186 if (node->
key <= lastKey)
187 elog(
ERROR,
"left-right walk gives elements not in sorted order");
192 if (lastKey != size - 1)
193 elog(
ERROR,
"left-right walk did not reach end");
195 elog(
ERROR,
"left-right walk missed some elements");
199 * Check the correctness of right-left traversal.
200 * Right-left traversal is correct if all elements are
201 * visited in decreasing order.
212 /* check iteration over empty tree */
215 elog(
ERROR,
"right-left walk over empty tree produced an element");
217 /* fill tree with consecutive natural numbers */
220 /* iterate over the tree */
225 /* check that order is decreasing */
226 if (node->
key >= lastKey)
227 elog(
ERROR,
"right-left walk gives elements not in sorted order");
233 elog(
ERROR,
"right-left walk did not reach end");
235 elog(
ERROR,
"right-left walk missed some elements");
239 * Check the correctness of the rbt_find operation by searching for
240 * both elements we inserted and elements we didn't.
248 /* Insert even integers from 0 to 2 * (size-1) */
251 /* Check that all inserted elements can be found */
252 for (
i = 0;
i < size;
i++)
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");
266 * Check that not-inserted elements can not be found, being sure to try
267 * values before the first and after the last element.
269 for (
i = -1;
i <= 2 * size;
i += 2)
276 if (resultNode != NULL)
277 elog(
ERROR,
"not-inserted element was found");
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
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
302 /* Insert natural numbers */
306 * Since the search key is included in the naturals of the tree, we're
307 * sure to find an equal match
312 if (lteNode == NULL || lteNode->
key != searchNode.
key)
313 elog(
ERROR,
"rbt_find_less() didn't find the equal key");
315 if (gteNode == NULL || gteNode->
key != searchNode.
key)
316 elog(
ERROR,
"rbt_find_great() didn't find the equal key");
318 if (lteNode != gteNode)
319 elog(
ERROR,
"rbt_find_less() and rbt_find_great() found different equal keys");
321 /* Find the rest of the naturals lesser than the search key */
323 for (; searchNode.
key > 0; searchNode.
key--)
326 * Find the next key. If the current key is deleted, we can pass
327 * equal_match == true and still find the next one.
332 /* ensure we find a lesser match */
333 if (!node || !(node->
key < searchNode.
key))
334 elog(
ERROR,
"rbt_find_less() didn't find a lesser key");
336 /* randomly delete the found key or leave it */
342 /* Find the rest of the naturals greater than the search key */
344 for (searchNode.
key = randomKey; searchNode.
key < size - 1; searchNode.
key++)
347 * Find the next key. If the current key is deleted, we can pass
348 * equal_match == true and still find the next one.
353 /* ensure we find a greater match */
354 if (!node || !(node->
key > searchNode.
key))
355 elog(
ERROR,
"rbt_find_great() didn't find a greater key");
357 /* randomly delete the found key or leave it */
363 /* Check out of bounds searches find nothing */
367 elog(
ERROR,
"rbt_find_less() found non-inserted element");
371 elog(
ERROR,
"rbt_find_less() found non-inserted element");
372 searchNode.
key = size;
375 elog(
ERROR,
"rbt_find_great() found non-inserted element");
376 searchNode.
key = size - 1;
379 elog(
ERROR,
"rbt_find_great() found non-inserted element");
383 * Check the correctness of the rbt_leftmost operation.
384 * This operation should always return the smallest element of the tree.
392 /* Check that empty tree has no leftmost element */
394 elog(
ERROR,
"leftmost node of empty tree is not NULL");
396 /* fill tree with consecutive natural numbers */
399 /* Check that leftmost element is the smallest one */
401 if (result == NULL || result->
key != 0)
402 elog(
ERROR,
"rbt_leftmost gave wrong result");
406 * Check the correctness of the rbt_delete operation.
416 /* fill tree with consecutive natural numbers */
419 /* Choose unique ids to delete */
420 deleteIds = (
int *)
palloc(delsize *
sizeof(
int));
421 chosen = (
bool *)
palloc0(size *
sizeof(
bool));
423 for (
i = 0;
i < delsize;
i++)
433 /* Delete elements */
434 for (
i = 0;
i < delsize;
i++)
439 find.key = deleteIds[
i];
440 /* Locate the node to be deleted */
442 if (node == NULL || node->
key != deleteIds[
i])
443 elog(
ERROR,
"expected element was not found during deleting");
448 /* Check that deleted elements are deleted */
449 for (
i = 0;
i < size;
i++)
458 /* Deleted element should be absent */
460 elog(
ERROR,
"deleted element still present in the rbtree");
464 /* Else it should be present */
465 if (result == NULL || result->
key !=
i)
466 elog(
ERROR,
"delete operation removed wrong rbtree value");
470 /* Delete remaining elements, so as to exercise reducing tree to empty */
471 for (
i = 0;
i < size;
i++)
479 /* Locate the node to be deleted */
481 if (node == NULL || node->
key !=
i)
482 elog(
ERROR,
"expected element was not found during deleting");
487 /* Tree should now be empty */
489 elog(
ERROR,
"deleting all elements failed");
496 * SQL-callable entry point to perform all tests
498 * Argument is the number of entries to put in the trees
508 elog(
ERROR,
"invalid size for test_rb_tree: %d", size);
#define PG_GETARG_INT32(n)
void pfree(void *pointer)
void * palloc0(Size size)
uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
pg_prng_state pg_global_prng_state
RBTNode * rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match)
RBTNode * rbt_iterate(RBTreeIterator *iter)
RBTNode * rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew)
void rbt_begin_iterate(RBTree *rbt, RBTOrderControl ctrl, RBTreeIterator *iter)
RBTNode * rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match)
RBTree * rbt_create(Size node_size, rbt_comparator comparator, rbt_combiner combiner, rbt_allocfunc allocfunc, rbt_freefunc freefunc, void *arg)
RBTNode * rbt_find(RBTree *rbt, const RBTNode *data)
RBTNode * rbt_leftmost(RBTree *rbt)
void rbt_delete(RBTree *rbt, RBTNode *node)
static int find(struct vars *v, struct cnfa *cnfa, struct colormap *cm)
static int * GetPermutation(int size)
static RBTNode * irbt_alloc(void *arg)
Datum test_rb_tree(PG_FUNCTION_ARGS)
static void irbt_free(RBTNode *node, void *arg)
static void testleftright(int size)
static void irbt_combine(RBTNode *existing, const RBTNode *newdata, void *arg)
PG_FUNCTION_INFO_V1(test_rb_tree)
static void rbt_populate(RBTree *tree, int size, int step)
static void testleftmost(int size)
static void testdelete(int size, int delsize)
static void testfindltgt(int size)
struct IntRBTreeNode IntRBTreeNode
static int irbt_cmp(const RBTNode *a, const RBTNode *b, void *arg)
static RBTree * create_int_rbtree(void)
static void testrightleft(int size)
static void testfind(int size)