Hi

2016-01-04 5:49 GMT+01:00 Jim Nasby <jim.na...@bluetreble.com>:

> On 1/3/16 10:23 PM, Pavel Stehule wrote:
>
>> Hi
>>
>> 2016-01-03 22:49 GMT+01:00 Jim Nasby <jim.na...@bluetreble.com
>> <mailto:jim.na...@bluetreble.com>>:
>>
>>     On 1/3/16 2:37 PM, Pavel Stehule wrote:
>>
>>         +               /* num_nulls(VARIADIC NULL) is defined as NULL */
>>         +               if (PG_ARGISNULL(0))
>>         +                       return false;
>>
>>
>>     Could you add to the comment explaining why that's the desired
>> behavior?
>>
>>
>> This case should be different than num_nulls(VARIADIC ARRAY[..]) - this
>> situation is really equivalent of missing data and NULL is correct
>> answer. It should not be too clean in num_nulls, but when it is cleaner
>> for num_notnulls. And more, it is consistent with other variadic
>> functions in Postgres: see concat_internal and text_format.
>>
>
> Makes sense, now that you explain it. Which is why I'm thinking it'd be
> good to add that explanation to the comment... ;)
>
>
>> Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo,
>> 0))));
>>
>>
>>     Erm... is that really the way to verify that what you have is an
>>     array? ISTM there should be a macro for that somewhere...
>>
>>
>> really, it is. It is used more time. Although I am not against some
>> macro, I don't think so it is necessary. The macro should not be too
>> shorter than this text.
>>
>
> Well, if there's other stuff doing that... would be nice to refactor that
> though.
>
>     For brevity and example sake it'd probably be better to just use the
>>     normal iterator, unless there's a serious speed difference?
>>
>>
>> The iterator does some memory allocations and some access to type cache.
>> Almost all work of iterator is useless for this case. This code is
>> developed by Marko, but I agree with this design. Using the iterator is
>> big gun for this case. I didn't any performance checks, but it should be
>> measurable  for any varlena arrays.
>>
>
> Makes sense then.
>
>
+ enhanced comment
+ rewritten regress tests

Regards

Pavel


> --
> Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
> Experts in Analytics, Data Architecture and PostgreSQL
> Data in Trouble? Get it in Treble! http://BlueTreble.com
>
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 ****
    </sect1>
  
    <sect1 id="functions-comparison">
!    <title>Comparison Operators</title>
  
     <indexterm zone="functions-comparison">
      <primary>comparison</primary>
--- 182,188 ----
    </sect1>
  
    <sect1 id="functions-comparison">
!    <title>Comparison Functions and Operators</title>
  
     <indexterm zone="functions-comparison">
      <primary>comparison</primary>
***************
*** 191,200 ****
  
     <para>
      The usual comparison operators are available, shown in <xref
!     linkend="functions-comparison-table">.
     </para>
  
!    <table id="functions-comparison-table">
      <title>Comparison Operators</title>
      <tgroup cols="2">
       <thead>
--- 191,200 ----
  
     <para>
      The usual comparison operators are available, shown in <xref
!     linkend="functions-comparison-op-table">.
     </para>
  
!    <table id="functions-comparison-op-table">
      <title>Comparison Operators</title>
      <tgroup cols="2">
       <thead>
***************
*** 437,442 ****
--- 437,479 ----
     </para>
  -->
  
+   <table id="functions-comparison-table">
+     <title>Comparison Functions</title>
+     <tgroup cols="4">
+      <thead>
+       <row>
+        <entry>Function</entry>
+        <entry>Description</entry>
+        <entry>Example</entry>
+        <entry>Example Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry>
+          <indexterm>
+           <primary>num_notnulls</primary>
+          </indexterm>
+          <literal>num_notnulls(VARIADIC "any")</literal>
+        </entry>
+        <entry>Returns the number of not NULL input arguments</entry>
+        <entry><literal>num_nulls(1, NULL, 2)</literal></entry>
+        <entry><literal>2</literal></entry>
+       </row>
+       <row>
+        <entry>
+          <indexterm>
+           <primary>num_nulls</primary>
+          </indexterm>
+          <literal>num_nulls(VARIADIC "any")</literal>
+        </entry>
+        <entry>Returns the number of NULL input arguments</entry>
+        <entry><literal>num_nulls(1, NULL, 2)</literal></entry>
+        <entry><literal>1</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
    </sect1>
  
    <sect1 id="functions-math">
*************** table2-mapping
*** 10307,10313 ****
    </note>
    <para>
     The standard comparison operators shown in  <xref
!    linkend="functions-comparison-table"> are available for
     <type>jsonb</type>, but not for <type>json</type>. They follow the
     ordering rules for B-tree operations outlined at <xref
     linkend="json-indexing">.
--- 10344,10350 ----
    </note>
    <para>
     The standard comparison operators shown in  <xref
!    linkend="functions-comparison-op-table"> are available for
     <type>jsonb</type>, but not for <type>json</type>. They follow the
     ordering rules for B-tree operations outlined at <xref
     linkend="json-indexing">.
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;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to