commit 1ee0df053d139f98f69090672ecc33afb96e9818
Author: Joel Jakobsson <joel@compiler.org>
Date:   Tue Jun 20 12:42:03 2023 +0200

    Apply STRICT to hashset functions; clean up null handling in hashset-api.c
    
    Set hashset functions to be STRICT, thereby letting the system reject null
    inputs automatically. This change reflects the nature of hashset as an
    implementation of a set-theoretic system, where null values are conceptually
    unusual.
    
    Alongside, the hashset-api.c code has been refactored for clarity, consolidating
    null checks and assignments into single lines.
    
    A 'strict' test case has been added to account for these changes.

diff --git a/Makefile b/Makefile
index 85e7691..cfb8362 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 io_varying_lengths random table invalid parsing reported_bugs
+REGRESS = prelude basic io_varying_lengths random table invalid parsing reported_bugs strict
 REGRESS_OPTS = --inputdir=test
 
 PG_CONFIG = pg_config
diff --git a/hashset--0.0.1.sql b/hashset--0.0.1.sql
index a155190..d48260f 100644
--- a/hashset--0.0.1.sql
+++ b/hashset--0.0.1.sql
@@ -50,42 +50,42 @@ LANGUAGE C IMMUTABLE;
 CREATE OR REPLACE FUNCTION hashset_add(int4hashset, int)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_add'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_contains(int4hashset, int)
 RETURNS bool
 AS 'hashset', 'int4hashset_contains'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_merge(int4hashset, int4hashset)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_merge'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_to_array(int4hashset)
 RETURNS int[]
 AS 'hashset', 'int4hashset_to_array'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_count(int4hashset)
 RETURNS bigint
 AS 'hashset', 'int4hashset_count'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_capacity(int4hashset)
 RETURNS bigint
 AS 'hashset', 'int4hashset_capacity'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_collisions(int4hashset)
 RETURNS bigint
 AS 'hashset', 'int4hashset_collisions'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_max_collisions(int4hashset)
 RETURNS bigint
 AS 'hashset', 'int4hashset_max_collisions'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION int4_add_int4hashset(int4, int4hashset)
 RETURNS int4hashset
@@ -96,17 +96,17 @@ IMMUTABLE PARALLEL SAFE STRICT COST 1;
 CREATE OR REPLACE FUNCTION hashset_intersection(int4hashset, int4hashset)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_intersection'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_difference(int4hashset, int4hashset)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_difference'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION hashset_symmetric_difference(int4hashset, int4hashset)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_symmetric_difference'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 /*
  * Aggregation Functions
@@ -120,7 +120,7 @@ LANGUAGE C IMMUTABLE;
 CREATE OR REPLACE FUNCTION int4hashset_agg_final(p_pointer internal)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_agg_final'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION int4hashset_agg_combine(p_pointer internal, p_pointer2 internal)
 RETURNS internal
@@ -143,7 +143,7 @@ LANGUAGE C IMMUTABLE;
 CREATE OR REPLACE FUNCTION int4hashset_agg_final(p_pointer internal)
 RETURNS int4hashset
 AS 'hashset', 'int4hashset_agg_final'
-LANGUAGE C IMMUTABLE;
+LANGUAGE C IMMUTABLE STRICT;
 
 CREATE OR REPLACE FUNCTION int4hashset_agg_combine(p_pointer internal, p_pointer2 internal)
 RETURNS internal
diff --git a/hashset-api.c b/hashset-api.c
index 3feb06d..e00ef0c 100644
--- a/hashset-api.c
+++ b/hashset-api.c
@@ -318,33 +318,10 @@ int4hashset_recv(PG_FUNCTION_ARGS)
 Datum
 int4hashset_add(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *set;
-
-	if (PG_ARGISNULL(1))
-	{
-		if (PG_ARGISNULL(0))
-			PG_RETURN_NULL();
-
-		PG_RETURN_DATUM(PG_GETARG_DATUM(0));
-	}
-
-	/* if there's no hashset allocated, create it now */
-	if (PG_ARGISNULL(0))
-	{
-		set = int4hashset_allocate(
-			DEFAULT_INITIAL_CAPACITY,
-			DEFAULT_LOAD_FACTOR,
-			DEFAULT_GROWTH_FACTOR,
-			DEFAULT_HASHFN_ID
-		);
-	}
-	else
-	{
-		/* make sure we are working with a non-toasted and non-shared copy of the input */
-		set = PG_GETARG_INT4HASHSET_COPY(0);
-	}
-
-	set = int4hashset_add_element(set, PG_GETARG_INT32(1));
+	int4hashset_t *set = int4hashset_add_element(
+		PG_GETARG_INT4HASHSET_COPY(0),
+		PG_GETARG_INT32(1)
+	);
 
 	PG_RETURN_POINTER(set);
 }
@@ -352,14 +329,8 @@ int4hashset_add(PG_FUNCTION_ARGS)
 Datum
 int4hashset_contains(PG_FUNCTION_ARGS)
 {
-	int4hashset_t  *set;
-	int32			value;
-
-	if (PG_ARGISNULL(1) || PG_ARGISNULL(0))
-		PG_RETURN_BOOL(false);
-
-	set = PG_GETARG_INT4HASHSET(0);
-	value = PG_GETARG_INT32(1);
+	int4hashset_t  *set = PG_GETARG_INT4HASHSET(0);
+	int32			value = PG_GETARG_INT32(1);
 
 	PG_RETURN_BOOL(int4hashset_contains_element(set, value));
 }
@@ -367,12 +338,7 @@ int4hashset_contains(PG_FUNCTION_ARGS)
 Datum
 int4hashset_count(PG_FUNCTION_ARGS)
 {
-	int4hashset_t	*set;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
-	set = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t	*set = PG_GETARG_INT4HASHSET(0);
 
 	PG_RETURN_INT64(set->nelements);
 }
@@ -381,25 +347,10 @@ Datum
 int4hashset_merge(PG_FUNCTION_ARGS)
 {
 	int				i;
-
-	int4hashset_t  *seta;
-	int4hashset_t  *setb;
-
-	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_INT4HASHSET(0));
-	else if (PG_ARGISNULL(0))
-		PG_RETURN_POINTER(PG_GETARG_INT4HASHSET(1));
-
-	seta = PG_GETARG_INT4HASHSET_COPY(0);
-	setb = PG_GETARG_INT4HASHSET(1);
-
-	bitmap = setb->data;
-	values = (int32 *) (setb->data + CEIL_DIV(setb->capacity, 8));
+	int4hashset_t  *seta = PG_GETARG_INT4HASHSET_COPY(0);
+	int4hashset_t  *setb = PG_GETARG_INT4HASHSET(1);
+	char		   *bitmap = setb->data;
+	int32_t		   *values = (int32 *) (bitmap + CEIL_DIV(setb->capacity, 8));
 
 	for (i = 0; i < setb->capacity; i++)
 	{
@@ -416,11 +367,11 @@ int4hashset_merge(PG_FUNCTION_ARGS)
 Datum
 int4hashset_init(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *set;
-	int32 initial_capacity = PG_GETARG_INT32(0);
-	float4 load_factor = PG_GETARG_FLOAT4(1);
-	float4 growth_factor = PG_GETARG_FLOAT4(2);
-	int32 hashfn_id = PG_GETARG_INT32(3);
+	int4hashset_t  *set;
+	int32			initial_capacity = PG_GETARG_INT32(0);
+	float4			load_factor = PG_GETARG_FLOAT4(1);
+	float4			growth_factor = PG_GETARG_FLOAT4(2);
+	int32			hashfn_id = PG_GETARG_INT32(3);
 
 	/* Validate input arguments */
 	if (!(initial_capacity >= 0))
@@ -466,12 +417,7 @@ int4hashset_init(PG_FUNCTION_ARGS)
 Datum
 int4hashset_capacity(PG_FUNCTION_ARGS)
 {
-	int4hashset_t	*set;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
-	set = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *set = PG_GETARG_INT4HASHSET(0);
 
 	PG_RETURN_INT64(set->capacity);
 }
@@ -479,12 +425,7 @@ int4hashset_capacity(PG_FUNCTION_ARGS)
 Datum
 int4hashset_collisions(PG_FUNCTION_ARGS)
 {
-	int4hashset_t	*set;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
-	set = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *set = PG_GETARG_INT4HASHSET(0);
 
 	PG_RETURN_INT64(set->ncollisions);
 }
@@ -492,12 +433,7 @@ int4hashset_collisions(PG_FUNCTION_ARGS)
 Datum
 int4hashset_max_collisions(PG_FUNCTION_ARGS)
 {
-	int4hashset_t	*set;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
-	set = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *set = PG_GETARG_INT4HASHSET(0);
 
 	PG_RETURN_INT64(set->max_collisions);
 }
@@ -505,11 +441,10 @@ int4hashset_max_collisions(PG_FUNCTION_ARGS)
 Datum
 int4hashset_agg_add(PG_FUNCTION_ARGS)
 {
+	MemoryContext	aggcontext;
 	MemoryContext	oldcontext;
 	int4hashset_t  *state;
 
-	MemoryContext	aggcontext;
-
 	/* cannot be called directly because of internal-type argument */
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
 		elog(ERROR, "hashset_add_add called in non-aggregate context");
@@ -552,10 +487,9 @@ int4hashset_agg_add(PG_FUNCTION_ARGS)
 Datum
 int4hashset_agg_add_set(PG_FUNCTION_ARGS)
 {
-	MemoryContext	oldcontext;
-	int4hashset_t  *state;
-
 	MemoryContext   aggcontext;
+	MemoryContext	oldcontext;
+	int4hashset_t  *state;
 
 	/* cannot be called directly because of internal-type argument */
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
@@ -620,9 +554,6 @@ int4hashset_agg_add_set(PG_FUNCTION_ARGS)
 Datum
 int4hashset_agg_final(PG_FUNCTION_ARGS)
 {
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
 	PG_RETURN_POINTER(PG_GETARG_POINTER(0));
 }
 
@@ -634,7 +565,6 @@ int4hashset_agg_combine(PG_FUNCTION_ARGS)
 	int4hashset_t  *dst;
 	MemoryContext	aggcontext;
 	MemoryContext	oldcontext;
-
 	char		   *bitmap;
 	int32		   *values;
 
@@ -694,13 +624,9 @@ int4hashset_to_array(PG_FUNCTION_ARGS)
 	int4hashset_t	   *set;
 	int32			   *values;
 	int					nvalues;
-
 	char			   *sbitmap;
 	int32			   *svalues;
 
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-
 	set = PG_GETARG_INT4HASHSET(0);
 
 	sbitmap = set->data;
@@ -728,12 +654,11 @@ int4hashset_to_array(PG_FUNCTION_ARGS)
 Datum
 int4hashset_equals(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-	int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-
-	char *bitmap_a;
-	int32 *values_a;
-	int i;
+	int				i;
+	int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+	char		   *bitmap_a;
+	int32		   *values_a;
 
 	/*
 	 * Check if the number of elements is the same
@@ -794,9 +719,9 @@ Datum int4hashset_hash(PG_FUNCTION_ARGS)
 Datum
 int4hashset_lt(PG_FUNCTION_ARGS)
 {
-    int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-    int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-    int32 cmp;
+    int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+    int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+    int32			cmp;
 
     cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp,
                                             PointerGetDatum(a),
@@ -809,9 +734,9 @@ int4hashset_lt(PG_FUNCTION_ARGS)
 Datum
 int4hashset_le(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-	int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-	int32 cmp;
+	int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+	int32			cmp;
 
 	cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp,
 											PointerGetDatum(a),
@@ -824,9 +749,9 @@ int4hashset_le(PG_FUNCTION_ARGS)
 Datum
 int4hashset_gt(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-	int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-	int32 cmp;
+	int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+	int32			cmp;
 
 	cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp,
 											PointerGetDatum(a),
@@ -839,9 +764,9 @@ int4hashset_gt(PG_FUNCTION_ARGS)
 Datum
 int4hashset_ge(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-	int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-	int32 cmp;
+	int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+	int32			cmp;
 
 	cmp = DatumGetInt32(DirectFunctionCall2(int4hashset_cmp,
 											PointerGetDatum(a),
@@ -853,10 +778,10 @@ int4hashset_ge(PG_FUNCTION_ARGS)
 Datum
 int4hashset_cmp(PG_FUNCTION_ARGS)
 {
-	int4hashset_t *a = PG_GETARG_INT4HASHSET(0);
-	int4hashset_t *b = PG_GETARG_INT4HASHSET(1);
-	int32		  *elements_a;
-	int32		  *elements_b;
+	int4hashset_t  *a = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *b = PG_GETARG_INT4HASHSET(1);
+	int32		   *elements_a;
+	int32		   *elements_b;
 
 	/*
 	 * Compare the hashes first, if they are different,
@@ -914,17 +839,11 @@ Datum
 int4hashset_intersection(PG_FUNCTION_ARGS)
 {
 	int				i;
-	int4hashset_t  *seta;
-	int4hashset_t  *setb;
+	int4hashset_t  *seta = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *setb = PG_GETARG_INT4HASHSET(1);
+	char		   *bitmap = setb->data;
+	int32_t		   *values = (int32_t *)(bitmap + CEIL_DIV(setb->capacity, 8));
 	int4hashset_t  *intersection;
-	char		   *bitmap;
-	int32_t		   *values;
-
-	if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
-		PG_RETURN_NULL();
-
-	seta = PG_GETARG_INT4HASHSET(0);
-	setb = PG_GETARG_INT4HASHSET(1);
 
 	intersection = int4hashset_allocate(
 		seta->capacity,
@@ -933,9 +852,6 @@ int4hashset_intersection(PG_FUNCTION_ARGS)
 		DEFAULT_HASHFN_ID
 	);
 
-	bitmap = setb->data;
-	values = (int32_t *)(setb->data + CEIL_DIV(setb->capacity, 8));
-
 	for (i = 0; i < setb->capacity; i++)
 	{
 		int byte = (i / 8);
@@ -955,17 +871,11 @@ Datum
 int4hashset_difference(PG_FUNCTION_ARGS)
 {
 	int				i;
-	int4hashset_t	*seta;
-	int4hashset_t	*setb;
+	int4hashset_t	*seta = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t	*setb = PG_GETARG_INT4HASHSET(1);
 	int4hashset_t	*difference;
-	char			*bitmap;
-	int32_t			*values;
-
-	if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
-		PG_RETURN_NULL();
-
-	seta = PG_GETARG_INT4HASHSET(0);
-	setb = PG_GETARG_INT4HASHSET(1);
+	char			*bitmap = seta->data;
+	int32_t			*values = (int32_t *)(bitmap + CEIL_DIV(seta->capacity, 8));
 
 	difference = int4hashset_allocate(
 		seta->capacity,
@@ -974,9 +884,6 @@ int4hashset_difference(PG_FUNCTION_ARGS)
 		DEFAULT_HASHFN_ID
 	);
 
-	bitmap = seta->data;
-	values = (int32_t *)(seta->data + CEIL_DIV(seta->capacity, 8));
-
 	for (i = 0; i < seta->capacity; i++)
 	{
 		int byte = (i / 8);
@@ -996,27 +903,13 @@ Datum
 int4hashset_symmetric_difference(PG_FUNCTION_ARGS)
 {
 	int				i;
-	int4hashset_t  *seta;
-	int4hashset_t  *setb;
+	int4hashset_t  *seta = PG_GETARG_INT4HASHSET(0);
+	int4hashset_t  *setb = PG_GETARG_INT4HASHSET(1);
 	int4hashset_t  *result;
-	char		   *bitmapa;
-	char		   *bitmapb;
-	int32_t		   *valuesa;
-	int32_t		   *valuesb;
-
-	if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
-		ereport(ERROR,
-				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-				 errmsg("hashset arguments cannot be null")));
-
-	seta = PG_GETARG_INT4HASHSET(0);
-	setb = PG_GETARG_INT4HASHSET(1);
-
-	bitmapa = seta->data;
-	valuesa = (int32 *) (seta->data + CEIL_DIV(seta->capacity, 8));
-
-	bitmapb = setb->data;
-	valuesb = (int32 *) (setb->data + CEIL_DIV(setb->capacity, 8));
+	char		   *bitmapa = seta->data;
+	char		   *bitmapb = setb->data;
+	int32_t		   *valuesa = (int32 *) (bitmapa + CEIL_DIV(seta->capacity, 8));
+	int32_t		   *valuesb = (int32 *) (bitmapb + CEIL_DIV(setb->capacity, 8));
 
 	result = int4hashset_allocate(
 		seta->nelements + setb->nelements,
diff --git a/test/expected/basic.out b/test/expected/basic.out
index b89ab52..b5326f2 100644
--- a/test/expected/basic.out
+++ b/test/expected/basic.out
@@ -53,12 +53,6 @@ SELECT hashset_add(int4hashset(), 123);
  {123}
 (1 row)
 
-SELECT hashset_add(NULL::int4hashset, 123);
- hashset_add 
--------------
- {123}
-(1 row)
-
 SELECT hashset_add('{123}'::int4hashset, 456);
  hashset_add 
 -------------
diff --git a/test/expected/strict.out b/test/expected/strict.out
new file mode 100644
index 0000000..4a9d904
--- /dev/null
+++ b/test/expected/strict.out
@@ -0,0 +1,114 @@
+/*
+ * Test to verify all relevant functions return NULL if any of
+ * the input parameters are NULL, i.e. testing that they are declared as STRICT
+ */
+SELECT hashset_add(int4hashset(), NULL::int);
+ hashset_add 
+-------------
+ 
+(1 row)
+
+SELECT hashset_add(NULL::int4hashset, 123::int);
+ hashset_add 
+-------------
+ 
+(1 row)
+
+SELECT hashset_contains('{123,456}'::int4hashset, NULL::int);
+ hashset_contains 
+------------------
+ 
+(1 row)
+
+SELECT hashset_contains(NULL::int4hashset, 456::int);
+ hashset_contains 
+------------------
+ 
+(1 row)
+
+SELECT hashset_merge('{1,2}'::int4hashset, NULL::int4hashset);
+ hashset_merge 
+---------------
+ 
+(1 row)
+
+SELECT hashset_merge(NULL::int4hashset, '{2,3}'::int4hashset);
+ hashset_merge 
+---------------
+ 
+(1 row)
+
+SELECT hashset_to_array(NULL::int4hashset);
+ hashset_to_array 
+------------------
+ 
+(1 row)
+
+SELECT hashset_count(NULL::int4hashset);
+ hashset_count 
+---------------
+              
+(1 row)
+
+SELECT hashset_capacity(NULL::int4hashset);
+ hashset_capacity 
+------------------
+                 
+(1 row)
+
+SELECT hashset_intersection('{1,2}'::int4hashset,NULL::int4hashset);
+ hashset_intersection 
+----------------------
+ 
+(1 row)
+
+SELECT hashset_intersection(NULL::int4hashset,'{2,3}'::int4hashset);
+ hashset_intersection 
+----------------------
+ 
+(1 row)
+
+SELECT hashset_difference('{1,2}'::int4hashset,NULL::int4hashset);
+ hashset_difference 
+--------------------
+ 
+(1 row)
+
+SELECT hashset_difference(NULL::int4hashset,'{2,3}'::int4hashset);
+ hashset_difference 
+--------------------
+ 
+(1 row)
+
+SELECT hashset_symmetric_difference('{1,2}'::int4hashset,NULL::int4hashset);
+ hashset_symmetric_difference 
+------------------------------
+ 
+(1 row)
+
+SELECT hashset_symmetric_difference(NULL::int4hashset,'{2,3}'::int4hashset);
+ hashset_symmetric_difference 
+------------------------------
+ 
+(1 row)
+
+/*
+ * For convenience, hashset_agg() is not STRICT and just ignore NULL values
+ */
+SELECT hashset_agg(i) FROM (VALUES (NULL::int),(1::int),(2::int)) q(i);
+ hashset_agg 
+-------------
+ {1,2}
+(1 row)
+
+SELECT hashset_agg(h) FROM
+(
+    SELECT NULL::int4hashset AS h
+    UNION ALL
+    SELECT hashset_agg(j) AS h FROM generate_series(6,10) AS j
+) q;
+ hashset_agg  
+--------------
+ {6,7,8,9,10}
+(1 row)
+
diff --git a/test/sql/basic.sql b/test/sql/basic.sql
index 8688666..061794c 100644
--- a/test/sql/basic.sql
+++ b/test/sql/basic.sql
@@ -20,7 +20,6 @@ SELECT int4hashset(
     hashfn_id := 1
 );
 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
diff --git a/test/sql/strict.sql b/test/sql/strict.sql
new file mode 100644
index 0000000..d0f33bd
--- /dev/null
+++ b/test/sql/strict.sql
@@ -0,0 +1,32 @@
+/*
+ * Test to verify all relevant functions return NULL if any of
+ * the input parameters are NULL, i.e. testing that they are declared as STRICT
+ */
+
+SELECT hashset_add(int4hashset(), NULL::int);
+SELECT hashset_add(NULL::int4hashset, 123::int);
+SELECT hashset_contains('{123,456}'::int4hashset, NULL::int);
+SELECT hashset_contains(NULL::int4hashset, 456::int);
+SELECT hashset_merge('{1,2}'::int4hashset, NULL::int4hashset);
+SELECT hashset_merge(NULL::int4hashset, '{2,3}'::int4hashset);
+SELECT hashset_to_array(NULL::int4hashset);
+SELECT hashset_count(NULL::int4hashset);
+SELECT hashset_capacity(NULL::int4hashset);
+SELECT hashset_intersection('{1,2}'::int4hashset,NULL::int4hashset);
+SELECT hashset_intersection(NULL::int4hashset,'{2,3}'::int4hashset);
+SELECT hashset_difference('{1,2}'::int4hashset,NULL::int4hashset);
+SELECT hashset_difference(NULL::int4hashset,'{2,3}'::int4hashset);
+SELECT hashset_symmetric_difference('{1,2}'::int4hashset,NULL::int4hashset);
+SELECT hashset_symmetric_difference(NULL::int4hashset,'{2,3}'::int4hashset);
+
+/*
+ * For convenience, hashset_agg() is not STRICT and just ignore NULL values
+ */
+SELECT hashset_agg(i) FROM (VALUES (NULL::int),(1::int),(2::int)) q(i);
+
+SELECT hashset_agg(h) FROM
+(
+    SELECT NULL::int4hashset AS h
+    UNION ALL
+    SELECT hashset_agg(j) AS h FROM generate_series(6,10) AS j
+) q;
