diff --git a/Makefile b/Makefile index 928e211..68c29a2 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ SERVER_INCLUDES=-I$(shell pg_config --includedir-server) CLIENT_INCLUDES=-I$(shell pg_config --includedir) LIBRARY_PATH = -L$(shell pg_config --libdir) -REGRESS = prelude basic random table invalid order +REGRESS = prelude basic io_varying_lengths random table invalid order REGRESS_OPTS = --inputdir=test PG_CONFIG = pg_config diff --git a/README.md b/README.md index bd386ad..3ff5757 100644 --- a/README.md +++ b/README.md @@ -3,30 +3,36 @@ This PostgreSQL extension implements hashset, a data structure (type) providing a collection of integer items with fast lookup. + +## Version + +0.0.1 + 🚧 **NOTICE** 🚧 This repository is currently under active development and the hashset PostgreSQL extension is **not production-ready**. As the codebase is evolving with possible breaking changes, we are not providing any migration scripts until we reach our first release. + ## Usage -After installing the extension, you can use the `hashset` data type and +After installing the extension, you can use the `int4hashset` data type and associated functions within your PostgreSQL queries. To demonstrate the usage, let's consider a hypothetical table `users` which has -a `user_id` and a `user_likes` of type `hashset`. +a `user_id` and a `user_likes` of type `int4hashset`. Firstly, let's create the table: ```sql CREATE TABLE users( user_id int PRIMARY KEY, - user_likes hashset DEFAULT hashset_init(2) + user_likes int4hashset DEFAULT int4hashset() ); ``` -In the above statement, the `hashset_init(2)` initializes a hashset with initial -capacity for 2 elements. The hashset will automatically resize itself when more -elements are added beyond this initial capacity. +In the above statement, the `int4hashset()` initializes an empty hashset +with zero capacity. The hashset will automatically resize itself when more +elements are added. Now, we can perform operations on this table. Here are some examples: @@ -47,47 +53,52 @@ SELECT hashset_count(user_likes) FROM users WHERE user_id = 1; -- 2 ``` You can also use the aggregate functions to perform operations on multiple rows. -For instance, you can add an integer to a `hashset`. ## Data types -- **hashset**: This data type represents a set of integers. Internally, it uses +- **int4hashset**: This data type represents a set of integers. Internally, it uses a combination of a bitmap and a value array to store the elements in a set. It's a variable-length type. ## Functions -The extension provides the following functions: +- `int4hashset() -> int4hashset`: Initialize an empty int4hashset with no capacity. +- `int4hashset_with_capacity(int) -> int4hashset`: Initialize an empty int4hashset with given capacity. +- `hashset_add(int4hashset, int) -> int4hashset`: Adds an integer to an int4hashset. +- `hashset_contains(int4hashset, int) -> boolean`: Checks if an int4hashset contains a given integer. +- `hashset_merge(int4hashset, int4hashset) -> int4hashset`: Merges two int4hashsets into a new int4hashset. +- `hashset_to_array(int4hashset) -> int[]`: Converts an int4hashset to an array of integers. +- `hashset_count(int4hashset) -> bigint`: Returns the number of elements in an int4hashset. +- `hashset_capacity(int4hashset) -> bigint`: Returns the current capacity of an int4hashset. -### hashset_add(hashset, int) -> hashset -Adds an integer to a `hashset`. +## Aggregation Functions -### hashset_contains(hashset, int) -> boolean -Checks if an integer is contained in a `hashset`. +- `hashset(int) -> int4hashset`: Aggregate integers into a hashset. +- `hashset(int4hashset) -> int4hashset`: Aggregate hashsets into a hashset. -### hashset_count(hashset) -> bigint -Returns the number of elements in a `hashset`. -### hashset_merge(hashset, hashset) -> hashset -Merges two `hashset`s into a single `hashset`. +## Operators -### hashset_to_array(hashset) -> integer[] -Converts a `hashset` to an integer array. +- Equality (`=`): Checks if two hashsets are equal. +- Inequality (`<>`): Checks if two hashsets are not equal. -### hashset_init(int) -> hashset -Initializes an empty `hashset` with a specified initial capacity for maximum -elements. The argument determines the maximum number of elements the `hashset` -can hold before it needs to resize. -## Aggregate Functions +## Hashset Hash Operators -### hashset(integer) -> hashset -Generates a `hashset` from a series of integers, keeping only the unique ones. +- `hashset_hash(int4hashset) -> integer`: Returns the hash value of an int4hashset. -### hashset(hashset) -> hashset -Merges multiple `hashset`s into a single `hashset`, preserving unique elements. + +## Hashset Btree Operators + +- `<`, `<=`, `>`, `>=`: Comparison operators for hashsets. + + +## Limitations + +- The `int4hashset` data type currently supports integers within the range of int4 +(-2147483648 to 2147483647). ## Installation diff --git a/hashset--0.0.1.sql b/hashset--0.0.1.sql index 7b79b73..ea559ca 100644 --- a/hashset--0.0.1.sql +++ b/hashset--0.0.1.sql @@ -1,135 +1,158 @@ -CREATE TYPE hashset; +/* + * Hashset Type Definition + */ -CREATE OR REPLACE FUNCTION hashset_in(cstring) - RETURNS hashset - AS 'hashset', 'hashset_in' +CREATE TYPE int4hashset; + +CREATE OR REPLACE FUNCTION int4hashset_in(cstring) + RETURNS int4hashset + AS 'hashset', 'int4hashset_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_out(hashset) +CREATE OR REPLACE FUNCTION int4hashset_out(int4hashset) RETURNS cstring - AS 'hashset', 'hashset_out' + AS 'hashset', 'int4hashset_out' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_send(hashset) +CREATE OR REPLACE FUNCTION int4hashset_send(int4hashset) RETURNS bytea - AS 'hashset', 'hashset_send' + AS 'hashset', 'int4hashset_send' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_recv(internal) - RETURNS hashset - AS 'hashset', 'hashset_recv' +CREATE OR REPLACE FUNCTION int4hashset_recv(internal) + RETURNS int4hashset + AS 'hashset', 'int4hashset_recv' LANGUAGE C IMMUTABLE STRICT; -CREATE TYPE hashset ( - INPUT = hashset_in, - OUTPUT = hashset_out, - RECEIVE = hashset_recv, - SEND = hashset_send, +CREATE TYPE int4hashset ( + INPUT = int4hashset_in, + OUTPUT = int4hashset_out, + RECEIVE = int4hashset_recv, + SEND = int4hashset_send, INTERNALLENGTH = variable, STORAGE = external ); +/* + * Hashset Functions + */ -CREATE OR REPLACE FUNCTION hashset_add(hashset, int) - RETURNS hashset - AS 'hashset', 'hashset_add' +CREATE OR REPLACE FUNCTION int4hashset() + RETURNS int4hashset + AS 'hashset', 'int4hashset_init' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_contains(hashset, int) +CREATE OR REPLACE FUNCTION int4hashset_with_capacity(int) + RETURNS int4hashset + AS 'hashset', 'int4hashset_init' + LANGUAGE C IMMUTABLE; + +CREATE OR REPLACE FUNCTION hashset_add(int4hashset, int) + RETURNS int4hashset + AS 'hashset', 'int4hashset_add' + LANGUAGE C IMMUTABLE; + +CREATE OR REPLACE FUNCTION hashset_contains(int4hashset, int) RETURNS bool - AS 'hashset', 'hashset_contains' + AS 'hashset', 'int4hashset_contains' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_count(hashset) - RETURNS bigint - AS 'hashset', 'hashset_count' +CREATE OR REPLACE FUNCTION hashset_merge(int4hashset, int4hashset) + RETURNS int4hashset + AS 'hashset', 'int4hashset_merge' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_merge(hashset, hashset) - RETURNS hashset - AS 'hashset', 'hashset_merge' - LANGUAGE C IMMUTABLE; - -CREATE OR REPLACE FUNCTION hashset_to_array(hashset) +CREATE OR REPLACE FUNCTION hashset_to_array(int4hashset) RETURNS int[] - AS 'hashset', 'hashset_to_array' + AS 'hashset', 'int4hashset_to_array' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_init(int) - RETURNS hashset - AS 'hashset', 'hashset_init' +CREATE OR REPLACE FUNCTION hashset_count(int4hashset) + RETURNS bigint + AS 'hashset', 'int4hashset_count' LANGUAGE C IMMUTABLE; +CREATE OR REPLACE FUNCTION hashset_capacity(int4hashset) + RETURNS bigint + AS 'hashset', 'int4hashset_capacity' + LANGUAGE C IMMUTABLE; + + +/* + * Aggregation Functions + */ -CREATE OR REPLACE FUNCTION hashset_agg_add(p_pointer internal, p_value int) +CREATE OR REPLACE FUNCTION int4hashset_agg_add(p_pointer internal, p_value int) RETURNS internal - AS 'hashset', 'hashset_agg_add' + AS 'hashset', 'int4hashset_agg_add' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_agg_final(p_pointer internal) - RETURNS hashset - AS 'hashset', 'hashset_agg_final' +CREATE OR REPLACE FUNCTION int4hashset_agg_final(p_pointer internal) + RETURNS int4hashset + AS 'hashset', 'int4hashset_agg_final' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_agg_combine(p_pointer internal, p_pointer2 internal) +CREATE OR REPLACE FUNCTION int4hashset_agg_combine(p_pointer internal, p_pointer2 internal) RETURNS internal - AS 'hashset', 'hashset_agg_combine' + AS 'hashset', 'int4hashset_agg_combine' LANGUAGE C IMMUTABLE; CREATE AGGREGATE hashset(int) ( - SFUNC = hashset_agg_add, + SFUNC = int4hashset_agg_add, STYPE = internal, - FINALFUNC = hashset_agg_final, - COMBINEFUNC = hashset_agg_combine, + FINALFUNC = int4hashset_agg_final, + COMBINEFUNC = int4hashset_agg_combine, PARALLEL = SAFE ); - -CREATE OR REPLACE FUNCTION hashset_agg_add_set(p_pointer internal, p_value hashset) +CREATE OR REPLACE FUNCTION int4hashset_agg_add_set(p_pointer internal, p_value int4hashset) RETURNS internal - AS 'hashset', 'hashset_agg_add_set' + AS 'hashset', 'int4hashset_agg_add_set' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION hashset_agg_final(p_pointer internal) - RETURNS hashset - AS 'hashset', 'hashset_agg_final' +CREATE OR REPLACE FUNCTION int4hashset_agg_final(p_pointer internal) + RETURNS int4hashset + AS 'hashset', 'int4hashset_agg_final' LANGUAGE C IMMUTABLE; - -CREATE OR REPLACE FUNCTION hashset_agg_combine(p_pointer internal, p_pointer2 internal) + +CREATE OR REPLACE FUNCTION int4hashset_agg_combine(p_pointer internal, p_pointer2 internal) RETURNS internal - AS 'hashset', 'hashset_agg_combine' + AS 'hashset', 'int4hashset_agg_combine' LANGUAGE C IMMUTABLE; -CREATE AGGREGATE hashset(hashset) ( - SFUNC = hashset_agg_add_set, +CREATE AGGREGATE hashset(int4hashset) ( + SFUNC = int4hashset_agg_add_set, STYPE = internal, - FINALFUNC = hashset_agg_final, - COMBINEFUNC = hashset_agg_combine, + FINALFUNC = int4hashset_agg_final, + COMBINEFUNC = int4hashset_agg_combine, PARALLEL = SAFE ); +/* + * Operator Definitions + */ -CREATE OR REPLACE FUNCTION hashset_equals(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_equals(int4hashset, int4hashset) RETURNS bool - AS 'hashset', 'hashset_equals' + AS 'hashset', 'int4hashset_equals' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR = ( - LEFTARG = hashset, - RIGHTARG = hashset, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, PROCEDURE = hashset_equals, COMMUTATOR = =, HASHES ); -CREATE OR REPLACE FUNCTION hashset_neq(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_neq(int4hashset, int4hashset) RETURNS bool - AS 'hashset', 'hashset_neq' + AS 'hashset', 'int4hashset_neq' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR <> ( - LEFTARG = hashset, - RIGHTARG = hashset, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, PROCEDURE = hashset_neq, COMMUTATOR = '<>', NEGATOR = '=', @@ -138,46 +161,53 @@ CREATE OPERATOR <> ( HASHES ); +/* + * Hashset Hash Operators + */ -CREATE OR REPLACE FUNCTION hashset_hash(hashset) +CREATE OR REPLACE FUNCTION hashset_hash(int4hashset) RETURNS integer - AS 'hashset', 'hashset_hash' + AS 'hashset', 'int4hashset_hash' LANGUAGE C IMMUTABLE STRICT; -CREATE OPERATOR CLASS hashset_hash_ops - DEFAULT FOR TYPE hashset USING hash AS - OPERATOR 1 = (hashset, hashset), - FUNCTION 1 hashset_hash(hashset); +CREATE OPERATOR CLASS int4hashset_hash_ops + DEFAULT FOR TYPE int4hashset USING hash AS + OPERATOR 1 = (int4hashset, int4hashset), + FUNCTION 1 hashset_hash(int4hashset); -CREATE OR REPLACE FUNCTION hashset_lt(hashset, hashset) +/* + * Hashset Btree Operators + */ + +CREATE OR REPLACE FUNCTION hashset_lt(int4hashset, int4hashset) RETURNS bool - AS 'hashset', 'hashset_lt' + AS 'hashset', 'int4hashset_lt' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_le(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_le(int4hashset, int4hashset) RETURNS boolean - AS 'hashset', 'hashset_le' + AS 'hashset', 'int4hashset_le' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_gt(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_gt(int4hashset, int4hashset) RETURNS boolean - AS 'hashset', 'hashset_gt' + AS 'hashset', 'int4hashset_gt' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_ge(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_ge(int4hashset, int4hashset) RETURNS boolean - AS 'hashset', 'hashset_ge' + AS 'hashset', 'int4hashset_ge' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hashset_cmp(hashset, hashset) +CREATE OR REPLACE FUNCTION hashset_cmp(int4hashset, int4hashset) RETURNS integer - AS 'hashset', 'hashset_cmp' + AS 'hashset', 'int4hashset_cmp' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR < ( - LEFTARG = hashset, - RIGHTARG = hashset, PROCEDURE = hashset_lt, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, COMMUTATOR = >, NEGATOR = >=, RESTRICT = scalarltsel, @@ -186,8 +216,8 @@ CREATE OPERATOR < ( CREATE OPERATOR <= ( PROCEDURE = hashset_le, - LEFTARG = hashset, - RIGHTARG = hashset, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, COMMUTATOR = '>=', NEGATOR = '>', RESTRICT = scalarltsel, @@ -196,8 +226,8 @@ CREATE OPERATOR <= ( CREATE OPERATOR > ( PROCEDURE = hashset_gt, - LEFTARG = hashset, - RIGHTARG = hashset, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, COMMUTATOR = '<', NEGATOR = '<=', RESTRICT = scalargtsel, @@ -206,19 +236,19 @@ CREATE OPERATOR > ( CREATE OPERATOR >= ( PROCEDURE = hashset_ge, - LEFTARG = hashset, - RIGHTARG = hashset, + LEFTARG = int4hashset, + RIGHTARG = int4hashset, COMMUTATOR = '<=', NEGATOR = '<', RESTRICT = scalargtsel, JOIN = scalargtjoinsel ); -CREATE OPERATOR CLASS hashset_btree_ops - DEFAULT FOR TYPE hashset USING btree AS - OPERATOR 1 < (hashset, hashset), - OPERATOR 2 <= (hashset, hashset), - OPERATOR 3 = (hashset, hashset), - OPERATOR 4 >= (hashset, hashset), - OPERATOR 5 > (hashset, hashset), - FUNCTION 1 hashset_cmp(hashset, hashset); +CREATE OPERATOR CLASS int4hashset_btree_ops + DEFAULT FOR TYPE int4hashset USING btree AS + OPERATOR 1 < (int4hashset, int4hashset), + OPERATOR 2 <= (int4hashset, int4hashset), + OPERATOR 3 = (int4hashset, int4hashset), + OPERATOR 4 >= (int4hashset, int4hashset), + OPERATOR 5 > (int4hashset, int4hashset), + FUNCTION 1 hashset_cmp(int4hashset, int4hashset); diff --git a/hashset.c b/hashset.c index 2e6a51f..3bc7fdc 100644 --- a/hashset.c +++ b/hashset.c @@ -26,78 +26,80 @@ PG_MODULE_MAGIC; /* * hashset */ -typedef struct hashset_t { +typedef struct int4hashset_t { int32 vl_len_; /* varlena header (do not touch directly!) */ int32 flags; /* reserved for future use (versioning, ...) */ int32 maxelements; /* max number of element we have space for */ int32 nelements; /* number of items added to the hashset */ int32 hashfn_id; /* ID of the hash function used */ char data[FLEXIBLE_ARRAY_MEMBER]; -} hashset_t; +} int4hashset_t; -static hashset_t *hashset_resize(hashset_t * set); -static hashset_t *hashset_add_element(hashset_t *set, int32 value); -static bool hashset_contains_element(hashset_t *set, int32 value); +static int4hashset_t *int4hashset_resize(int4hashset_t * set); +static int4hashset_t *int4hashset_add_element(int4hashset_t *set, int32 value); +static bool int4hashset_contains_element(int4hashset_t *set, int32 value); static Datum int32_to_array(FunctionCallInfo fcinfo, int32 * d, int len); -#define PG_GETARG_HASHSET(x) (hashset_t *) PG_DETOAST_DATUM(PG_GETARG_DATUM(x)) +#define PG_GETARG_INT4HASHSET(x) (int4hashset_t *) PG_DETOAST_DATUM(PG_GETARG_DATUM(x)) #define CEIL_DIV(a, b) (((a) + (b) - 1) / (b)) #define HASHSET_STEP 13 #define JENKINS_LOOKUP3_HASHFN_ID 1 -PG_FUNCTION_INFO_V1(hashset_in); -PG_FUNCTION_INFO_V1(hashset_out); -PG_FUNCTION_INFO_V1(hashset_send); -PG_FUNCTION_INFO_V1(hashset_recv); -PG_FUNCTION_INFO_V1(hashset_add); -PG_FUNCTION_INFO_V1(hashset_contains); -PG_FUNCTION_INFO_V1(hashset_count); -PG_FUNCTION_INFO_V1(hashset_merge); -PG_FUNCTION_INFO_V1(hashset_init); -PG_FUNCTION_INFO_V1(hashset_agg_add_set); -PG_FUNCTION_INFO_V1(hashset_agg_add); -PG_FUNCTION_INFO_V1(hashset_agg_final); -PG_FUNCTION_INFO_V1(hashset_agg_combine); -PG_FUNCTION_INFO_V1(hashset_to_array); -PG_FUNCTION_INFO_V1(hashset_equals); -PG_FUNCTION_INFO_V1(hashset_neq); -PG_FUNCTION_INFO_V1(hashset_hash); -PG_FUNCTION_INFO_V1(hashset_lt); -PG_FUNCTION_INFO_V1(hashset_le); -PG_FUNCTION_INFO_V1(hashset_gt); -PG_FUNCTION_INFO_V1(hashset_ge); -PG_FUNCTION_INFO_V1(hashset_cmp); +PG_FUNCTION_INFO_V1(int4hashset_in); +PG_FUNCTION_INFO_V1(int4hashset_out); +PG_FUNCTION_INFO_V1(int4hashset_send); +PG_FUNCTION_INFO_V1(int4hashset_recv); +PG_FUNCTION_INFO_V1(int4hashset_add); +PG_FUNCTION_INFO_V1(int4hashset_contains); +PG_FUNCTION_INFO_V1(int4hashset_count); +PG_FUNCTION_INFO_V1(int4hashset_merge); +PG_FUNCTION_INFO_V1(int4hashset_init); +PG_FUNCTION_INFO_V1(int4hashset_capacity); +PG_FUNCTION_INFO_V1(int4hashset_agg_add_set); +PG_FUNCTION_INFO_V1(int4hashset_agg_add); +PG_FUNCTION_INFO_V1(int4hashset_agg_final); +PG_FUNCTION_INFO_V1(int4hashset_agg_combine); +PG_FUNCTION_INFO_V1(int4hashset_to_array); +PG_FUNCTION_INFO_V1(int4hashset_equals); +PG_FUNCTION_INFO_V1(int4hashset_neq); +PG_FUNCTION_INFO_V1(int4hashset_hash); +PG_FUNCTION_INFO_V1(int4hashset_lt); +PG_FUNCTION_INFO_V1(int4hashset_le); +PG_FUNCTION_INFO_V1(int4hashset_gt); +PG_FUNCTION_INFO_V1(int4hashset_ge); +PG_FUNCTION_INFO_V1(int4hashset_cmp); -Datum hashset_in(PG_FUNCTION_ARGS); -Datum hashset_out(PG_FUNCTION_ARGS); -Datum hashset_send(PG_FUNCTION_ARGS); -Datum hashset_recv(PG_FUNCTION_ARGS); -Datum hashset_add(PG_FUNCTION_ARGS); -Datum hashset_contains(PG_FUNCTION_ARGS); -Datum hashset_count(PG_FUNCTION_ARGS); -Datum hashset_merge(PG_FUNCTION_ARGS); -Datum hashset_init(PG_FUNCTION_ARGS); -Datum hashset_agg_add(PG_FUNCTION_ARGS); -Datum hashset_agg_add_set(PG_FUNCTION_ARGS); -Datum hashset_agg_final(PG_FUNCTION_ARGS); -Datum hashset_agg_combine(PG_FUNCTION_ARGS); -Datum hashset_to_array(PG_FUNCTION_ARGS); -Datum hashset_equals(PG_FUNCTION_ARGS); -Datum hashset_neq(PG_FUNCTION_ARGS); -Datum hashset_hash(PG_FUNCTION_ARGS); -Datum hashset_lt(PG_FUNCTION_ARGS); -Datum hashset_le(PG_FUNCTION_ARGS); -Datum hashset_gt(PG_FUNCTION_ARGS); -Datum hashset_ge(PG_FUNCTION_ARGS); -Datum hashset_cmp(PG_FUNCTION_ARGS); +Datum int4hashset_in(PG_FUNCTION_ARGS); +Datum int4hashset_out(PG_FUNCTION_ARGS); +Datum int4hashset_send(PG_FUNCTION_ARGS); +Datum int4hashset_recv(PG_FUNCTION_ARGS); +Datum int4hashset_add(PG_FUNCTION_ARGS); +Datum int4hashset_contains(PG_FUNCTION_ARGS); +Datum int4hashset_count(PG_FUNCTION_ARGS); +Datum int4hashset_merge(PG_FUNCTION_ARGS); +Datum int4hashset_init(PG_FUNCTION_ARGS); +Datum int4hashset_capacity(PG_FUNCTION_ARGS); +Datum int4hashset_agg_add(PG_FUNCTION_ARGS); +Datum int4hashset_agg_add_set(PG_FUNCTION_ARGS); +Datum int4hashset_agg_final(PG_FUNCTION_ARGS); +Datum int4hashset_agg_combine(PG_FUNCTION_ARGS); +Datum int4hashset_to_array(PG_FUNCTION_ARGS); +Datum int4hashset_equals(PG_FUNCTION_ARGS); +Datum int4hashset_neq(PG_FUNCTION_ARGS); +Datum int4hashset_hash(PG_FUNCTION_ARGS); +Datum int4hashset_lt(PG_FUNCTION_ARGS); +Datum int4hashset_le(PG_FUNCTION_ARGS); +Datum int4hashset_gt(PG_FUNCTION_ARGS); +Datum int4hashset_ge(PG_FUNCTION_ARGS); +Datum int4hashset_cmp(PG_FUNCTION_ARGS); -static hashset_t * -hashset_allocate(int maxelements) +static int4hashset_t * +int4hashset_allocate(int maxelements) { - Size len; - hashset_t *set; - char *ptr; + Size len; + int4hashset_t *set; + char *ptr; /* * Ensure that maxelements is not divisible by HASHSET_STEP; @@ -107,14 +109,14 @@ hashset_allocate(int maxelements) while (maxelements % HASHSET_STEP == 0) maxelements++; - len = offsetof(hashset_t, data); + len = offsetof(int4hashset_t, data); len += CEIL_DIV(maxelements, 8); len += maxelements * sizeof(int32); ptr = palloc0(len); SET_VARSIZE(ptr, len); - set = (hashset_t *) ptr; + set = (int4hashset_t *) ptr; set->flags = 0; set->maxelements = maxelements; @@ -127,12 +129,12 @@ hashset_allocate(int maxelements) } Datum -hashset_in(PG_FUNCTION_ARGS) +int4hashset_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); char *endptr; int32 len = strlen(str); - hashset_t *set; + int4hashset_t *set; /* Check the opening and closing braces */ if (str[0] != '{' || str[len - 1] != '}') @@ -147,9 +149,15 @@ hashset_in(PG_FUNCTION_ARGS) str++; /* Initial size based on input length (arbitrary, could be optimized) */ - set = hashset_allocate(len/2); + set = int4hashset_allocate(len/2); - while (true) + /* Check for empty set */ + if (*str == '}') + { + PG_RETURN_POINTER(set); + } + + while (*str != '}') { int64 value = strtol(str, &endptr, 10); @@ -164,9 +172,9 @@ hashset_in(PG_FUNCTION_ARGS) /* Add the value to the hashset, resize if needed */ if (set->nelements >= set->maxelements) { - set = hashset_resize(set); + set = int4hashset_resize(set); } - set = hashset_add_element(set, (int32)value); + set = int4hashset_add_element(set, (int32)value); /* Error handling for strtol */ if (endptr == str) @@ -179,26 +187,26 @@ hashset_in(PG_FUNCTION_ARGS) { str = endptr + 1; /* Move to the next number */ } - else if (*endptr == '}') - { - break; /* End of the hashset */ - } - else + else if (*endptr != '}') { /* Unexpected character */ ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("unexpected character \"%c\" in hashset input", *endptr))); } + else /* *endptr is '}', move to next iteration */ + { + str = endptr; + } } PG_RETURN_POINTER(set); } Datum -hashset_out(PG_FUNCTION_ARGS) +int4hashset_out(PG_FUNCTION_ARGS) { - hashset_t *set = (hashset_t *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + int4hashset_t *set = (int4hashset_t *) PG_GETARG_INT4HASHSET(0); char *bitmap; int32 *values; int i; @@ -239,9 +247,9 @@ hashset_out(PG_FUNCTION_ARGS) Datum -hashset_send(PG_FUNCTION_ARGS) +int4hashset_send(PG_FUNCTION_ARGS) { - hashset_t *set = (hashset_t *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + int4hashset_t *set = (int4hashset_t *) PG_GETARG_INT4HASHSET(0); StringInfoData buf; int32 data_size; @@ -255,7 +263,7 @@ hashset_send(PG_FUNCTION_ARGS) pq_sendint(&buf, set->hashfn_id, 4); /* Compute and send the size of the data field */ - data_size = VARSIZE(set) - offsetof(hashset_t, data); + data_size = VARSIZE(set) - offsetof(int4hashset_t, data); pq_sendbytes(&buf, set->data, data_size); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); @@ -263,10 +271,10 @@ hashset_send(PG_FUNCTION_ARGS) Datum -hashset_recv(PG_FUNCTION_ARGS) +int4hashset_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); - hashset_t *set; + int4hashset_t *set; int32 data_size; Size total_size; const char *binary_data; @@ -284,10 +292,10 @@ hashset_recv(PG_FUNCTION_ARGS) binary_data = pq_getmsgbytes(buf, data_size); /* Compute total size of hashset_t */ - total_size = offsetof(hashset_t, data) + data_size; + total_size = offsetof(int4hashset_t, data) + data_size; /* Allocate memory for hashset including the data field */ - set = (hashset_t *) palloc0(total_size); + set = (int4hashset_t *) palloc0(total_size); /* Set the size of the variable-length data structure */ SET_VARSIZE(set, total_size); @@ -304,21 +312,21 @@ hashset_recv(PG_FUNCTION_ARGS) Datum -hashset_to_array(PG_FUNCTION_ARGS) +int4hashset_to_array(PG_FUNCTION_ARGS) { - int i, - idx; - hashset_t *set; - int32 *values; - int nvalues; + int i, + idx; + int4hashset_t *set; + int32 *values; + int nvalues; - char *sbitmap; - int32 *svalues; + char *sbitmap; + int32 *svalues; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); - set = (hashset_t *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + set = (int4hashset_t *) PG_GETARG_INT4HASHSET(0); sbitmap = set->data; svalues = (int32 *) (set->data + CEIL_DIV(set->maxelements, 8)); @@ -367,13 +375,13 @@ int32_to_array(FunctionCallInfo fcinfo, int32 *d, int len) } -static hashset_t * -hashset_resize(hashset_t * set) +static int4hashset_t * +int4hashset_resize(int4hashset_t * set) { - int i; - hashset_t *new = hashset_allocate(set->maxelements * 2); - char *bitmap; - int32 *values; + int i; + int4hashset_t *new = int4hashset_allocate(set->maxelements * 2); + char *bitmap; + int32 *values; /* Calculate the pointer to the bitmap and values array */ bitmap = set->data; @@ -385,14 +393,14 @@ hashset_resize(hashset_t * set) int bit = (i % 8); if (bitmap[byte] & (0x01 << bit)) - hashset_add_element(new, values[i]); + int4hashset_add_element(new, values[i]); } return new; } -static hashset_t * -hashset_add_element(hashset_t *set, int32 value) +static int4hashset_t * +int4hashset_add_element(int4hashset_t *set, int32 value) { int byte; int bit; @@ -401,7 +409,7 @@ hashset_add_element(hashset_t *set, int32 value) int32 *values; if (set->nelements > set->maxelements * 0.75) - set = hashset_resize(set); + set = int4hashset_resize(set); if (set->hashfn_id == JENKINS_LOOKUP3_HASHFN_ID) { @@ -446,7 +454,7 @@ hashset_add_element(hashset_t *set, int32 value) } static bool -hashset_contains_element(hashset_t *set, int32 value) +int4hashset_contains_element(int4hashset_t *set, int32 value) { int byte; int bit; @@ -494,9 +502,9 @@ hashset_contains_element(hashset_t *set, int32 value) } Datum -hashset_add(PG_FUNCTION_ARGS) +int4hashset_add(PG_FUNCTION_ARGS) { - hashset_t *set; + int4hashset_t *set; if (PG_ARGISNULL(1)) { @@ -508,39 +516,38 @@ hashset_add(PG_FUNCTION_ARGS) /* if there's no hashset allocated, create it now */ if (PG_ARGISNULL(0)) - set = hashset_allocate(64); + set = int4hashset_allocate(64); else { /* make sure we are working with a non-toasted and non-shared copy of the input */ - set = (hashset_t *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); + set = (int4hashset_t *) PG_GETARG_INT4HASHSET(0); } - set = hashset_add_element(set, PG_GETARG_INT32(1)); + set = int4hashset_add_element(set, PG_GETARG_INT32(1)); PG_RETURN_POINTER(set); } Datum -hashset_merge(PG_FUNCTION_ARGS) +int4hashset_merge(PG_FUNCTION_ARGS) { - int i; + int i; - hashset_t *seta; - hashset_t *setb; + int4hashset_t *seta; + int4hashset_t *setb; - char *bitmap; - int32 *values; + char *bitmap; + int32_t *values; - /* */ if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1)) - PG_RETURN_POINTER(PG_GETARG_HASHSET(0)); + PG_RETURN_POINTER(PG_GETARG_INT4HASHSET(0)); else if (PG_ARGISNULL(0)) - PG_RETURN_POINTER(PG_GETARG_HASHSET(1)); + PG_RETURN_POINTER(PG_GETARG_INT4HASHSET(1)); - seta = PG_GETARG_HASHSET(0); - setb = PG_GETARG_HASHSET(1); + seta = PG_GETARG_INT4HASHSET(0); + setb = PG_GETARG_INT4HASHSET(1); bitmap = setb->data; values = (int32 *) (setb->data + CEIL_DIV(setb->maxelements, 8)); @@ -551,53 +558,78 @@ hashset_merge(PG_FUNCTION_ARGS) int bit = (i % 8); if (bitmap[byte] & (0x01 << bit)) - seta = hashset_add_element(seta, values[i]); + seta = int4hashset_add_element(seta, values[i]); } PG_RETURN_POINTER(seta); } Datum -hashset_init(PG_FUNCTION_ARGS) +int4hashset_init(PG_FUNCTION_ARGS) { - PG_RETURN_POINTER(hashset_allocate(PG_GETARG_INT32(0))); + if (PG_NARGS() == 0) { + /* + * No initial capacity argument was passed, + * allocate hashset with zero capacity + */ + PG_RETURN_POINTER(int4hashset_allocate(0)); + } else { + /* + * Initial capacity argument was passed, + * allocate hashset with the specified capacity + */ + PG_RETURN_POINTER(int4hashset_allocate(PG_GETARG_INT32(0))); + } } Datum -hashset_contains(PG_FUNCTION_ARGS) +int4hashset_contains(PG_FUNCTION_ARGS) { - hashset_t *set; - int32 value; + int4hashset_t *set; + int32 value; if (PG_ARGISNULL(1) || PG_ARGISNULL(0)) PG_RETURN_BOOL(false); - set = PG_GETARG_HASHSET(0); + set = PG_GETARG_INT4HASHSET(0); value = PG_GETARG_INT32(1); - PG_RETURN_BOOL(hashset_contains_element(set, value)); + PG_RETURN_BOOL(int4hashset_contains_element(set, value)); } Datum -hashset_count(PG_FUNCTION_ARGS) +int4hashset_count(PG_FUNCTION_ARGS) { - hashset_t *set; + int4hashset_t *set; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); - set = PG_GETARG_HASHSET(0); + set = PG_GETARG_INT4HASHSET(0); PG_RETURN_INT64(set->nelements); } Datum -hashset_agg_add(PG_FUNCTION_ARGS) +int4hashset_capacity(PG_FUNCTION_ARGS) +{ + int4hashset_t *set; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + set = (int4hashset_t *) PG_GETARG_POINTER(0); + + PG_RETURN_INT64(set->maxelements); +} + +Datum +int4hashset_agg_add(PG_FUNCTION_ARGS) { MemoryContext oldcontext; - hashset_t *state; + int4hashset_t *state; - MemoryContext aggcontext; + MemoryContext aggcontext; /* cannot be called directly because of internal-type argument */ if (!AggCheckCallContext(fcinfo, &aggcontext)) @@ -620,26 +652,26 @@ hashset_agg_add(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { oldcontext = MemoryContextSwitchTo(aggcontext); - state = hashset_allocate(64); + state = int4hashset_allocate(64); MemoryContextSwitchTo(oldcontext); } else - state = (hashset_t *) PG_GETARG_POINTER(0); + state = (int4hashset_t *) PG_GETARG_POINTER(0); oldcontext = MemoryContextSwitchTo(aggcontext); - state = hashset_add_element(state, PG_GETARG_INT32(1)); + state = int4hashset_add_element(state, PG_GETARG_INT32(1)); MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(state); } Datum -hashset_agg_add_set(PG_FUNCTION_ARGS) +int4hashset_agg_add_set(PG_FUNCTION_ARGS) { MemoryContext oldcontext; - hashset_t *state; + int4hashset_t *state; - MemoryContext aggcontext; + MemoryContext aggcontext; /* cannot be called directly because of internal-type argument */ if (!AggCheckCallContext(fcinfo, &aggcontext)) @@ -662,21 +694,21 @@ hashset_agg_add_set(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { oldcontext = MemoryContextSwitchTo(aggcontext); - state = hashset_allocate(64); + state = int4hashset_allocate(64); MemoryContextSwitchTo(oldcontext); } else - state = (hashset_t *) PG_GETARG_POINTER(0); + state = (int4hashset_t *) PG_GETARG_POINTER(0); oldcontext = MemoryContextSwitchTo(aggcontext); { - int i; - char *bitmap; - int32 *values; - hashset_t *value; + int i; + char *bitmap; + int32 *values; + int4hashset_t *value; - value = PG_GETARG_HASHSET(1); + value = PG_GETARG_INT4HASHSET(1); bitmap = value->data; values = (int32 *) (value->data + CEIL_DIV(value->maxelements, 8)); @@ -687,7 +719,7 @@ hashset_agg_add_set(PG_FUNCTION_ARGS) int bit = (i % 8); if (bitmap[byte] & (0x01 << bit)) - state = hashset_add_element(state, values[i]); + state = int4hashset_add_element(state, values[i]); } } @@ -697,7 +729,7 @@ hashset_agg_add_set(PG_FUNCTION_ARGS) } Datum -hashset_agg_final(PG_FUNCTION_ARGS) +int4hashset_agg_final(PG_FUNCTION_ARGS) { if (PG_ARGISNULL(0)) PG_RETURN_NULL(); @@ -705,23 +737,23 @@ hashset_agg_final(PG_FUNCTION_ARGS) PG_RETURN_POINTER(PG_GETARG_POINTER(0)); } -static hashset_t * -hashset_copy(hashset_t *src) +static int4hashset_t * +int4hashset_copy(int4hashset_t *src) { return src; } Datum -hashset_agg_combine(PG_FUNCTION_ARGS) +int4hashset_agg_combine(PG_FUNCTION_ARGS) { - int i; - hashset_t *src; - hashset_t *dst; - MemoryContext aggcontext; - MemoryContext oldcontext; + int i; + int4hashset_t *src; + int4hashset_t *dst; + MemoryContext aggcontext; + MemoryContext oldcontext; - char *bitmap; - int32 *values; + char *bitmap; + int32 *values; if (!AggCheckCallContext(fcinfo, &aggcontext)) elog(ERROR, "hashset_agg_combine called in non-aggregate context"); @@ -734,11 +766,11 @@ hashset_agg_combine(PG_FUNCTION_ARGS) PG_RETURN_NULL(); /* the second argument is not NULL, so copy it */ - src = (hashset_t *) PG_GETARG_POINTER(1); + src = (int4hashset_t *) PG_GETARG_POINTER(1); /* copy the hashset into the right long-lived memory context */ oldcontext = MemoryContextSwitchTo(aggcontext); - src = hashset_copy(src); + src = int4hashset_copy(src); MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(src); @@ -752,8 +784,8 @@ hashset_agg_combine(PG_FUNCTION_ARGS) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* Now we know neither argument is NULL, so merge them. */ - src = (hashset_t *) PG_GETARG_POINTER(1); - dst = (hashset_t *) PG_GETARG_POINTER(0); + src = (int4hashset_t *) PG_GETARG_POINTER(1); + dst = (int4hashset_t *) PG_GETARG_POINTER(0); bitmap = src->data; values = (int32 *) (src->data + CEIL_DIV(src->maxelements, 8)); @@ -764,7 +796,7 @@ hashset_agg_combine(PG_FUNCTION_ARGS) int bit = (i % 8); if (bitmap[byte] & (0x01 << bit)) - dst = hashset_add_element(dst, values[i]); + dst = int4hashset_add_element(dst, values[i]); } @@ -773,10 +805,10 @@ hashset_agg_combine(PG_FUNCTION_ARGS) Datum -hashset_equals(PG_FUNCTION_ARGS) +int4hashset_equals(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); char *bitmap_a; int32 *values_a; @@ -803,7 +835,7 @@ hashset_equals(PG_FUNCTION_ARGS) { int32 value = values_a[i]; - if (!hashset_contains_element(b, value)) + if (!int4hashset_contains_element(b, value)) PG_RETURN_BOOL(false); } } @@ -817,22 +849,22 @@ hashset_equals(PG_FUNCTION_ARGS) Datum -hashset_neq(PG_FUNCTION_ARGS) +int4hashset_neq(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); /* If a is not equal to b, then they are not equal */ - if (!DatumGetBool(DirectFunctionCall2(hashset_equals, PointerGetDatum(a), PointerGetDatum(b)))) + if (!DatumGetBool(DirectFunctionCall2(int4hashset_equals, PointerGetDatum(a), PointerGetDatum(b)))) PG_RETURN_BOOL(true); PG_RETURN_BOOL(false); } -Datum hashset_hash(PG_FUNCTION_ARGS) +Datum int4hashset_hash(PG_FUNCTION_ARGS) { - hashset_t *set = PG_GETARG_HASHSET(0); + int4hashset_t *set = PG_GETARG_INT4HASHSET(0); /* Initial hash value */ uint32 hash = 0; @@ -861,13 +893,13 @@ Datum hashset_hash(PG_FUNCTION_ARGS) Datum -hashset_lt(PG_FUNCTION_ARGS) +int4hashset_lt(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); int32 cmp; - cmp = DatumGetInt32(DirectFunctionCall2(hashset_cmp, + cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp, PointerGetDatum(a), PointerGetDatum(b))); @@ -876,13 +908,13 @@ hashset_lt(PG_FUNCTION_ARGS) Datum -hashset_le(PG_FUNCTION_ARGS) +int4hashset_le(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); int32 cmp; - cmp = DatumGetInt32(DirectFunctionCall2(hashset_cmp, + cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp, PointerGetDatum(a), PointerGetDatum(b))); @@ -891,13 +923,13 @@ hashset_le(PG_FUNCTION_ARGS) Datum -hashset_gt(PG_FUNCTION_ARGS) +int4hashset_gt(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); int32 cmp; - cmp = DatumGetInt32(DirectFunctionCall2(hashset_cmp, + cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp, PointerGetDatum(a), PointerGetDatum(b))); @@ -906,13 +938,13 @@ hashset_gt(PG_FUNCTION_ARGS) Datum -hashset_ge(PG_FUNCTION_ARGS) +int4hashset_ge(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); int32 cmp; - cmp = DatumGetInt32(DirectFunctionCall2(hashset_cmp, + cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp, PointerGetDatum(a), PointerGetDatum(b))); @@ -921,10 +953,10 @@ hashset_ge(PG_FUNCTION_ARGS) Datum -hashset_cmp(PG_FUNCTION_ARGS) +int4hashset_cmp(PG_FUNCTION_ARGS) { - hashset_t *a = PG_GETARG_HASHSET(0); - hashset_t *b = PG_GETARG_HASHSET(1); + int4hashset_t *a = PG_GETARG_INT4HASHSET(0); + int4hashset_t *b = PG_GETARG_INT4HASHSET(1); char *bitmap_a, *bitmap_b; int32 *values_a, *values_b; diff --git a/test/expected/basic.out b/test/expected/basic.out index 5be2501..a793ef2 100644 --- a/test/expected/basic.out +++ b/test/expected/basic.out @@ -1,96 +1,335 @@ -SELECT hashset_sorted('{1}'::hashset); - hashset_sorted ----------------- - {1} -(1 row) - -SELECT hashset_sorted('{1,2}'::hashset); - hashset_sorted ----------------- - {1,2} -(1 row) - -SELECT hashset_sorted('{1,2,3}'::hashset); - hashset_sorted ----------------- - {1,2,3} -(1 row) - -SELECT hashset_sorted('{1,2,3,4}'::hashset); - hashset_sorted ----------------- - {1,2,3,4} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5}'::hashset); - hashset_sorted ----------------- - {1,2,3,4,5} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6}'::hashset); - hashset_sorted ----------------- - {1,2,3,4,5,6} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6,7}'::hashset); - hashset_sorted ------------------ - {1,2,3,4,5,6,7} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6,7,8}'::hashset); - hashset_sorted -------------------- - {1,2,3,4,5,6,7,8} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9}'::hashset); - hashset_sorted ---------------------- - {1,2,3,4,5,6,7,8,9} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10}'::hashset); - hashset_sorted ------------------------- - {1,2,3,4,5,6,7,8,9,10} -(1 row) - -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11}'::hashset); - hashset_sorted +/* + * Hashset Type + */ +SELECT '{}'::int4hashset; -- empty int4hashset + int4hashset +------------- + {} +(1 row) + +SELECT '{1,2,3}'::int4hashset; + int4hashset +------------- + {3,2,1} +(1 row) + +SELECT '{-2147483648,0,2147483647}'::int4hashset; + int4hashset +---------------------------- + {0,2147483647,-2147483648} +(1 row) + +SELECT '{-2147483649}'::int4hashset; -- out of range +ERROR: value "-2147483649}" is out of range for type integer +LINE 1: SELECT '{-2147483649}'::int4hashset; + ^ +SELECT '{2147483648}'::int4hashset; -- out of range +ERROR: value "2147483648}" is out of range for type integer +LINE 1: SELECT '{2147483648}'::int4hashset; + ^ +/* + * Hashset Functions + */ +SELECT int4hashset(); -- init empty int4hashset with no capacity + int4hashset +------------- + {} +(1 row) + +SELECT int4hashset_with_capacity(10); -- init empty int4hashset with specified capacity + int4hashset_with_capacity --------------------------- - {1,2,3,4,5,6,7,8,9,10,11} + {} (1 row) -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12}'::hashset); - hashset_sorted ------------------------------- - {1,2,3,4,5,6,7,8,9,10,11,12} +SELECT hashset_add(int4hashset(), 123); + hashset_add +------------- + {123} (1 row) -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13}'::hashset); - hashset_sorted ---------------------------------- - {1,2,3,4,5,6,7,8,9,10,11,12,13} +SELECT hashset_add(NULL::int4hashset, 123); + hashset_add +------------- + {123} (1 row) -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14}'::hashset); - hashset_sorted ------------------------------------- - {1,2,3,4,5,6,7,8,9,10,11,12,13,14} +SELECT hashset_add('{123}'::int4hashset, 456); + hashset_add +------------- + {456,123} (1 row) -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}'::hashset); - hashset_sorted ---------------------------------------- - {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} +SELECT hashset_contains('{123,456}'::int4hashset, 456); -- true + hashset_contains +------------------ + t (1 row) -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}'::hashset); - hashset_sorted ------------------------------------------- - {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16} +SELECT hashset_contains('{123,456}'::int4hashset, 789); -- false + hashset_contains +------------------ + f +(1 row) + +SELECT hashset_merge('{1,2}'::int4hashset, '{2,3}'::int4hashset); + hashset_merge +--------------- + {3,1,2} +(1 row) + +SELECT hashset_to_array('{1,2,3}'::int4hashset); + hashset_to_array +------------------ + {3,2,1} +(1 row) + +SELECT hashset_count('{1,2,3}'::int4hashset); -- 3 + hashset_count +--------------- + 3 +(1 row) + +SELECT hashset_capacity(int4hashset_with_capacity(10)); -- 10 + hashset_capacity +------------------ + 10 +(1 row) + +/* + * Aggregation Functions + */ +SELECT hashset(i) FROM generate_series(1,10) AS i; + hashset +------------------------ + {8,1,10,3,9,4,6,2,5,7} +(1 row) + +SELECT hashset(h) FROM +( + SELECT hashset(i) AS h FROM generate_series(1,5) AS i + UNION ALL + SELECT hashset(j) AS h FROM generate_series(6,10) AS j +) q; + hashset +------------------------ + {8,1,10,3,9,4,6,2,5,7} +(1 row) + +/* + * Operator Definitions + */ +SELECT '{2}'::int4hashset = '{1}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset = '{2}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset = '{3}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{1,2,3}'::int4hashset = '{1,2,3}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{1,2,3}'::int4hashset = '{2,3,1}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{1,2,3}'::int4hashset = '{4,5,6}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{1,2,3}'::int4hashset = '{1,2}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{1,2,3}'::int4hashset = '{1,2,3,4}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset <> '{1}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset <> '{2}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset <> '{3}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{1,2,3}'::int4hashset <> '{1,2,3}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{1,2,3}'::int4hashset <> '{2,3,1}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{1,2,3}'::int4hashset <> '{4,5,6}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{1,2,3}'::int4hashset <> '{1,2}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{1,2,3}'::int4hashset <> '{1,2,3,4}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +/* + * Hashset Hash Operators + */ +SELECT hashset_hash('{1,2,3}'::int4hashset); + hashset_hash +-------------- + -1778803072 +(1 row) + +SELECT hashset_hash('{3,2,1}'::int4hashset); + hashset_hash +-------------- + -1778803072 +(1 row) + +SELECT COUNT(*), COUNT(DISTINCT h) +FROM +( + SELECT '{1,2,3}'::int4hashset AS h + UNION ALL + SELECT '{3,2,1}'::int4hashset AS h +) q; + count | count +-------+------- + 2 | 1 +(1 row) + +/* + * Hashset Btree Operators + */ +SELECT h FROM +( + SELECT '{2}'::int4hashset AS h + UNION ALL + SELECT '{1}'::int4hashset AS h + UNION ALL + SELECT '{3}'::int4hashset AS h +) q +ORDER BY h; + h +----- + {1} + {2} + {3} +(3 rows) + +SELECT '{2}'::int4hashset < '{1}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset < '{2}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset < '{3}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset <= '{1}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset <= '{2}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset <= '{3}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset > '{1}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset > '{2}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset > '{3}'::int4hashset; -- false + ?column? +---------- + f +(1 row) + +SELECT '{2}'::int4hashset >= '{1}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset >= '{2}'::int4hashset; -- true + ?column? +---------- + t +(1 row) + +SELECT '{2}'::int4hashset >= '{3}'::int4hashset; -- false + ?column? +---------- + f (1 row) diff --git a/test/expected/invalid.out b/test/expected/invalid.out index 0e08925..bd44199 100644 --- a/test/expected/invalid.out +++ b/test/expected/invalid.out @@ -1,4 +1,4 @@ -SELECT '{1,2s}'::hashset; +SELECT '{1,2s}'::int4hashset; ERROR: unexpected character "s" in hashset input -LINE 1: SELECT '{1,2s}'::hashset; +LINE 1: SELECT '{1,2s}'::int4hashset; ^ diff --git a/test/expected/io_varying_lengths.out b/test/expected/io_varying_lengths.out new file mode 100644 index 0000000..45e9fb1 --- /dev/null +++ b/test/expected/io_varying_lengths.out @@ -0,0 +1,100 @@ +/* + * This test verifies the hashset input/output functions for varying + * initial capacities, ensuring functionality across different sizes. + */ +SELECT hashset_sorted('{1}'::int4hashset); + hashset_sorted +---------------- + {1} +(1 row) + +SELECT hashset_sorted('{1,2}'::int4hashset); + hashset_sorted +---------------- + {1,2} +(1 row) + +SELECT hashset_sorted('{1,2,3}'::int4hashset); + hashset_sorted +---------------- + {1,2,3} +(1 row) + +SELECT hashset_sorted('{1,2,3,4}'::int4hashset); + hashset_sorted +---------------- + {1,2,3,4} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5}'::int4hashset); + hashset_sorted +---------------- + {1,2,3,4,5} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6}'::int4hashset); + hashset_sorted +---------------- + {1,2,3,4,5,6} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7}'::int4hashset); + hashset_sorted +----------------- + {1,2,3,4,5,6,7} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8}'::int4hashset); + hashset_sorted +------------------- + {1,2,3,4,5,6,7,8} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9}'::int4hashset); + hashset_sorted +--------------------- + {1,2,3,4,5,6,7,8,9} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10}'::int4hashset); + hashset_sorted +------------------------ + {1,2,3,4,5,6,7,8,9,10} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11}'::int4hashset); + hashset_sorted +--------------------------- + {1,2,3,4,5,6,7,8,9,10,11} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12}'::int4hashset); + hashset_sorted +------------------------------ + {1,2,3,4,5,6,7,8,9,10,11,12} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13}'::int4hashset); + hashset_sorted +--------------------------------- + {1,2,3,4,5,6,7,8,9,10,11,12,13} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14}'::int4hashset); + hashset_sorted +------------------------------------ + {1,2,3,4,5,6,7,8,9,10,11,12,13,14} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}'::int4hashset); + hashset_sorted +--------------------------------------- + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} +(1 row) + +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}'::int4hashset); + hashset_sorted +------------------------------------------ + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16} +(1 row) + diff --git a/test/expected/order.out b/test/expected/order.out index f8f8d5b..089bd15 100644 --- a/test/expected/order.out +++ b/test/expected/order.out @@ -1,128 +1,20 @@ -CREATE TABLE IF NOT EXISTS test_hashset_order (hashset_col hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{1,2,3}'::hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{3,2,1}'::hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{4,5,6}'::hashset); -SELECT COUNT(DISTINCT hashset_col) FROM test_hashset_order; +CREATE TABLE IF NOT EXISTS test_int4hashset_order (int4hashset_col int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{1,2,3}'::int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{3,2,1}'::int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{4,5,6}'::int4hashset); +SELECT COUNT(DISTINCT int4hashset_col) FROM test_int4hashset_order; count ------- 2 (1 row) -SELECT '{2}'::hashset < '{1}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset < '{2}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset < '{3}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset <= '{1}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset <= '{2}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset <= '{3}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset > '{1}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset > '{2}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset > '{3}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset >= '{1}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset >= '{2}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset >= '{3}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset = '{1}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset = '{2}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset = '{3}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset <> '{1}'::hashset; -- true - ?column? ----------- - t -(1 row) - -SELECT '{2}'::hashset <> '{2}'::hashset; -- false - ?column? ----------- - f -(1 row) - -SELECT '{2}'::hashset <> '{3}'::hashset; -- true - ?column? ----------- - t -(1 row) - -CREATE OR REPLACE FUNCTION generate_random_hashset(num_elements INT) -RETURNS hashset AS $$ +CREATE OR REPLACE FUNCTION generate_random_int4hashset(num_elements INT) +RETURNS int4hashset AS $$ DECLARE element INT; - random_set hashset; + random_set int4hashset; BEGIN - random_set := hashset_init(num_elements); + random_set := int4hashset_with_capacity(num_elements); FOR i IN 1..num_elements LOOP element := floor(random() * 1000)::INT; @@ -138,14 +30,14 @@ SELECT setseed(0.123465); (1 row) -CREATE TABLE hashset_order_test AS -SELECT generate_random_hashset(3) AS hashset_col +CREATE TABLE int4hashset_order_test AS +SELECT generate_random_int4hashset(3) AS hashset_col FROM generate_series(1,1000) UNION -SELECT generate_random_hashset(2) +SELECT generate_random_int4hashset(2) FROM generate_series(1,1000); SELECT hashset_col -FROM hashset_order_test +FROM int4hashset_order_test ORDER BY hashset_col LIMIT 20; hashset_col diff --git a/test/expected/prelude.out b/test/expected/prelude.out index b094033..f34e190 100644 --- a/test/expected/prelude.out +++ b/test/expected/prelude.out @@ -1,5 +1,5 @@ CREATE EXTENSION hashset; -CREATE OR REPLACE FUNCTION hashset_sorted(hashset) +CREATE OR REPLACE FUNCTION hashset_sorted(int4hashset) RETURNS TEXT AS $$ SELECT array_agg(i ORDER BY i::int)::text diff --git a/test/expected/random.out b/test/expected/random.out index 889f5ca..9d9026b 100644 --- a/test/expected/random.out +++ b/test/expected/random.out @@ -5,7 +5,7 @@ SELECT setseed(0.12345); (1 row) \set MAX_INT 2147483647 -CREATE TABLE hashset_random_numbers AS +CREATE TABLE hashset_random_int4_numbers AS SELECT (random()*:MAX_INT)::int AS i FROM generate_series(1,(random()*10000)::int) @@ -15,8 +15,8 @@ SELECT FROM ( SELECT - hashset_sorted(hashset(format('{%s}',string_agg(i::text,',')))) - FROM hashset_random_numbers + hashset_sorted(int4hashset(format('{%s}',string_agg(i::text,',')))) + FROM hashset_random_int4_numbers ) q; md5 ---------------------------------- @@ -29,7 +29,7 @@ FROM ( SELECT format('{%s}',string_agg(i::text,',' ORDER BY i)) AS input_sorted - FROM hashset_random_numbers + FROM hashset_random_int4_numbers ) q; md5 ---------------------------------- diff --git a/test/expected/table.out b/test/expected/table.out index 8d4fbe2..3c020b6 100644 --- a/test/expected/table.out +++ b/test/expected/table.out @@ -1,6 +1,6 @@ CREATE TABLE users ( user_id int PRIMARY KEY, - user_likes hashset DEFAULT hashset_init(2) + user_likes int4hashset DEFAULT int4hashset_with_capacity(2) ); INSERT INTO users (user_id) VALUES (1); UPDATE users SET user_likes = hashset_add(user_likes, 101) WHERE user_id = 1; diff --git a/test/sql/basic.sql b/test/sql/basic.sql index 662e65a..563c626 100644 --- a/test/sql/basic.sql +++ b/test/sql/basic.sql @@ -1,16 +1,107 @@ -SELECT hashset_sorted('{1}'::hashset); -SELECT hashset_sorted('{1,2}'::hashset); -SELECT hashset_sorted('{1,2,3}'::hashset); -SELECT hashset_sorted('{1,2,3,4}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}'::hashset); -SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}'::hashset); +/* + * Hashset Type + */ + +SELECT '{}'::int4hashset; -- empty int4hashset +SELECT '{1,2,3}'::int4hashset; +SELECT '{-2147483648,0,2147483647}'::int4hashset; +SELECT '{-2147483649}'::int4hashset; -- out of range +SELECT '{2147483648}'::int4hashset; -- out of range + +/* + * Hashset Functions + */ + +SELECT int4hashset(); -- init empty int4hashset with no capacity +SELECT int4hashset_with_capacity(10); -- init empty int4hashset with specified capacity +SELECT hashset_add(int4hashset(), 123); +SELECT hashset_add(NULL::int4hashset, 123); +SELECT hashset_add('{123}'::int4hashset, 456); +SELECT hashset_contains('{123,456}'::int4hashset, 456); -- true +SELECT hashset_contains('{123,456}'::int4hashset, 789); -- false +SELECT hashset_merge('{1,2}'::int4hashset, '{2,3}'::int4hashset); +SELECT hashset_to_array('{1,2,3}'::int4hashset); +SELECT hashset_count('{1,2,3}'::int4hashset); -- 3 +SELECT hashset_capacity(int4hashset_with_capacity(10)); -- 10 + +/* + * Aggregation Functions + */ + +SELECT hashset(i) FROM generate_series(1,10) AS i; + +SELECT hashset(h) FROM +( + SELECT hashset(i) AS h FROM generate_series(1,5) AS i + UNION ALL + SELECT hashset(j) AS h FROM generate_series(6,10) AS j +) q; + +/* + * Operator Definitions + */ + +SELECT '{2}'::int4hashset = '{1}'::int4hashset; -- false +SELECT '{2}'::int4hashset = '{2}'::int4hashset; -- true +SELECT '{2}'::int4hashset = '{3}'::int4hashset; -- false + +SELECT '{1,2,3}'::int4hashset = '{1,2,3}'::int4hashset; -- true +SELECT '{1,2,3}'::int4hashset = '{2,3,1}'::int4hashset; -- true +SELECT '{1,2,3}'::int4hashset = '{4,5,6}'::int4hashset; -- false +SELECT '{1,2,3}'::int4hashset = '{1,2}'::int4hashset; -- false +SELECT '{1,2,3}'::int4hashset = '{1,2,3,4}'::int4hashset; -- false + +SELECT '{2}'::int4hashset <> '{1}'::int4hashset; -- true +SELECT '{2}'::int4hashset <> '{2}'::int4hashset; -- false +SELECT '{2}'::int4hashset <> '{3}'::int4hashset; -- true + +SELECT '{1,2,3}'::int4hashset <> '{1,2,3}'::int4hashset; -- false +SELECT '{1,2,3}'::int4hashset <> '{2,3,1}'::int4hashset; -- false +SELECT '{1,2,3}'::int4hashset <> '{4,5,6}'::int4hashset; -- true +SELECT '{1,2,3}'::int4hashset <> '{1,2}'::int4hashset; -- true +SELECT '{1,2,3}'::int4hashset <> '{1,2,3,4}'::int4hashset; -- true + +/* + * Hashset Hash Operators + */ + +SELECT hashset_hash('{1,2,3}'::int4hashset); +SELECT hashset_hash('{3,2,1}'::int4hashset); + +SELECT COUNT(*), COUNT(DISTINCT h) +FROM +( + SELECT '{1,2,3}'::int4hashset AS h + UNION ALL + SELECT '{3,2,1}'::int4hashset AS h +) q; + +/* + * Hashset Btree Operators + */ + +SELECT h FROM +( + SELECT '{2}'::int4hashset AS h + UNION ALL + SELECT '{1}'::int4hashset AS h + UNION ALL + SELECT '{3}'::int4hashset AS h +) q +ORDER BY h; + +SELECT '{2}'::int4hashset < '{1}'::int4hashset; -- false +SELECT '{2}'::int4hashset < '{2}'::int4hashset; -- false +SELECT '{2}'::int4hashset < '{3}'::int4hashset; -- true + +SELECT '{2}'::int4hashset <= '{1}'::int4hashset; -- false +SELECT '{2}'::int4hashset <= '{2}'::int4hashset; -- true +SELECT '{2}'::int4hashset <= '{3}'::int4hashset; -- true + +SELECT '{2}'::int4hashset > '{1}'::int4hashset; -- true +SELECT '{2}'::int4hashset > '{2}'::int4hashset; -- false +SELECT '{2}'::int4hashset > '{3}'::int4hashset; -- false + +SELECT '{2}'::int4hashset >= '{1}'::int4hashset; -- true +SELECT '{2}'::int4hashset >= '{2}'::int4hashset; -- true +SELECT '{2}'::int4hashset >= '{3}'::int4hashset; -- false diff --git a/test/sql/invalid.sql b/test/sql/invalid.sql index f1a9488..43689ab 100644 --- a/test/sql/invalid.sql +++ b/test/sql/invalid.sql @@ -1 +1 @@ -SELECT '{1,2s}'::hashset; +SELECT '{1,2s}'::int4hashset; diff --git a/test/sql/io_varying_lengths.sql b/test/sql/io_varying_lengths.sql new file mode 100644 index 0000000..8acb6b8 --- /dev/null +++ b/test/sql/io_varying_lengths.sql @@ -0,0 +1,21 @@ +/* + * This test verifies the hashset input/output functions for varying + * initial capacities, ensuring functionality across different sizes. + */ + +SELECT hashset_sorted('{1}'::int4hashset); +SELECT hashset_sorted('{1,2}'::int4hashset); +SELECT hashset_sorted('{1,2,3}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}'::int4hashset); +SELECT hashset_sorted('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}'::int4hashset); diff --git a/test/sql/order.sql b/test/sql/order.sql index 1780c0b..2dcdb39 100644 --- a/test/sql/order.sql +++ b/test/sql/order.sql @@ -1,40 +1,16 @@ -CREATE TABLE IF NOT EXISTS test_hashset_order (hashset_col hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{1,2,3}'::hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{3,2,1}'::hashset); -INSERT INTO test_hashset_order (hashset_col) VALUES ('{4,5,6}'::hashset); -SELECT COUNT(DISTINCT hashset_col) FROM test_hashset_order; - -SELECT '{2}'::hashset < '{1}'::hashset; -- false -SELECT '{2}'::hashset < '{2}'::hashset; -- false -SELECT '{2}'::hashset < '{3}'::hashset; -- true - -SELECT '{2}'::hashset <= '{1}'::hashset; -- false -SELECT '{2}'::hashset <= '{2}'::hashset; -- true -SELECT '{2}'::hashset <= '{3}'::hashset; -- true - -SELECT '{2}'::hashset > '{1}'::hashset; -- true -SELECT '{2}'::hashset > '{2}'::hashset; -- false -SELECT '{2}'::hashset > '{3}'::hashset; -- false - -SELECT '{2}'::hashset >= '{1}'::hashset; -- true -SELECT '{2}'::hashset >= '{2}'::hashset; -- true -SELECT '{2}'::hashset >= '{3}'::hashset; -- false - -SELECT '{2}'::hashset = '{1}'::hashset; -- false -SELECT '{2}'::hashset = '{2}'::hashset; -- true -SELECT '{2}'::hashset = '{3}'::hashset; -- false - -SELECT '{2}'::hashset <> '{1}'::hashset; -- true -SELECT '{2}'::hashset <> '{2}'::hashset; -- false -SELECT '{2}'::hashset <> '{3}'::hashset; -- true - -CREATE OR REPLACE FUNCTION generate_random_hashset(num_elements INT) -RETURNS hashset AS $$ +CREATE TABLE IF NOT EXISTS test_int4hashset_order (int4hashset_col int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{1,2,3}'::int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{3,2,1}'::int4hashset); +INSERT INTO test_int4hashset_order (int4hashset_col) VALUES ('{4,5,6}'::int4hashset); +SELECT COUNT(DISTINCT int4hashset_col) FROM test_int4hashset_order; + +CREATE OR REPLACE FUNCTION generate_random_int4hashset(num_elements INT) +RETURNS int4hashset AS $$ DECLARE element INT; - random_set hashset; + random_set int4hashset; BEGIN - random_set := hashset_init(num_elements); + random_set := int4hashset_with_capacity(num_elements); FOR i IN 1..num_elements LOOP element := floor(random() * 1000)::INT; @@ -47,14 +23,14 @@ $$ LANGUAGE plpgsql; SELECT setseed(0.123465); -CREATE TABLE hashset_order_test AS -SELECT generate_random_hashset(3) AS hashset_col +CREATE TABLE int4hashset_order_test AS +SELECT generate_random_int4hashset(3) AS hashset_col FROM generate_series(1,1000) UNION -SELECT generate_random_hashset(2) +SELECT generate_random_int4hashset(2) FROM generate_series(1,1000); SELECT hashset_col -FROM hashset_order_test +FROM int4hashset_order_test ORDER BY hashset_col LIMIT 20; diff --git a/test/sql/prelude.sql b/test/sql/prelude.sql index ccc0595..2fee0fc 100644 --- a/test/sql/prelude.sql +++ b/test/sql/prelude.sql @@ -1,6 +1,6 @@ CREATE EXTENSION hashset; -CREATE OR REPLACE FUNCTION hashset_sorted(hashset) +CREATE OR REPLACE FUNCTION hashset_sorted(int4hashset) RETURNS TEXT AS $$ SELECT array_agg(i ORDER BY i::int)::text diff --git a/test/sql/random.sql b/test/sql/random.sql index 16c9084..7cc8f87 100644 --- a/test/sql/random.sql +++ b/test/sql/random.sql @@ -2,7 +2,7 @@ SELECT setseed(0.12345); \set MAX_INT 2147483647 -CREATE TABLE hashset_random_numbers AS +CREATE TABLE hashset_random_int4_numbers AS SELECT (random()*:MAX_INT)::int AS i FROM generate_series(1,(random()*10000)::int) @@ -13,8 +13,8 @@ SELECT FROM ( SELECT - hashset_sorted(hashset(format('{%s}',string_agg(i::text,',')))) - FROM hashset_random_numbers + hashset_sorted(int4hashset(format('{%s}',string_agg(i::text,',')))) + FROM hashset_random_int4_numbers ) q; SELECT @@ -23,5 +23,5 @@ FROM ( SELECT format('{%s}',string_agg(i::text,',' ORDER BY i)) AS input_sorted - FROM hashset_random_numbers + FROM hashset_random_int4_numbers ) q; diff --git a/test/sql/table.sql b/test/sql/table.sql index d848207..a63253f 100644 --- a/test/sql/table.sql +++ b/test/sql/table.sql @@ -1,6 +1,6 @@ CREATE TABLE users ( user_id int PRIMARY KEY, - user_likes hashset DEFAULT hashset_init(2) + user_likes int4hashset DEFAULT int4hashset_with_capacity(2) ); INSERT INTO users (user_id) VALUES (1); UPDATE users SET user_likes = hashset_add(user_likes, 101) WHERE user_id = 1;