Implement ANY_VALUE aggregate
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 22 Feb 2023 08:32:12 +0000 (09:32 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 22 Feb 2023 08:33:07 +0000 (09:33 +0100)
SQL:2023 defines an ANY_VALUE aggregate whose purpose is to emit an
implementation-dependent (i.e. non-deterministic) value from the
aggregated rows.

Author: Vik Fearing <vik@postgresfriends.org>
Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/5cff866c-10a8-d2df-32cb-e9072e6b04a2@postgresfriends.org

doc/src/sgml/func.sgml
src/backend/catalog/sql_features.txt
src/backend/utils/adt/misc.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.dat
src/include/catalog/pg_proc.dat
src/test/regress/expected/aggregates.out
src/test/regress/sql/aggregates.sql

index e09e289a438d855e898088d31110cc7424f4311a..0cbdf636327ccfd3f29897fb8f4ebd4d3d4594a4 100644 (file)
@@ -19735,6 +19735,20 @@ SELECT NULLIF(value, '(none)') ...
      </thead>
 
      <tbody>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>any_value</primary>
+        </indexterm>
+        <function>any_value</function> ( <type>anyelement</type> )
+        <returnvalue><replaceable>same as input type</replaceable></returnvalue>
+       </para>
+       <para>
+        Returns an arbitrary value from the non-null input values.
+       </para></entry>
+       <entry>Yes</entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
index 3766762ae361afa32874382e633f5a55b72b5ce8..75a09f14e089599f7f71c56cb376e3d8e3d721fb 100644 (file)
@@ -520,6 +520,7 @@ T622    Trigonometric functions         YES
 T623   General logarithm functions         YES 
 T624   Common logarithm functions          YES 
 T625   LISTAGG         NO  
+T626   ANY_VALUE           YES SQL:202x draft
 T631   IN predicate with one list element          YES 
 T641   Multiple column assignment          NO  only some syntax variants supported
 T651   SQL-schema statements in SQL routines           YES 
index 220ddb8c01111c5458188f491a8c08e08a5b80b0..f95256efd3d51d77d1381047e93d87dd3df74ec2 100644 (file)
@@ -1041,3 +1041,12 @@ pg_get_replica_identity_index(PG_FUNCTION_ARGS)
    else
        PG_RETURN_NULL();
 }
+
+/*
+ * Transition function for the ANY_VALUE aggregate
+ */
+Datum
+any_value_transfn(PG_FUNCTION_ARGS)
+{
+   PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+}
index 9c298cb125301ed5c88d5e2b15a3f125a9b26def..23a446fb11d432950ca741beab0f3ea73d6edaf8 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202302111
+#define CATALOG_VERSION_NO 202302221
 
 #endif
index 4fea7d8dc19b39af5055510d5e25a26ec54d088a..d7895cd676b736cc231cdb97df43126b3c4c3f26 100644 (file)
   aggfinalfn => 'dense_rank_final', aggfinalextra => 't', aggfinalmodify => 'w',
   aggmfinalmodify => 'w', aggtranstype => 'internal' },
 
+# any_value
+{ aggfnoid => 'any_value(anyelement)', aggtransfn => 'any_value_transfn',
+  aggcombinefn => 'any_value_transfn', aggtranstype => 'anyelement' },
+
 ]
index 66b73c3900dc52a1130cfd6dea89d038da0c4fb0..e2a7642a2ba1ab47d3094e3c2123553960411b49 100644 (file)
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+{ oid => '8981', descr => 'arbitrary value from among input values',
+  proname => 'any_value', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anyelement', proargtypes => 'anyelement',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8982', descr => 'aggregate transition function',
+  proname => 'any_value_transfn', prorettype => 'anyelement',
+  proargtypes => 'anyelement anyelement', prosrc => 'any_value_transfn' },
+
 ]
index 52046c33dc8d479a880840a5735fe6b838ab26f5..e074cb71bf6b2251fc633af9d4d437f79c070981 100644 (file)
@@ -25,6 +25,30 @@ SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
  32.6666666666666667
 (1 row)
 
+SELECT any_value(v) FROM (VALUES (1), (2), (3)) AS v (v);
+ any_value 
+-----------
+         1
+(1 row)
+
+SELECT any_value(v) FROM (VALUES (NULL)) AS v (v);
+ any_value 
+-----------
+(1 row)
+
+SELECT any_value(v) FROM (VALUES (NULL), (1), (2)) AS v (v);
+ any_value 
+-----------
+         1
+(1 row)
+
+SELECT any_value(v) FROM (VALUES (array['hello', 'world'])) AS v (v);
+   any_value   
+---------------
+ {hello,world}
+(1 row)
+
 -- In 7.1, avg(float4) is computed using float8 arithmetic.
 -- Round the result to 3 digits to avoid platform-specific results.
 SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest;
@@ -2033,6 +2057,12 @@ from (values ('a', 'b')) AS v(foo,bar);
  a
 (1 row)
 
+select any_value(v) filter (where v > 2) from (values (1), (2), (3)) as v (v);
+ any_value 
+-----------
+         3
+(1 row)
+
 -- outer reference in FILTER (PostgreSQL extension)
 select (select count(*)
         from (values (1)) t0(inner_c))
index e7970983c3677d45c6feeb238864876cdf9358fb..616ef38c25952b9c7a0a2c27f835c8a42f61b7dd 100644 (file)
@@ -24,6 +24,11 @@ SELECT avg(four) AS avg_1 FROM onek;
 
 SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
 
+SELECT any_value(v) FROM (VALUES (1), (2), (3)) AS v (v);
+SELECT any_value(v) FROM (VALUES (NULL)) AS v (v);
+SELECT any_value(v) FROM (VALUES (NULL), (1), (2)) AS v (v);
+SELECT any_value(v) FROM (VALUES (array['hello', 'world'])) AS v (v);
+
 -- In 7.1, avg(float4) is computed using float8 arithmetic.
 -- Round the result to 3 digits to avoid platform-specific results.
 
@@ -810,6 +815,8 @@ having exists (select 1 from onek b where sum(distinct a.four) = b.four);
 select max(foo COLLATE "C") filter (where (bar collate "POSIX") > '0')
 from (values ('a', 'b')) AS v(foo,bar);
 
+select any_value(v) filter (where v > 2) from (values (1), (2), (3)) as v (v);
+
 -- outer reference in FILTER (PostgreSQL extension)
 select (select count(*)
         from (values (1)) t0(inner_c))