mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
Add missing inequality searches to rbtree
PostgreSQL contains the implementation of the red-black tree. The red-black tree is the ordered data structure, and one of its advantages is the ability to do inequality searches. This commit adds rbt_find_less() and rbt_find_great() functions implementing these searches. While these searches aren't yet used in the core code, they might be useful for extensions. Discussion: https://postgr.es/m/CAGRrpzYE8-7GCoaPjOiL9T_HY605MRax-2jgTtLq236uksZ1Sw%40mail.gmail.com Author: Steve Chavez, Alexander Korotkov Reviewed-by: Alexander Korotkov
This commit is contained in:
parent
8d51d7f403
commit
e57519a463
@ -161,6 +161,68 @@ rbt_find(RBTree *rbt, const RBTNode *data)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rbt_find_great: search for a greater value in an RBTree
|
||||||
|
*
|
||||||
|
* If equal_match is true, this will be a great or equal search.
|
||||||
|
*
|
||||||
|
* Returns the matching tree entry, or NULL if no match is found.
|
||||||
|
*/
|
||||||
|
RBTNode *
|
||||||
|
rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match)
|
||||||
|
{
|
||||||
|
RBTNode *node = rbt->root;
|
||||||
|
RBTNode *greater = NULL;
|
||||||
|
|
||||||
|
while (node != RBTNIL)
|
||||||
|
{
|
||||||
|
int cmp = rbt->comparator(data, node, rbt->arg);
|
||||||
|
|
||||||
|
if (equal_match && cmp == 0)
|
||||||
|
return node;
|
||||||
|
else if (cmp < 0)
|
||||||
|
{
|
||||||
|
greater = node;
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node = node->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return greater;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rbt_find_less: search for a lesser value in an RBTree
|
||||||
|
*
|
||||||
|
* If equal_match is true, this will be a less or equal search.
|
||||||
|
*
|
||||||
|
* Returns the matching tree entry, or NULL if no match is found.
|
||||||
|
*/
|
||||||
|
RBTNode *
|
||||||
|
rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match)
|
||||||
|
{
|
||||||
|
RBTNode *node = rbt->root;
|
||||||
|
RBTNode *lesser = NULL;
|
||||||
|
|
||||||
|
while (node != RBTNIL)
|
||||||
|
{
|
||||||
|
int cmp = rbt->comparator(data, node, rbt->arg);
|
||||||
|
|
||||||
|
if (equal_match && cmp == 0)
|
||||||
|
return node;
|
||||||
|
else if (cmp > 0)
|
||||||
|
{
|
||||||
|
lesser = node;
|
||||||
|
node = node->right;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lesser;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rbt_leftmost: fetch the leftmost (smallest-valued) tree node.
|
* rbt_leftmost: fetch the leftmost (smallest-valued) tree node.
|
||||||
* Returns NULL if tree is empty.
|
* Returns NULL if tree is empty.
|
||||||
|
@ -67,6 +67,8 @@ extern RBTree *rbt_create(Size node_size,
|
|||||||
void *arg);
|
void *arg);
|
||||||
|
|
||||||
extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
|
extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
|
||||||
|
extern RBTNode *rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match);
|
||||||
|
extern RBTNode *rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match);
|
||||||
extern RBTNode *rbt_leftmost(RBTree *rbt);
|
extern RBTNode *rbt_leftmost(RBTree *rbt);
|
||||||
|
|
||||||
extern RBTNode *rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew);
|
extern RBTNode *rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew);
|
||||||
|
@ -278,6 +278,107 @@ testfind(int size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the correctness of the rbt_find_less() and rbt_find_great() functions
|
||||||
|
* by searching for an equal key and iterating the lesser keys then the greater
|
||||||
|
* keys.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
testfindltgt(int size)
|
||||||
|
{
|
||||||
|
RBTree *tree = create_int_rbtree();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using the size as the random key to search wouldn't allow us to get at
|
||||||
|
* least one greater match, so we do size - 1
|
||||||
|
*/
|
||||||
|
int randomKey = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
|
||||||
|
bool keyDeleted;
|
||||||
|
IntRBTreeNode searchNode = {.key = randomKey};
|
||||||
|
IntRBTreeNode *lteNode;
|
||||||
|
IntRBTreeNode *gteNode;
|
||||||
|
IntRBTreeNode *node;
|
||||||
|
|
||||||
|
/* Insert natural numbers */
|
||||||
|
rbt_populate(tree, size, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since the search key is included in the naturals of the tree, we're
|
||||||
|
* sure to find an equal match
|
||||||
|
*/
|
||||||
|
lteNode = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
|
||||||
|
gteNode = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
|
||||||
|
|
||||||
|
if (lteNode == NULL || lteNode->key != searchNode.key)
|
||||||
|
elog(ERROR, "rbt_find_less() didn't find the equal key");
|
||||||
|
|
||||||
|
if (gteNode == NULL || gteNode->key != searchNode.key)
|
||||||
|
elog(ERROR, "rbt_find_great() didn't find the equal key");
|
||||||
|
|
||||||
|
if (lteNode != gteNode)
|
||||||
|
elog(ERROR, "rbt_find_less() and rbt_find_great() found different equal keys");
|
||||||
|
|
||||||
|
/* Find the rest of the naturals lesser than the search key */
|
||||||
|
keyDeleted = false;
|
||||||
|
for (; searchNode.key > 0; searchNode.key--)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Find the next key. If the current key is deleted, we can pass
|
||||||
|
* equal_match == true and still find the next one.
|
||||||
|
*/
|
||||||
|
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode,
|
||||||
|
keyDeleted);
|
||||||
|
|
||||||
|
/* ensure we find a lesser match */
|
||||||
|
if (!node || !(node->key < searchNode.key))
|
||||||
|
elog(ERROR, "rbt_find_less() didn't find a lesser key");
|
||||||
|
|
||||||
|
/* randomly delete the found key or leave it */
|
||||||
|
keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
|
||||||
|
if (keyDeleted)
|
||||||
|
rbt_delete(tree, (RBTNode *) node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the rest of the naturals greater than the search key */
|
||||||
|
keyDeleted = false;
|
||||||
|
for (searchNode.key = randomKey; searchNode.key < size - 1; searchNode.key++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Find the next key. If the current key is deleted, we can pass
|
||||||
|
* equal_match == true and still find the next one.
|
||||||
|
*/
|
||||||
|
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode,
|
||||||
|
keyDeleted);
|
||||||
|
|
||||||
|
/* ensure we find a greater match */
|
||||||
|
if (!node || !(node->key > searchNode.key))
|
||||||
|
elog(ERROR, "rbt_find_great() didn't find a greater key");
|
||||||
|
|
||||||
|
/* randomly delete the found key or leave it */
|
||||||
|
keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
|
||||||
|
if (keyDeleted)
|
||||||
|
rbt_delete(tree, (RBTNode *) node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check out of bounds searches find nothing */
|
||||||
|
searchNode.key = -1;
|
||||||
|
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
|
||||||
|
if (node != NULL)
|
||||||
|
elog(ERROR, "rbt_find_less() found non-inserted element");
|
||||||
|
searchNode.key = 0;
|
||||||
|
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, false);
|
||||||
|
if (node != NULL)
|
||||||
|
elog(ERROR, "rbt_find_less() found non-inserted element");
|
||||||
|
searchNode.key = size;
|
||||||
|
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
|
||||||
|
if (node != NULL)
|
||||||
|
elog(ERROR, "rbt_find_great() found non-inserted element");
|
||||||
|
searchNode.key = size - 1;
|
||||||
|
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, false);
|
||||||
|
if (node != NULL)
|
||||||
|
elog(ERROR, "rbt_find_great() found non-inserted element");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the correctness of the rbt_leftmost operation.
|
* Check the correctness of the rbt_leftmost operation.
|
||||||
* This operation should always return the smallest element of the tree.
|
* This operation should always return the smallest element of the tree.
|
||||||
@ -408,6 +509,7 @@ test_rb_tree(PG_FUNCTION_ARGS)
|
|||||||
testleftright(size);
|
testleftright(size);
|
||||||
testrightleft(size);
|
testrightleft(size);
|
||||||
testfind(size);
|
testfind(size);
|
||||||
|
testfindltgt(size);
|
||||||
testleftmost(size);
|
testleftmost(size);
|
||||||
testdelete(size, Max(size / 10, 1));
|
testdelete(size, Max(size / 10, 1));
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user