diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index 8ef9fce..fd7890e
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 182,188 ****
! Comparison Operators
comparison
--- 182,188 ----
! Comparison Functions and Operators
comparison
***************
*** 191,200 ****
The usual comparison operators are available, shown in .
!
Comparison Operators
--- 191,200 ----
The usual comparison operators are available, shown in .
!
Comparison Operators
***************
*** 437,442 ****
--- 437,479 ----
-->
+
+ Comparison Functions
+
+
+
+ Function
+ Description
+ Example
+ Example Result
+
+
+
+
+
+
+ num_notnulls
+
+ num_notnulls(VARIADIC "any")
+
+ Returns the number of not NULL input arguments
+ num_nulls(1, NULL, 2)
+ 2
+
+
+
+
+ num_nulls
+
+ num_nulls(VARIADIC "any")
+
+ Returns the number of NULL input arguments
+ num_nulls(1, NULL, 2)
+ 1
+
+
+
+
*************** table2-mapping
*** 10307,10313 ****
The standard comparison operators shown in are available for
jsonb, but not for json. They follow the
ordering rules for B-tree operations outlined at .
--- 10344,10350 ----
The standard comparison operators shown in are available for
jsonb, but not for json. They follow the
ordering rules for B-tree operations outlined at .
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index 6a306f3..35810d1
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 43,48 ****
--- 43,160 ----
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
+ /*
+ * Collect info about NULL arguments. Returns true when result values
+ * are valid.
+ */
+ static bool
+ count_nulls(FunctionCallInfo fcinfo,
+ int32 *nargs, int32 *nulls)
+ {
+ int32 count = 0;
+ int i;
+
+ if (get_fn_expr_variadic(fcinfo->flinfo))
+ {
+ ArrayType *arr;
+ int ndims, nitems, *dims;
+ bits8 *bitmap;
+ int bitmask;
+
+ /*
+ * When parameter with packed variadic arguments is NULL, we
+ * cannot to identify number of variadic argumens (NULL
+ * or not NULL), then the correct result is NULL. This behave
+ * is consistent with other variadic functions - see concat_internal.
+ */
+ if (PG_ARGISNULL(0))
+ return false;
+
+ /*
+ * Non-null argument had better be an array. We assume that any call
+ * context that could let get_fn_expr_variadic return true will have
+ * checked that a VARIADIC-labeled parameter actually is an array. So
+ * it should be okay to just Assert that it's an array rather than
+ * doing a full-fledged error check.
+ */
+ Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 0))));
+
+ /* OK, safe to fetch the array value */
+ arr = PG_GETARG_ARRAYTYPE_P(0);
+
+ ndims = ARR_NDIM(arr);
+ dims = ARR_DIMS(arr);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ bitmap = ARR_NULLBITMAP(arr);
+ if (bitmap)
+ {
+ bitmask = 1;
+
+ for (i = 0; i < nitems; i++)
+ {
+ if ((*bitmap & bitmask) == 0)
+ count++;
+
+ bitmask <<= 1;
+ if (bitmask == 0x100)
+ {
+ bitmap++;
+ bitmask = 1;
+ }
+ }
+ }
+
+ *nargs = nitems;
+ *nulls = count;
+ }
+ else
+ {
+ for (i = 0; i < PG_NARGS(); i++)
+ {
+ if (PG_ARGISNULL(i))
+ count++;
+ }
+
+ *nargs = PG_NARGS();
+ *nulls = count;
+ }
+
+ return true;
+ }
+
+ /*
+ * num_nulls()
+ * Count the number of NULL input arguments
+ */
+ Datum
+ pg_num_nulls(PG_FUNCTION_ARGS)
+ {
+ int32 nargs,
+ nulls;
+
+ if (!count_nulls(fcinfo, &nargs, &nulls))
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT32(nulls);
+ }
+
+ /*
+ * num_notnulls()
+ * Count the number of not NULL input arguments
+ */
+ Datum
+ pg_num_notnulls(PG_FUNCTION_ARGS)
+ {
+ int32 nargs,
+ nulls;
+
+ if (!count_nulls(fcinfo, &nargs, &nulls))
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT32(nargs - nulls);
+ }
+
/*
* current_database()
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index e5d6c77..ad1a70c
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("adjust time with time zone precis
*** 2963,2968 ****
--- 2963,2972 ----
DATA(insert OID = 2003 ( textanycat PGNSP PGUID 14 1 0 0 0 f f f f t f s s 2 0 25 "25 2776" _null_ _null_ _null_ _null_ _null_ "select $1 || $2::pg_catalog.text" _null_ _null_ _null_ ));
DATA(insert OID = 2004 ( anytextcat PGNSP PGUID 14 1 0 0 0 f f f f t f s s 2 0 25 "2776 25" _null_ _null_ _null_ _null_ _null_ "select $1::pg_catalog.text || $2" _null_ _null_ _null_ ));
+ DATA(insert OID = 4400 ( num_nulls PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 1 0 23 "2276" "{2276}" "{v}" _null_ _null_ _null_ pg_num_nulls _null_ _null_ _null_ ));
+ DESCR("count the number of NULL input arguments");
+ DATA(insert OID = 4401 ( num_notnulls PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 1 0 23 "2276" "{2276}" "{v}" _null_ _null_ _null_ pg_num_notnulls _null_ _null_ _null_ ));
+ DESCR("count the number of not NULL input arguments");
DATA(insert OID = 2005 ( bytealike PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "17 17" _null_ _null_ _null_ _null_ _null_ bytealike _null_ _null_ _null_ ));
DATA(insert OID = 2006 ( byteanlike PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "17 17" _null_ _null_ _null_ _null_ _null_ byteanlike _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index bbaa2ce..2fc68fc
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_ls_dir(PG_FUNCTION_ARGS)
*** 481,486 ****
--- 481,488 ----
extern Datum pg_ls_dir_1arg(PG_FUNCTION_ARGS);
/* misc.c */
+ extern Datum pg_num_notnulls(PG_FUNCTION_ARGS);
+ extern Datum pg_num_nulls(PG_FUNCTION_ARGS);
extern Datum current_database(PG_FUNCTION_ARGS);
extern Datum current_query(PG_FUNCTION_ARGS);
extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out
new file mode 100644
index ...03d4c47
*** a/src/test/regress/expected/misc_functions.out
--- b/src/test/regress/expected/misc_functions.out
***************
*** 0 ****
--- 1,87 ----
+ --
+ -- num_nulls()
+ --
+ -- should fail, one or more arguments is required
+ SELECT num_notnulls();
+ ERROR: function num_notnulls() does not exist
+ LINE 1: SELECT num_notnulls();
+ ^
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+ SELECT num_nulls();
+ ERROR: function num_nulls() does not exist
+ LINE 1: SELECT num_nulls();
+ ^
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+ -- special cases
+ SELECT num_notnulls(VARIADIC NULL::text[]);
+ num_notnulls
+ --------------
+
+ (1 row)
+
+ SELECT num_nulls(VARIADIC NULL::text[]);
+ num_nulls
+ -----------
+
+ (1 row)
+
+ SELECT num_notnulls(VARIADIC '{}'::int[]);
+ num_notnulls
+ --------------
+ 0
+ (1 row)
+
+ SELECT num_nulls(VARIADIC '{}'::int[]);
+ num_nulls
+ -----------
+ 0
+ (1 row)
+
+ CREATE TABLE num_nulls_test(a int, b text, c numeric);
+ INSERT INTO num_nulls_test VALUES(NULL, NULL, NULL);
+ INSERT INTO num_nulls_test VALUES(10, NULL, NULL);
+ INSERT INTO num_nulls_test VALUES(10, 'Hello', NULL);
+ INSERT INTO num_nulls_test VALUES(10, 'Hello', 1.0);
+ SELECT num_notnulls(a, b, c), num_nulls(a, b, c) FROM num_nulls_test;
+ num_notnulls | num_nulls
+ --------------+-----------
+ 0 | 3
+ 1 | 2
+ 2 | 1
+ 3 | 0
+ (4 rows)
+
+ SELECT num_notnulls(VARIADIC ARRAY[a::text, b::text, c::text]),
+ num_nulls(VARIADIC ARRAY[a::text, b::text, c::text])
+ FROM num_nulls_test;
+ num_notnulls | num_nulls
+ --------------+-----------
+ 0 | 3
+ 1 | 2
+ 2 | 1
+ 3 | 0
+ (4 rows)
+
+ SELECT num_notnulls(VARIADIC ARRAY[a,c]), num_nulls(VARIADIC ARRAY[a,c])
+ FROM num_nulls_test;
+ num_notnulls | num_nulls
+ --------------+-----------
+ 0 | 2
+ 1 | 1
+ 1 | 1
+ 2 | 0
+ (4 rows)
+
+ SELECT num_notnulls(VARIADIC ARRAY(SELECT CASE WHEN i > 40 THEN i END FROM generate_series(1, 100) i));
+ num_notnulls
+ --------------
+ 60
+ (1 row)
+
+ SELECT num_nulls(VARIADIC ARRAY(SELECT CASE WHEN i > 40 THEN i END FROM generate_series(1, 100) i));
+ num_nulls
+ -----------
+ 40
+ (1 row)
+
+ DROP TABLE num_nulls_test;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index b1bc7c7..bec0316
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: brin gin gist spgist privileges se
*** 89,95 ****
# ----------
# Another group of parallel tests
# ----------
! test: alter_generic alter_operator misc psql async dbsize
# rules cannot run concurrently with any test that creates a view
test: rules
--- 89,95 ----
# ----------
# Another group of parallel tests
# ----------
! test: alter_generic alter_operator misc psql async dbsize misc_functions
# rules cannot run concurrently with any test that creates a view
test: rules
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index ade9ef1..7e9b319
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: misc
*** 119,124 ****
--- 119,125 ----
test: psql
test: async
test: dbsize
+ test: misc_functions
test: rules
test: select_views
test: portals_p2
diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql
new file mode 100644
index ...31d0738
*** a/src/test/regress/sql/misc_functions.sql
--- b/src/test/regress/sql/misc_functions.sql
***************
*** 0 ****
--- 1,32 ----
+ --
+ -- num_nulls()
+ --
+
+ -- should fail, one or more arguments is required
+ SELECT num_notnulls();
+ SELECT num_nulls();
+
+ -- special cases
+ SELECT num_notnulls(VARIADIC NULL::text[]);
+ SELECT num_nulls(VARIADIC NULL::text[]);
+ SELECT num_notnulls(VARIADIC '{}'::int[]);
+ SELECT num_nulls(VARIADIC '{}'::int[]);
+
+ CREATE TABLE num_nulls_test(a int, b text, c numeric);
+ INSERT INTO num_nulls_test VALUES(NULL, NULL, NULL);
+ INSERT INTO num_nulls_test VALUES(10, NULL, NULL);
+ INSERT INTO num_nulls_test VALUES(10, 'Hello', NULL);
+ INSERT INTO num_nulls_test VALUES(10, 'Hello', 1.0);
+
+ SELECT num_notnulls(a, b, c), num_nulls(a, b, c) FROM num_nulls_test;
+ SELECT num_notnulls(VARIADIC ARRAY[a::text, b::text, c::text]),
+ num_nulls(VARIADIC ARRAY[a::text, b::text, c::text])
+ FROM num_nulls_test;
+
+ SELECT num_notnulls(VARIADIC ARRAY[a,c]), num_nulls(VARIADIC ARRAY[a,c])
+ FROM num_nulls_test;
+
+ SELECT num_notnulls(VARIADIC ARRAY(SELECT CASE WHEN i > 40 THEN i END FROM generate_series(1, 100) i));
+ SELECT num_nulls(VARIADIC ARRAY(SELECT CASE WHEN i > 40 THEN i END FROM generate_series(1, 100) i));
+
+ DROP TABLE num_nulls_test;