Hello,Postgres now has its own red-black tree implementation. This tree has 4 types of traversals. In the attachment, you can find module test that checks the correctness of tree traversal strategies.
I hope that someone can find it useful. Thank you for attention! -- ------ Victor Drobny Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 3ce9904..b7ed0af 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -13,6 +13,7 @@ SUBDIRS = \ test_extensions \ test_parser \ test_pg_dump \ + test_rbtree \ test_rls_hooks \ test_shm_mq \ worker_spi diff --git a/src/test/modules/test_rbtree/.gitignore b/src/test/modules/test_rbtree/.gitignore new file mode 100644 index 0000000..5dcb3ff --- /dev/null +++ b/src/test/modules/test_rbtree/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_rbtree/Makefile b/src/test/modules/test_rbtree/Makefile new file mode 100644 index 0000000..11a10cf --- /dev/null +++ b/src/test/modules/test_rbtree/Makefile @@ -0,0 +1,21 @@ +# src/test/modules/test_rbtree/Makefile + +MODULE_big = test_rbtree +OBJS = test.o $(WIN32RES) +PGFILEDESC = "test_rbtree - rbtree triversal testing" + +EXTENSION = test_rbtree +DATA = test_rbtree--1.0.sql + +REGRESS = test_rbtree + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_rbtree +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_rbtree/README b/src/test/modules/test_rbtree/README new file mode 100644 index 0000000..9e6c977 --- /dev/null +++ b/src/test/modules/test_rbtree/README @@ -0,0 +1,17 @@ +test_rbtree is a module tests for checking the correctness of all kinds of +traversal of red-black tree. Right now rbtree in postgres has 4 kinds of +traversals: Left-Current-Right, Right-Current-Left, Current-Left-Right and +Left-Right-Current. + +This extention has 4 functions. Each function checks one traversal. +The checking the correctness of first two types are based on the fact that +red-black tree is a binary search tree, so the elements should be iterated in +increasing(for Left-Current-Right) or decreasing(for Right-Current-Left) +order. +In order to verify last two strategies, we will check the sequence if it is +correct or not. For given pre- or post- order traversing of binary search tree +it is always possible to say is it correct or not and to rebuild original tree. +The idea is based on the fact that in such traversal sequence is always +possible to determine current node, left subtree and right subtree. + +This tests are performed on red-black trees that store integers. \ No newline at end of file diff --git a/src/test/modules/test_rbtree/expected/test_rbtree.out b/src/test/modules/test_rbtree/expected/test_rbtree.out new file mode 100644 index 0000000..be8cd75 --- /dev/null +++ b/src/test/modules/test_rbtree/expected/test_rbtree.out @@ -0,0 +1,25 @@ +CREATE EXTENSION test_rbtree; +SELECT testleftright(); + testleftright +--------------- + +(1 row) + +SELECT testrightleft(); + testrightleft +--------------- + +(1 row) + +SELECT testdirect(); + testdirect +------------ + +(1 row) + +SELECT testinverted(); + testinverted +-------------- + +(1 row) + diff --git a/src/test/modules/test_rbtree/int_rbtree.h b/src/test/modules/test_rbtree/int_rbtree.h new file mode 100644 index 0000000..d153616 --- /dev/null +++ b/src/test/modules/test_rbtree/int_rbtree.h @@ -0,0 +1,49 @@ +/*-------------------------------------------------------------------------- + * + * int_rbtree.h + * Definitions for integer red-black tree + * + * Copyright (c) 2013-2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_rbtree/int_rbtree.h + * + * ------------------------------------------------------------------------- + */ + +#ifndef INT_RBTREE_H +#define INT_RBTREE_H + +#include "lib/rbtree.h" + +typedef struct IntRBTreeNode +{ + RBNode rbnode; + int key; + +} IntRBTreeNode; + +static int +cmp(const RBNode *a, const RBNode *b, void *arg) +{ + const IntRBTreeNode *ea = (const IntRBTreeNode *) a; + const IntRBTreeNode *eb = (const IntRBTreeNode *) b; + + return ea->key - eb->key; +} + +static RBNode * +alloc(void *arg) +{ + IntRBTreeNode *ea; + ea = malloc(sizeof(IntRBTreeNode)); + return (RBNode *) ea; +} + +static void +fr(RBNode * node, void *arg) +{ + free(node); +} + +#endif // INT_RBTREE_H diff --git a/src/test/modules/test_rbtree/sql/test_rbtree.sql b/src/test/modules/test_rbtree/sql/test_rbtree.sql new file mode 100644 index 0000000..edd90c1 --- /dev/null +++ b/src/test/modules/test_rbtree/sql/test_rbtree.sql @@ -0,0 +1,6 @@ +CREATE EXTENSION test_rbtree; + +SELECT testleftright(); +SELECT testrightleft(); +SELECT testdirect(); +SELECT testinverted(); \ No newline at end of file diff --git a/src/test/modules/test_rbtree/test.c b/src/test/modules/test_rbtree/test.c new file mode 100644 index 0000000..b28591e --- /dev/null +++ b/src/test/modules/test_rbtree/test.c @@ -0,0 +1,407 @@ +/*-------------------------------------------------------------------------- + * + * test.c + * Test correctness of red-black tree traversals. + * + * Copyright (c) 2013-2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_rbtree/test.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "miscadmin.h" +#include "pgstat.h" + +#include "int_rbtree.h" + +PG_MODULE_MAGIC; + +PG_FUNCTION_INFO_V1(testleftright); +PG_FUNCTION_INFO_V1(testrightleft); +PG_FUNCTION_INFO_V1(testdirect); +PG_FUNCTION_INFO_V1(testinverted); + +void _PG_init(void); + +#define TEST_SIZE 100000 + +/* + * Checking the correctness of left-right traversal. + * Left-right traversal is correct if elements are + * given in the increasing order. + */ +Datum +testleftright(PG_FUNCTION_ARGS) +{ + IntRBTreeNode *node; + RBTree *tree = rb_create(sizeof(IntRBTreeNode), cmp, NULL, alloc, fr, NULL); + RBTreeIterator iter; + int i; + int lastKey = -1; + int size = TEST_SIZE; + + /* filling tree by consecutive natural numbers */ + for (i = 0; i < size; i++) + { + IntRBTreeNode node; + bool isNew; + node.key = i; + rb_insert(tree, (RBNode *) &node, &isNew); + } + + rb_begin_iterate(tree, LeftRightWalk, &iter); + + /* iterate over the tree */ + while ((node = (IntRBTreeNode *)rb_iterate(&iter)) != NULL) + { + /* checking that order is increasing */ + if (node->key <= lastKey) + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("left-right walk give elements not in sorted order"))); + lastKey = node->key; + } + + PG_RETURN_VOID(); +} + +/* + * Checking the correctness of right-left traversal. + * Right-left traversal is correct if elements are + * given in the decreasing order. + */ +Datum +testrightleft(PG_FUNCTION_ARGS) +{ + IntRBTreeNode *node; + RBTree *tree = rb_create(sizeof(IntRBTreeNode), cmp, NULL, alloc, fr, NULL); + RBTreeIterator iter; + int i; + int size = TEST_SIZE; + int lastKey = size + 1; + + /* filling tree by consecutive natural numbers */ + for (i = 0; i < size; i++) + { + IntRBTreeNode node; + bool isNew; + node.key = i; + rb_insert(tree, (RBNode *) &node, &isNew); + } + + rb_begin_iterate(tree, RightLeftWalk, &iter); + + /* iterate over the tree */ + while ((node = (IntRBTreeNode *)rb_iterate(&iter)) != NULL) + { + /* checking that order is decreasing */ + if (node->key >= lastKey) + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("right-left walk give elements not in sorted order"))); + lastKey = node->key; + } + + PG_RETURN_VOID(); +} + +/* + * Getting NIL element of the tree + */ +static RBNode * +GetNil(RBNode *node) +{ + if ((void *)node == (void *)node->left) + return node; + return GetNil(node->left); +} + +/* + * Checking the correctness of given preorder traversal. + * For the preorder traversal the root key is always on the first position. + * NOTE: BST has only one preorder traversal and vice versa. + * Any preorder traversal corresponds to only one BST. + * e1 e2 e3 ... ek ... elen + * ^ ^ ^ + * head left right + * e1 > ei for all i in [2; k - 1] + * e1 < ei for all i in [k; len] + */ +static bool +ValidatePreorderInt(int * array, int len) +{ + int *left = array + 1; + int *right = left; + int *tmp; + + if (len <= 1) + return true; + + /* searching for the right subtree */ + while (right < array + len && *right <= *array) + right++; + + /* checking that right subtree has only elements + * that are bigger than current */ + tmp = right; + while (tmp < array + len) + { + if (*tmp <= *array) + return false; + tmp++; + } + + /* checking left subtree and right subtree recursively */ + return ValidatePreorderInt(left, right - left) && + ValidatePreorderInt(right, array + len - right); +} + +/* Checking traversal by traversing the tree */ +static bool +ValidatePreorderIntTree(IntRBTreeNode *node, int *array, int len, void *null) +{ + int *right = array; + int rightValue; + int leftValue; + int i; + if (node == null) + return len == 0; + if (node->key != *array) + return false; + /* If node has left child -> check it */ + if ((void *)node->rbnode.left != null) + { + leftValue = ((IntRBTreeNode *)node->rbnode.left)->key; + if (leftValue != array[1]) + return false; + /* searching for right part */ + if ((void *)node->rbnode.right != null) + { + rightValue = ((IntRBTreeNode *)node->rbnode.right)->key; + i = 0; + while (array[i] != rightValue) + { + i++; + if (i > len) + return false; + } + right = array + i; + } + else + { + right = array + len; + } + if (!ValidatePreorderIntTree((IntRBTreeNode *)node->rbnode.left, array + 1, right - array - 1, null)) + return false; + } + + /* If node has right child -> check it */ + if ((void *)node->rbnode.right != null) + { + rightValue = ((IntRBTreeNode *)node->rbnode.right)->key; + /* if left child is not null -> right is properly set, otherwise set it now */ + if (right == array) + right++; + if (rightValue != *right) + return false; + return ValidatePreorderIntTree((IntRBTreeNode *)node->rbnode.right, right, array + len - right, null); + } + return true; +} + +/* + * Checking the correctness of preorder(direct) traversal. + * Firstly, the correctness of the sequence by itself is checked. + * Secondly, a correspondance to the tree is checked. + */ +Datum +testdirect(PG_FUNCTION_ARGS) +{ + IntRBTreeNode *node; + RBTree *tree = rb_create(sizeof(IntRBTreeNode), cmp, NULL, alloc, fr, NULL); + RBTreeIterator iter; + void *null; + int size = TEST_SIZE; + int *elements; + int i; + + for (i = 0; i < size; i++) + { + IntRBTreeNode node; + bool isNew; + node.key = i; + rb_insert(tree, (RBNode *) &node, &isNew); + } + + elements = (int *) malloc(sizeof(int) * size); + rb_begin_iterate(tree, DirectWalk, &iter); + i = 0; + + while ((node = (IntRBTreeNode *)rb_iterate(&iter)) != NULL) + { + elements[i++] = node->key; + } + + if (i != size || !ValidatePreorderInt(elements, size)) + { + free(elements); + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("preorder walk give elements not in the correct order"))); + + } + + null = (void *)GetNil(*(RBNode **)tree); + if (!ValidatePreorderIntTree(*(IntRBTreeNode **)tree, elements, size, null)) + { + free(elements); + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("preorder walk give elements not in the correct order in tree check"))); + + } + + free(elements); + PG_RETURN_VOID(); +} + +/* + * Checking the correctness of given postorder traversal + * For the postorder traversal the root key is always the last. + * NOTE: BST has only one postorder traversal and vice versa + * Any postorder traversal corresponds to only one BST + */ +static bool +ValidatePostorderInt(int * array, int len) +{ + int *left = array; + int *right = left; + int *tmp; + + if (len <= 1) + return true; + + /* searching for the right subtree */ + while (right < array + len && *right <= array[len - 1]) + right++; + + /* checking that right subtree has only elements + * that are bigger than current */ + tmp = right; + while (tmp < array + len - 1) + { + if (*tmp <= array[len - 1]) + return false; + tmp++; + } + + /* Checking left subtree and right subtree recursively */ + return ValidatePostorderInt(left, right - left) && + ValidatePostorderInt(right, array + len - right - 1); +} + +/* Checking traversal by traversing the tree */ +static bool +ValidatePostorderIntTree(IntRBTreeNode *node, int *array, int len, void *null, int depth) +{ + int *right = array; + int leftValue; + int i; + if (node == null) + return len == 0; + if (node->key != array[len - 1]) + return false; + + /* If node has left child -> check it */ + if ((void *)node->rbnode.left != null) + { + leftValue = ((IntRBTreeNode *)node->rbnode.left)->key; + /* searching for right part */ + if ((void *)node->rbnode.right != null) + { + i = 0; + while (array[i] != leftValue) + { + i++; + if (i >= len) + return false; + } + right = array + i + 1; + } + else + { + right = array + len - 1; + } + if (!ValidatePostorderIntTree((IntRBTreeNode *)node->rbnode.left, array, right - array, null, depth + 1)) + return false; + } + + /* If node has right child -> check it */ + if ((void *)node->rbnode.right != null) + { + return ValidatePostorderIntTree((IntRBTreeNode *)node->rbnode.right, right, array + len - right - 1, null, depth + 1); + } + return true; +} + +/* + * Checking the correctness of posorder(inverted) traversal. + * Firstly, the correctness of the sequence by itself is checked. + * Secondly, a correspondance to the tree is checked. + */ +Datum +testinverted(PG_FUNCTION_ARGS) +{ + IntRBTreeNode *node; + RBTree *tree = rb_create(sizeof(IntRBTreeNode), cmp, NULL, alloc, fr, NULL); + RBTreeIterator iter; + void *null; + int size = TEST_SIZE; + int *elements; + int i; + + for (i = 0; i < size; i++) + { + IntRBTreeNode node; + bool isNew; + node.key = i; + rb_insert(tree, (RBNode *) &node, &isNew); + } + + elements = (int *) malloc(sizeof(int) * size); + rb_begin_iterate(tree, InvertedWalk, &iter); + i = 0; + + while ((node = (IntRBTreeNode *)rb_iterate(&iter)) != NULL) + { + elements[i++] = node->key; + } + + if (i != size || !ValidatePostorderInt(elements, size)) + { + free(elements); + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("postorder walk give elements not in the correct order"))); + + } + + null = (void *)GetNil(*(RBNode **)tree); + if (!ValidatePostorderIntTree(*(IntRBTreeNode **)tree, elements, size, null, 0)) + { + free(elements); + ereport(ERROR, + (errcode(ERRCODE_WARNING), + errmsg("postorder walk give elements not in the correct order in tree check"))); + + } + + free(elements); + PG_RETURN_VOID(); +} + diff --git a/src/test/modules/test_rbtree/test_rbtree--1.0.sql b/src/test/modules/test_rbtree/test_rbtree--1.0.sql new file mode 100644 index 0000000..d17746b --- /dev/null +++ b/src/test/modules/test_rbtree/test_rbtree--1.0.sql @@ -0,0 +1,20 @@ +/* src/test/modules/test_rbtree/test_rbtree--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_rbtree" to load this file. \quit + +CREATE FUNCTION testleftright() + RETURNS pg_catalog.void STRICT + AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION testrightleft() + RETURNS pg_catalog.void STRICT + AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION testdirect() + RETURNS pg_catalog.void STRICT + AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION testinverted() + RETURNS pg_catalog.void STRICT + AS 'MODULE_PATHNAME' LANGUAGE C; \ No newline at end of file diff --git a/src/test/modules/test_rbtree/test_rbtree.control b/src/test/modules/test_rbtree/test_rbtree.control new file mode 100644 index 0000000..2fade6a --- /dev/null +++ b/src/test/modules/test_rbtree/test_rbtree.control @@ -0,0 +1,4 @@ +comment = 'Test rbtree triversal' +default_version = '1.0' +module_pathname = '$libdir/test_rbtree' +relocatable = true
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers