LCOV - differential code coverage report
Current view: top level - src/test/modules/test_rbtree - test_rbtree.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 84.5 % 200 169 9 2 6 14 6 28 38 97 11 70
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 16 16 4 1 11 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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                 : 
      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
      39         1337692 : irbt_cmp(const RBTNode *a, const RBTNode *b, void *arg)
      40                 : {
      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
      52               6 : irbt_combine(RBTNode *existing, const RBTNode *newdata, void *arg)
      53                 : {
      54               6 :     const IntRBTreeNode *eexist = (const IntRBTreeNode *) existing;
      55               6 :     const IntRBTreeNode *enew = (const IntRBTreeNode *) newdata;
      56                 : 
      57               6 :     if (eexist->key != enew->key)
      58 UBC           0 :         elog(ERROR, "red-black tree combines %d into %d",
      59                 :              enew->key, eexist->key);
      60 CBC           6 : }
      61                 : 
      62                 : /* Node allocator */
      63                 : static RBTNode *
      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                 : {
      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                 : {
      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 *
      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                 :     {
     112           59994 :         int         j = pg_prng_uint64_range(&pg_global_prng_state, 0, i);
     113                 : 
     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
     127               6 : rbt_populate(RBTree *tree, int size, int step)
     128                 : {
     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];
     138           60000 :         rbt_insert(tree, (RBTNode *) &node, &isNew);
     139           60000 :         if (!isNew)
     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                 :      */
     147 CBC           6 :     if (size > 0)
     148                 :     {
     149               6 :         node.key = step * permutation[0];
     150               6 :         rbt_insert(tree, (RBTNode *) &node, &isNew);
     151               6 :         if (isNew)
     152 UBC           0 :             elog(ERROR, "unexpected isNew result from rbt_insert");
     153                 :     }
     154                 : 
     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 */
     173               1 :     rbt_begin_iterate(tree, LeftRightWalk, &iter);
     174               1 :     if (rbt_iterate(&iter) != NULL)
     175 UBC           0 :         elog(ERROR, "left-right walk over empty tree produced an element");
     176                 : 
     177                 :     /* fill tree with consecutive natural numbers */
     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 */
     186           10000 :         if (node->key <= lastKey)
     187 UBC           0 :             elog(ERROR, "left-right walk gives elements not in sorted order");
     188 CBC       10000 :         lastKey = node->key;
     189           10000 :         count++;
     190                 :     }
     191                 : 
     192               1 :     if (lastKey != size - 1)
     193 UBC           0 :         elog(ERROR, "left-right walk did not reach end");
     194 CBC           1 :     if (count != size)
     195 UBC           0 :         elog(ERROR, "left-right walk missed some elements");
     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 */
     213               1 :     rbt_begin_iterate(tree, RightLeftWalk, &iter);
     214               1 :     if (rbt_iterate(&iter) != NULL)
     215 UBC           0 :         elog(ERROR, "right-left walk over empty tree produced an element");
     216                 : 
     217                 :     /* fill tree with consecutive natural numbers */
     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 */
     226           10000 :         if (node->key >= lastKey)
     227 UBC           0 :             elog(ERROR, "right-left walk gives elements not in sorted order");
     228 CBC       10000 :         lastKey = node->key;
     229           10000 :         count++;
     230                 :     }
     231                 : 
     232               1 :     if (lastKey != 0)
     233 UBC           0 :         elog(ERROR, "right-left walk did not reach end");
     234 CBC           1 :     if (count != size)
     235 UBC           0 :         elog(ERROR, "right-left walk missed some elements");
     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) */
     249               1 :     rbt_populate(tree, size, 2);
     250                 : 
     251                 :     /* Check that all inserted elements can be found */
     252           10001 :     for (i = 0; i < size; i++)
     253                 :     {
     254                 :         IntRBTreeNode node;
     255                 :         IntRBTreeNode *resultNode;
     256                 : 
     257           10000 :         node.key = 2 * i;
     258           10000 :         resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
     259           10000 :         if (resultNode == NULL)
     260 UBC           0 :             elog(ERROR, "inserted element was not found");
     261 CBC       10000 :         if (node.key != resultNode->key)
     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                 :      */
     269 CBC       10002 :     for (i = -1; i <= 2 * size; i += 2)
     270                 :     {
     271                 :         IntRBTreeNode node;
     272                 :         IntRBTreeNode *resultNode;
     273                 : 
     274           10001 :         node.key = i;
     275           10001 :         resultNode = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
     276           10001 :         if (resultNode != NULL)
     277 UBC           0 :             elog(ERROR, "not-inserted element was found");
     278                 :     }
     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
     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)
     313 UNC           0 :         elog(ERROR, "rbt_find_less() didn't find the equal key");
     314                 : 
     315 GNC           1 :     if (gteNode == NULL || gteNode->key != searchNode.key)
     316 UNC           0 :         elog(ERROR, "rbt_find_great() didn't find the equal key");
     317                 : 
     318 GNC           1 :     if (lteNode != gteNode)
     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 */
     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))
     334 UNC           0 :             elog(ERROR, "rbt_find_less() didn't find a lesser key");
     335                 : 
     336                 :         /* randomly delete the found key or leave it */
     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))
     355 UNC           0 :             elog(ERROR, "rbt_find_great() didn't find a greater key");
     356                 : 
     357                 :         /* randomly delete the found key or leave it */
     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)
     367 UNC           0 :         elog(ERROR, "rbt_find_less() found non-inserted element");
     368 GNC           1 :     searchNode.key = 0;
     369               1 :     node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, false);
     370               1 :     if (node != NULL)
     371 UNC           0 :         elog(ERROR, "rbt_find_less() found non-inserted element");
     372 GNC           1 :     searchNode.key = size;
     373               1 :     node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
     374               1 :     if (node != NULL)
     375 UNC           0 :         elog(ERROR, "rbt_find_great() found non-inserted element");
     376 GNC           1 :     searchNode.key = size - 1;
     377               1 :     node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, false);
     378               1 :     if (node != NULL)
     379 UNC           0 :         elog(ERROR, "rbt_find_great() found non-inserted element");
     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
     387 GIC           1 : testleftmost(int size)
     388 ECB             : {
     389 GIC           1 :     RBTree     *tree = create_int_rbtree();
     390 ECB             :     IntRBTreeNode *result;
     391                 : 
     392                 :     /* Check that empty tree has no leftmost element */
     393 GIC           1 :     if (rbt_leftmost(tree) != NULL)
     394 UIC           0 :         elog(ERROR, "leftmost node of empty tree is not NULL");
     395                 : 
     396 ECB             :     /* fill tree with consecutive natural numbers */
     397 GIC           1 :     rbt_populate(tree, size, 1);
     398 ECB             : 
     399                 :     /* Check that leftmost element is the smallest one */
     400 GIC           1 :     result = (IntRBTreeNode *) rbt_leftmost(tree);
     401               1 :     if (result == NULL || result->key != 0)
     402 UIC           0 :         elog(ERROR, "rbt_leftmost gave wrong result");
     403 GIC           1 : }
     404 ECB             : 
     405                 : /*
     406                 :  * Check the correctness of the rbt_delete operation.
     407                 :  */
     408                 : static void
     409 GIC           1 : testdelete(int size, int delsize)
     410 ECB             : {
     411 CBC           1 :     RBTree     *tree = create_int_rbtree();
     412                 :     int        *deleteIds;
     413 ECB             :     bool       *chosen;
     414 EUB             :     int         i;
     415                 : 
     416 ECB             :     /* fill tree with consecutive natural numbers */
     417 GBC           1 :     rbt_populate(tree, size, 1);
     418                 : 
     419 ECB             :     /* Choose unique ids to delete */
     420 GBC           1 :     deleteIds = (int *) palloc(delsize * sizeof(int));
     421 GIC           1 :     chosen = (bool *) palloc0(size * sizeof(bool));
     422                 : 
     423 CBC        1001 :     for (i = 0; i < delsize; i++)
     424 ECB             :     {
     425 GIC        1000 :         int         k = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
     426                 : 
     427            1042 :         while (chosen[k])
     428              42 :             k = (k + 1) % size;
     429            1000 :         deleteIds[i] = k;
     430 CBC        1000 :         chosen[k] = true;
     431                 :     }
     432                 : 
     433                 :     /* Delete elements */
     434            1001 :     for (i = 0; i < delsize; i++)
     435 EUB             :     {
     436                 :         IntRBTreeNode find;
     437                 :         IntRBTreeNode *node;
     438 ECB             : 
     439 CBC        1000 :         find.key = deleteIds[i];
     440 ECB             :         /* Locate the node to be deleted */
     441 GIC        1000 :         node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
     442            1000 :         if (node == NULL || node->key != deleteIds[i])
     443 UIC           0 :             elog(ERROR, "expected element was not found during deleting");
     444 ECB             :         /* Delete it */
     445 CBC        1000 :         rbt_delete(tree, (RBTNode *) node);
     446                 :     }
     447                 : 
     448                 :     /* Check that deleted elements are deleted */
     449 GIC       10001 :     for (i = 0; i < size; i++)
     450                 :     {
     451 ECB             :         IntRBTreeNode node;
     452                 :         IntRBTreeNode *result;
     453                 : 
     454 GIC       10000 :         node.key = i;
     455 CBC       10000 :         result = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &node);
     456 GBC       10000 :         if (chosen[i])
     457                 :         {
     458                 :             /* Deleted element should be absent */
     459 CBC        1000 :             if (result != NULL)
     460 LBC           0 :                 elog(ERROR, "deleted element still present in the rbtree");
     461 ECB             :         }
     462                 :         else
     463                 :         {
     464                 :             /* Else it should be present */
     465 CBC        9000 :             if (result == NULL || result->key != i)
     466 LBC           0 :                 elog(ERROR, "delete operation removed wrong rbtree value");
     467 ECB             :         }
     468 EUB             :     }
     469 ECB             : 
     470                 :     /* Delete remaining elements, so as to exercise reducing tree to empty */
     471 CBC       10001 :     for (i = 0; i < size; i++)
     472 EUB             :     {
     473 ECB             :         IntRBTreeNode find;
     474                 :         IntRBTreeNode *node;
     475                 : 
     476 GBC       10000 :         if (chosen[i])
     477 CBC        1000 :             continue;
     478            9000 :         find.key = i;
     479 ECB             :         /* Locate the node to be deleted */
     480 GBC        9000 :         node = (IntRBTreeNode *) rbt_find(tree, (RBTNode *) &find);
     481 CBC        9000 :         if (node == NULL || node->key != i)
     482 UIC           0 :             elog(ERROR, "expected element was not found during deleting");
     483                 :         /* Delete it */
     484 GIC        9000 :         rbt_delete(tree, (RBTNode *) node);
     485                 :     }
     486                 : 
     487                 :     /* Tree should now be empty */
     488 CBC           1 :     if (rbt_leftmost(tree) != NULL)
     489 UIC           0 :         elog(ERROR, "deleting all elements failed");
     490 ECB             : 
     491 GIC           1 :     pfree(deleteIds);
     492               1 :     pfree(chosen);
     493               1 : }
     494 ECB             : 
     495 EUB             : /*
     496                 :  * SQL-callable entry point to perform all tests
     497                 :  *
     498 ECB             :  * Argument is the number of entries to put in the trees
     499                 :  */
     500 GIC           2 : PG_FUNCTION_INFO_V1(test_rb_tree);
     501 ECB             : 
     502                 : Datum
     503 GBC           1 : test_rb_tree(PG_FUNCTION_ARGS)
     504 ECB             : {
     505 GIC           1 :     int         size = PG_GETARG_INT32(0);
     506                 : 
     507               1 :     if (size <= 0 || size > MaxAllocSize / sizeof(int))
     508 UIC           0 :         elog(ERROR, "invalid size for test_rb_tree: %d", size);
     509 GIC           1 :     testleftright(size);
     510 CBC           1 :     testrightleft(size);
     511 GIC           1 :     testfind(size);
     512 GNC           1 :     testfindltgt(size);
     513 CBC           1 :     testleftmost(size);
     514 GIC           1 :     testdelete(size, Max(size / 10, 1));
     515               1 :     PG_RETURN_VOID();
     516                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a