Add trim_array() function.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Mar 2021 21:39:57 +0000 (16:39 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Mar 2021 21:39:57 +0000 (16:39 -0500)
This has been in the SQL spec since 2008.  It's a pretty thin
wrapper around the array slice functionality, but the spec
says we should have it, so here it is.

Vik Fearing, reviewed by Dian Fay

Discussion: https://postgr.es/m/fc92ce17-9655-8ff1-c62a-4dc4c8ccd815@postgresfriends.org

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

index bf99f821496aa525aa4a868d0d653dc50ff0213c..fee05619612f69413b33025f0a207a3eb8f743d9 100644 (file)
@@ -17930,6 +17930,24 @@ SELECT NULLIF(value, '(none)') ...
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>trim_array</primary>
+        </indexterm>
+        <function>trim_array</function> ( <parameter>array</parameter> <type>anyarray</type>, <parameter>n</parameter> <type>integer</type> )
+        <returnvalue>anyarray</returnvalue>
+       </para>
+       <para>
+        Trims an array by removing the last <parameter>n</parameter> elements.
+        If the array is multidimensional, only the first dimension is trimmed.
+       </para>
+       <para>
+        <literal>trim_array(ARRAY[1,2,3,4,5,6], 2)</literal>
+        <returnvalue>{1,2,3,4}</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
index ab0895ce3c8aa809592a0c3ce0c15d023b319171..32eed988ab0763f9b185f6f59a50c144351cef7d 100644 (file)
@@ -398,7 +398,7 @@ S301    Enhanced UNNEST         YES
 S401   Distinct types based on array types         NO  
 S402   Distinct types based on distinct types          NO  
 S403   ARRAY_MAX_CARDINALITY           NO  
-S404   TRIM_ARRAY          NO  
+S404   TRIM_ARRAY          YES 
 T011   Timestamp in Information Schema         NO  
 T021   BINARY and VARBINARY data types         NO  
 T022   Advanced support for BINARY and VARBINARY data types            NO  
index f7012cc5d9876bf14fb038d31bcb27bcacc439a2..17a16b4c5ccfa62b263c0781f287fc4fc0f8266d 100644 (file)
@@ -6631,3 +6631,46 @@ width_bucket_array_variable(Datum operand,
 
    return left;
 }
+
+/*
+ * Trim the last N elements from an array by building an appropriate slice.
+ * Only the first dimension is trimmed.
+ */
+Datum
+trim_array(PG_FUNCTION_ARGS)
+{
+   ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+   int         n = PG_GETARG_INT32(1);
+   int         array_length = ARR_DIMS(v)[0];
+   int16       elmlen;
+   bool        elmbyval;
+   char        elmalign;
+   int         lower[MAXDIM];
+   int         upper[MAXDIM];
+   bool        lowerProvided[MAXDIM];
+   bool        upperProvided[MAXDIM];
+   Datum       result;
+
+   /* Per spec, throw an error if out of bounds */
+   if (n < 0 || n > array_length)
+       ereport(ERROR,
+               (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+                errmsg("number of elements to trim must be between 0 and %d",
+                       array_length)));
+
+   /* Set all the bounds as unprovided except the first upper bound */
+   memset(lowerProvided, false, sizeof(lowerProvided));
+   memset(upperProvided, false, sizeof(upperProvided));
+   upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
+   upperProvided[0] = true;
+
+   /* Fetch the needed information about the element type */
+   get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
+
+   /* Get the slice */
+   result = array_get_slice(PointerGetDatum(v), 1,
+                            upper, lower, upperProvided, lowerProvided,
+                            -1, elmlen, elmbyval, elmalign);
+
+   PG_RETURN_DATUM(result);
+}
index b19975c5c89543e8785db3c6f20af19428e6d19c..28b8a32129c66c92c115f68962d2e5407841e9ce 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202103031
+#define CATALOG_VERSION_NO 202103032
 
 #endif
index 3d3974f46761a58db23184f29e733b3294dde381..59d2b71ca9ce8ac4b86d2a13b2f3ff5700cb787a 100644 (file)
   proname => 'width_bucket', prorettype => 'int4',
   proargtypes => 'anycompatible anycompatiblearray',
   prosrc => 'width_bucket_array' },
+{ oid => '8819', descr => 'remove last N elements of array',
+  proname => 'trim_array', prorettype => 'anyarray',
+  proargtypes => 'anyarray int4', prosrc => 'trim_array' },
 { oid => '3816', descr => 'array typanalyze',
   proname => 'array_typanalyze', provolatile => 's', prorettype => 'bool',
   proargtypes => 'internal', prosrc => 'array_typanalyze' },
index 8bc7721e7d5db9c0ad7785c08116f53351a8558b..3e3a1beaab38927c6beea8b1c5f9491f16eec0dc 100644 (file)
@@ -2399,3 +2399,24 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]);
 ERROR:  thresholds array must not contain NULLs
 SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
 ERROR:  thresholds must be one-dimensional array
+-- trim_array
+SELECT arr, trim_array(arr, 2)
+FROM
+(VALUES ('{1,2,3,4,5,6}'::bigint[]),
+        ('{1,2}'),
+        ('[10:16]={1,2,3,4,5,6,7}'),
+        ('[-15:-10]={1,2,3,4,5,6}'),
+        ('{{1,10},{2,20},{3,30},{4,40}}')) v(arr);
+              arr              |   trim_array    
+-------------------------------+-----------------
+ {1,2,3,4,5,6}                 | {1,2,3,4}
+ {1,2}                         | {}
+ [10:16]={1,2,3,4,5,6,7}       | {1,2,3,4,5}
+ [-15:-10]={1,2,3,4,5,6}       | {1,2,3,4}
+ {{1,10},{2,20},{3,30},{4,40}} | {{1,10},{2,20}}
+(5 rows)
+
+SELECT trim_array(ARRAY[1, 2, 3], -1); -- fail
+ERROR:  number of elements to trim must be between 0 and 3
+SELECT trim_array(ARRAY[1, 2, 3], 10); -- fail
+ERROR:  number of elements to trim must be between 0 and 3
index c40619a8d5d24966cb423c413462f21c44bf0719..912233ef96810f204fcd527641c27e2b7ce32d62 100644 (file)
@@ -722,3 +722,16 @@ SELECT width_bucket(5, '{}');
 SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]);
 SELECT width_bucket(5, ARRAY[3, 4, NULL]);
 SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
+
+-- trim_array
+
+SELECT arr, trim_array(arr, 2)
+FROM
+(VALUES ('{1,2,3,4,5,6}'::bigint[]),
+        ('{1,2}'),
+        ('[10:16]={1,2,3,4,5,6,7}'),
+        ('[-15:-10]={1,2,3,4,5,6}'),
+        ('{{1,10},{2,20},{3,30},{4,40}}')) v(arr);
+
+SELECT trim_array(ARRAY[1, 2, 3], -1); -- fail
+SELECT trim_array(ARRAY[1, 2, 3], 10); -- fail