Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e57519a

Browse files
committed
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
1 parent 8d51d7f commit e57519a

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

src/backend/lib/rbtree.c

+62
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,68 @@ rbt_find(RBTree *rbt, const RBTNode *data)
161161
return NULL;
162162
}
163163

164+
/*
165+
* rbt_find_great: search for a greater value in an RBTree
166+
*
167+
* If equal_match is true, this will be a great or equal search.
168+
*
169+
* Returns the matching tree entry, or NULL if no match is found.
170+
*/
171+
RBTNode *
172+
rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match)
173+
{
174+
RBTNode *node = rbt->root;
175+
RBTNode *greater = NULL;
176+
177+
while (node != RBTNIL)
178+
{
179+
int cmp = rbt->comparator(data, node, rbt->arg);
180+
181+
if (equal_match && cmp == 0)
182+
return node;
183+
else if (cmp < 0)
184+
{
185+
greater = node;
186+
node = node->left;
187+
}
188+
else
189+
node = node->right;
190+
}
191+
192+
return greater;
193+
}
194+
195+
/*
196+
* rbt_find_less: search for a lesser value in an RBTree
197+
*
198+
* If equal_match is true, this will be a less or equal search.
199+
*
200+
* Returns the matching tree entry, or NULL if no match is found.
201+
*/
202+
RBTNode *
203+
rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match)
204+
{
205+
RBTNode *node = rbt->root;
206+
RBTNode *lesser = NULL;
207+
208+
while (node != RBTNIL)
209+
{
210+
int cmp = rbt->comparator(data, node, rbt->arg);
211+
212+
if (equal_match && cmp == 0)
213+
return node;
214+
else if (cmp > 0)
215+
{
216+
lesser = node;
217+
node = node->right;
218+
}
219+
else
220+
node = node->left;
221+
}
222+
223+
return lesser;
224+
}
225+
164226
/*
165227
* rbt_leftmost: fetch the leftmost (smallest-valued) tree node.
166228
* Returns NULL if tree is empty.

src/include/lib/rbtree.h

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ extern RBTree *rbt_create(Size node_size,
6767
void *arg);
6868

6969
extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
70+
extern RBTNode *rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match);
71+
extern RBTNode *rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match);
7072
extern RBTNode *rbt_leftmost(RBTree *rbt);
7173

7274
extern RBTNode *rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew);

src/test/modules/test_rbtree/test_rbtree.c

+102
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,107 @@ testfind(int size)
278278
}
279279
}
280280

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+
testfindltgt(int size)
288+
{
289+
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+
int randomKey = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
296+
bool keyDeleted;
297+
IntRBTreeNode searchNode = {.key = randomKey};
298+
IntRBTreeNode *lteNode;
299+
IntRBTreeNode *gteNode;
300+
IntRBTreeNode *node;
301+
302+
/* Insert natural numbers */
303+
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+
lteNode = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
310+
gteNode = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
311+
312+
if (lteNode == NULL || lteNode->key != searchNode.key)
313+
elog(ERROR, "rbt_find_less() didn't find the equal key");
314+
315+
if (gteNode == NULL || gteNode->key != searchNode.key)
316+
elog(ERROR, "rbt_find_great() didn't find the equal key");
317+
318+
if (lteNode != gteNode)
319+
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+
keyDeleted = false;
323+
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+
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode,
330+
keyDeleted);
331+
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");
335+
336+
/* randomly delete the found key or leave it */
337+
keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
338+
if (keyDeleted)
339+
rbt_delete(tree, (RBTNode *) node);
340+
}
341+
342+
/* Find the rest of the naturals greater than the search key */
343+
keyDeleted = false;
344+
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+
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode,
351+
keyDeleted);
352+
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");
356+
357+
/* randomly delete the found key or leave it */
358+
keyDeleted = (pg_prng_uint64_range(&pg_global_prng_state, 0, 1) == 1);
359+
if (keyDeleted)
360+
rbt_delete(tree, (RBTNode *) node);
361+
}
362+
363+
/* Check out of bounds searches find nothing */
364+
searchNode.key = -1;
365+
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, true);
366+
if (node != NULL)
367+
elog(ERROR, "rbt_find_less() found non-inserted element");
368+
searchNode.key = 0;
369+
node = (IntRBTreeNode *) rbt_find_less(tree, (RBTNode *) &searchNode, false);
370+
if (node != NULL)
371+
elog(ERROR, "rbt_find_less() found non-inserted element");
372+
searchNode.key = size;
373+
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, true);
374+
if (node != NULL)
375+
elog(ERROR, "rbt_find_great() found non-inserted element");
376+
searchNode.key = size - 1;
377+
node = (IntRBTreeNode *) rbt_find_great(tree, (RBTNode *) &searchNode, false);
378+
if (node != NULL)
379+
elog(ERROR, "rbt_find_great() found non-inserted element");
380+
}
381+
281382
/*
282383
* Check the correctness of the rbt_leftmost operation.
283384
* This operation should always return the smallest element of the tree.
@@ -408,6 +509,7 @@ test_rb_tree(PG_FUNCTION_ARGS)
408509
testleftright(size);
409510
testrightleft(size);
410511
testfind(size);
512+
testfindltgt(size);
411513
testleftmost(size);
412514
testdelete(size, Max(size / 10, 1));
413515
PG_RETURN_VOID();

0 commit comments

Comments
 (0)