Add min and max aggregates for composite types (records).
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 11 Jul 2024 15:50:50 +0000 (11:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 11 Jul 2024 15:50:50 +0000 (11:50 -0400)
Like min/max for arrays, these are just thin wrappers around
the existing btree comparison function for records.

Aleksander Alekseev

Discussion: https://postgr.es/m/CAO=iB8L4WYSNxCJ8GURRjQsrXEQ2-zn3FiCsh2LMqvWq2WcONg@mail.gmail.com

doc/src/sgml/func.sgml
src/backend/utils/adt/rowtypes.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 135590ba57487a47366863a143f33d06865836d0..785886af714a64d0ebf3fac9331186a8e12edc9c 100644 (file)
@@ -22048,7 +22048,7 @@ SELECT NULLIF(value, '(none)') ...
         as well as <type>inet</type>, <type>interval</type>,
         <type>money</type>, <type>oid</type>, <type>pg_lsn</type>,
         <type>tid</type>, <type>xid8</type>,
-        and arrays of any of these types.
+        and also arrays and composite types containing sortable data types.
        </para></entry>
        <entry>Yes</entry>
       </row>
@@ -22067,7 +22067,7 @@ SELECT NULLIF(value, '(none)') ...
         as well as <type>inet</type>, <type>interval</type>,
         <type>money</type>, <type>oid</type>, <type>pg_lsn</type>,
         <type>tid</type>, <type>xid8</type>,
-        and arrays of any of these types.
+        and also arrays and composite types containing sortable data types.
        </para></entry>
        <entry>Yes</entry>
       </row>
index 0214c23a1d4549cea5c84a9fa03eae7f82cf219c..18bbb62e9a11d4d6fd89f64a3466e77155c09426 100644 (file)
@@ -1315,6 +1315,24 @@ btrecordcmp(PG_FUNCTION_ARGS)
    PG_RETURN_INT32(record_cmp(fcinfo));
 }
 
+Datum
+record_larger(PG_FUNCTION_ARGS)
+{
+   if (record_cmp(fcinfo) > 0)
+       PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+   else
+       PG_RETURN_DATUM(PG_GETARG_DATUM(1));
+}
+
+Datum
+record_smaller(PG_FUNCTION_ARGS)
+{
+   if (record_cmp(fcinfo) < 0)
+       PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+   else
+       PG_RETURN_DATUM(PG_GETARG_DATUM(1));
+}
+
 
 /*
  * record_image_cmp :
index 87b52fffdde0b12c9269b1438d8cc19501dd34e0..3254e7ab9293934406b85c16e3ff978130843cdb 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202407101
+#define CATALOG_VERSION_NO 202407111
 
 #endif
index 5f13532abcc89f9fec2fd5422c81609ce40834b3..b6b6352d91712b399841498da6f5d26852f8e237 100644 (file)
 { aggfnoid => 'max(anyarray)', aggtransfn => 'array_larger',
   aggcombinefn => 'array_larger', aggsortop => '>(anyarray,anyarray)',
   aggtranstype => 'anyarray' },
+{ aggfnoid => 'max(record)', aggtransfn => 'record_larger',
+  aggcombinefn => 'record_larger', aggsortop => '>(record,record)',
+  aggtranstype => 'record' },
 { aggfnoid => 'max(bpchar)', aggtransfn => 'bpchar_larger',
   aggcombinefn => 'bpchar_larger', aggsortop => '>(bpchar,bpchar)',
   aggtranstype => 'bpchar' },
 { aggfnoid => 'min(anyarray)', aggtransfn => 'array_smaller',
   aggcombinefn => 'array_smaller', aggsortop => '<(anyarray,anyarray)',
   aggtranstype => 'anyarray' },
+{ aggfnoid => 'min(record)', aggtransfn => 'record_smaller',
+  aggcombinefn => 'record_smaller', aggsortop => '<(record,record)',
+  aggtranstype => 'record' },
 { aggfnoid => 'min(bpchar)', aggtransfn => 'bpchar_smaller',
   aggcombinefn => 'bpchar_smaller', aggsortop => '<(bpchar,bpchar)',
   aggtranstype => 'bpchar' },
index 0d140003e744255058f9eced695f449c80c0f09d..73d9cf85826c99cd22f4804d3c250a4568fdb1c0 100644 (file)
   proname => 'max', prokind => 'a', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
   prosrc => 'aggregate_dummy' },
+{ oid => '8595', descr => 'maximum value of all record input values',
+  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'record',
+  proargtypes => 'record', prosrc => 'aggregate_dummy' },
 { oid => '2244', descr => 'maximum value of all bpchar input values',
   proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'bpchar',
   proargtypes => 'bpchar', prosrc => 'aggregate_dummy' },
   proname => 'min', prokind => 'a', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
   prosrc => 'aggregate_dummy' },
+{ oid => '8596', descr => 'minimum value of all record input values',
+  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'record',
+  proargtypes => 'record', prosrc => 'aggregate_dummy' },
 { oid => '2245', descr => 'minimum value of all bpchar input values',
   proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'bpchar',
   proargtypes => 'bpchar', prosrc => 'aggregate_dummy' },
   prosrc => 'generate_series_timestamptz' },
 { oid => '6274', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
-  prosupport => 'generate_series_timestamp_support',  proretset => 't',
+  prosupport => 'generate_series_timestamp_support', proretset => 't',
   prorettype => 'timestamptz',
   proargtypes => 'timestamptz timestamptz interval text',
   prosrc => 'generate_series_timestamptz_at_zone' },
 { oid => '2987', descr => 'less-equal-greater',
   proname => 'btrecordcmp', prorettype => 'int4',
   proargtypes => 'record record', prosrc => 'btrecordcmp' },
+{ oid => '8597', descr => 'larger of two',
+  proname => 'record_larger', prorettype => 'record',
+  proargtypes => 'record record', prosrc => 'record_larger' },
+{ oid => '8598', descr => 'smaller of two',
+  proname => 'record_smaller', prorettype => 'record',
+  proargtypes => 'record record', prosrc => 'record_smaller' },
 
 { oid => '6192', descr => 'hash',
   proname => 'hash_record', prorettype => 'int4', proargtypes => 'record',
index 1c1ca7573ad3a4b534263a57fe58d1c01c42bcd0..a5596ab2106c7643b32cf72b63c401cd4c79b1fa 100644 (file)
@@ -269,6 +269,31 @@ SELECT stddev_pop('nan'::numeric), stddev_samp('nan'::numeric);
         NaN |            
 (1 row)
 
+-- verify correct results for min(record) and max(record) aggregates
+SELECT max(row(a,b)) FROM aggtest;
+     max      
+--------------
+ (100,99.097)
+(1 row)
+
+SELECT max(row(b,a)) FROM aggtest;
+     max     
+-------------
+ (324.78,42)
+(1 row)
+
+SELECT min(row(a,b)) FROM aggtest;
+     min     
+-------------
+ (0,0.09561)
+(1 row)
+
+SELECT min(row(b,a)) FROM aggtest;
+     min     
+-------------
+ (0.09561,0)
+(1 row)
+
 -- verify correct results for null and NaN inputs
 select sum(null::int4) from generate_series(1,3);
  sum 
index 1a18ca3d8fe207ab48faec2840c97be61906edbf..ca6d1bcfb7fa7fc3388833ca1bf8086ab7efc460 100644 (file)
@@ -78,6 +78,12 @@ SELECT stddev_pop('inf'::numeric), stddev_samp('inf'::numeric);
 SELECT var_pop('nan'::numeric), var_samp('nan'::numeric);
 SELECT stddev_pop('nan'::numeric), stddev_samp('nan'::numeric);
 
+-- verify correct results for min(record) and max(record) aggregates
+SELECT max(row(a,b)) FROM aggtest;
+SELECT max(row(b,a)) FROM aggtest;
+SELECT min(row(a,b)) FROM aggtest;
+SELECT min(row(b,a)) FROM aggtest;
+
 -- verify correct results for null and NaN inputs
 select sum(null::int4) from generate_series(1,3);
 select sum(null::int8) from generate_series(1,3);