]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add bytea equivalents of ltrim() and rtrim().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Jan 2021 20:11:32 +0000 (15:11 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Jan 2021 20:11:32 +0000 (15:11 -0500)
We had bytea btrim() already, but for some reason not the other two.

Joel Jacobson

Discussion: https://postgr.es/m/d10cd5cd-a901-42f1-b832-763ac6f7ff3a@www.fastmail.com

doc/src/sgml/func.sgml
src/backend/utils/adt/oracle_compat.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/create_view.out
src/test/regress/expected/strings.out
src/test/regress/sql/create_view.sql
src/test/regress/sql/strings.sql

index fd0370a1b438ba3a7ec95e2e5c3f66ae7b559d61..aa99665e2eb79c13fa956d4b017a33768ba88cac 100644 (file)
@@ -3948,15 +3948,16 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
         <indexterm>
          <primary>trim</primary>
         </indexterm>
-        <function>trim</function> ( <optional> <literal>BOTH</literal> </optional>
+        <function>trim</function> ( <optional> <literal>LEADING</literal> | <literal>TRAILING</literal> | <literal>BOTH</literal> </optional>
         <parameter>bytesremoved</parameter> <type>bytea</type> <literal>FROM</literal>
         <parameter>bytes</parameter> <type>bytea</type> )
         <returnvalue>bytea</returnvalue>
        </para>
        <para>
         Removes the longest string containing only bytes appearing in
-        <parameter>bytesremoved</parameter> from the start
-        and end of <parameter>bytes</parameter>.
+        <parameter>bytesremoved</parameter> from the start,
+        end, or both ends (<literal>BOTH</literal> is the default)
+        of <parameter>bytes</parameter>.
        </para>
        <para>
         <literal>trim('\x9012'::bytea from '\x1234567890'::bytea)</literal>
@@ -3966,7 +3967,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
 
       <row>
        <entry role="func_table_entry"><para role="func_signature">
-        <function>trim</function> ( <optional> <literal>BOTH</literal> </optional> <optional> <literal>FROM</literal> </optional>
+        <function>trim</function> ( <optional> <literal>LEADING</literal> | <literal>TRAILING</literal> | <literal>BOTH</literal> </optional> <optional> <literal>FROM</literal> </optional>
         <parameter>bytes</parameter> <type>bytea</type>,
         <parameter>bytesremoved</parameter> <type>bytea</type> )
         <returnvalue>bytea</returnvalue>
@@ -4109,6 +4110,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
        </para></entry>
       </row>
 
+      <row>
+        <entry role="func_table_entry"><para role="func_signature">
+         <indexterm>
+          <primary>ltrim</primary>
+         </indexterm>
+         <function>ltrim</function> ( <parameter>bytes</parameter> <type>bytea</type>,
+         <parameter>bytesremoved</parameter> <type>bytea</type> )
+         <returnvalue>bytea</returnvalue>
+        </para>
+        <para>
+         Removes the longest string containing only bytes appearing in
+         <parameter>bytesremoved</parameter> from the start of
+         <parameter>bytes</parameter>.
+        </para>
+        <para>
+         <literal>ltrim('\x1234567890'::bytea, '\x9012'::bytea)</literal>
+         <returnvalue>\x34567890</returnvalue>
+        </para></entry>
+       </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -4127,6 +4148,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
        </para></entry>
       </row>
 
+      <row>
+        <entry role="func_table_entry"><para role="func_signature">
+         <indexterm>
+          <primary>rtrim</primary>
+         </indexterm>
+         <function>rtrim</function> ( <parameter>bytes</parameter> <type>bytea</type>,
+         <parameter>bytesremoved</parameter> <type>bytea</type> )
+         <returnvalue>bytea</returnvalue>
+        </para>
+        <para>
+         Removes the longest string containing only bytes appearing in
+         <parameter>bytesremoved</parameter> from the end of
+         <parameter>bytes</parameter>.
+        </para>
+        <para>
+         <literal>rtrim('\x1234567890'::bytea, '\x9012'::bytea)</literal>
+         <returnvalue>\x12345678</returnvalue>
+        </para></entry>
+       </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
index 99077a90b92adc5a698f545a531cb76c7acc6dac..f737aa6fbde73763eb5c70be5ac3287652acac9c 100644 (file)
@@ -24,6 +24,8 @@
 static text *dotrim(const char *string, int stringlen,
                                        const char *set, int setlen,
                                        bool doltrim, bool dortrim);
+static bytea *dobyteatrim(bytea *string, bytea *set,
+                                                 bool doltrim, bool dortrim);
 
 
 /********************************************************************
@@ -521,27 +523,12 @@ dotrim(const char *string, int stringlen,
        return cstring_to_text_with_len(string, stringlen);
 }
 
-/********************************************************************
- *
- * byteatrim
- *
- * Syntax:
- *
- *      bytea byteatrim(bytea string, bytea set)
- *
- * Purpose:
- *
- *      Returns string with characters removed from the front and back
- *      up to the first character not in set.
- *
- * Cloned from btrim and modified as required.
- ********************************************************************/
-
-Datum
-byteatrim(PG_FUNCTION_ARGS)
+/*
+ * Common implementation for bytea versions of btrim, ltrim, rtrim
+ */
+bytea *
+dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
 {
-       bytea      *string = PG_GETARG_BYTEA_PP(0);
-       bytea      *set = PG_GETARG_BYTEA_PP(1);
        bytea      *ret;
        char       *ptr,
                           *end,
@@ -556,7 +543,7 @@ byteatrim(PG_FUNCTION_ARGS)
        setlen = VARSIZE_ANY_EXHDR(set);
 
        if (stringlen <= 0 || setlen <= 0)
-               PG_RETURN_BYTEA_P(string);
+               return string;
 
        m = stringlen;
        ptr = VARDATA_ANY(string);
@@ -564,39 +551,126 @@ byteatrim(PG_FUNCTION_ARGS)
        ptr2start = VARDATA_ANY(set);
        end2 = ptr2start + setlen - 1;
 
-       while (m > 0)
+       if (doltrim)
        {
-               ptr2 = ptr2start;
-               while (ptr2 <= end2)
+               while (m > 0)
                {
-                       if (*ptr == *ptr2)
+                       ptr2 = ptr2start;
+                       while (ptr2 <= end2)
+                       {
+                               if (*ptr == *ptr2)
+                                       break;
+                               ++ptr2;
+                       }
+                       if (ptr2 > end2)
                                break;
-                       ++ptr2;
+                       ptr++;
+                       m--;
                }
-               if (ptr2 > end2)
-                       break;
-               ptr++;
-               m--;
        }
 
-       while (m > 0)
+       if (dortrim)
        {
-               ptr2 = ptr2start;
-               while (ptr2 <= end2)
+               while (m > 0)
                {
-                       if (*end == *ptr2)
+                       ptr2 = ptr2start;
+                       while (ptr2 <= end2)
+                       {
+                               if (*end == *ptr2)
+                                       break;
+                               ++ptr2;
+                       }
+                       if (ptr2 > end2)
                                break;
-                       ++ptr2;
+                       end--;
+                       m--;
                }
-               if (ptr2 > end2)
-                       break;
-               end--;
-               m--;
        }
 
        ret = (bytea *) palloc(VARHDRSZ + m);
        SET_VARSIZE(ret, VARHDRSZ + m);
        memcpy(VARDATA(ret), ptr, m);
+       return ret;
+}
+
+/********************************************************************
+ *
+ * byteatrim
+ *
+ * Syntax:
+ *
+ *      bytea byteatrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ *      Returns string with characters removed from the front and back
+ *      up to the first character not in set.
+ *
+ * Cloned from btrim and modified as required.
+ ********************************************************************/
+
+Datum
+byteatrim(PG_FUNCTION_ARGS)
+{
+       bytea      *string = PG_GETARG_BYTEA_PP(0);
+       bytea      *set = PG_GETARG_BYTEA_PP(1);
+       bytea      *ret;
+
+       ret = dobyteatrim(string, set, true, true);
+
+       PG_RETURN_BYTEA_P(ret);
+}
+
+/********************************************************************
+ *
+ * bytealtrim
+ *
+ * Syntax:
+ *
+ *      bytea bytealtrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ *      Returns string with initial characters removed up to the first
+ *      character not in set.
+ *
+ ********************************************************************/
+
+Datum
+bytealtrim(PG_FUNCTION_ARGS)
+{
+       bytea      *string = PG_GETARG_BYTEA_PP(0);
+       bytea      *set = PG_GETARG_BYTEA_PP(1);
+       bytea      *ret;
+
+       ret = dobyteatrim(string, set, true, false);
+
+       PG_RETURN_BYTEA_P(ret);
+}
+
+/********************************************************************
+ *
+ * byteartrim
+ *
+ * Syntax:
+ *
+ *      bytea byteartrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ *      Returns string with final characters removed after the last
+ *      character not in set.
+ *
+ ********************************************************************/
+
+Datum
+byteartrim(PG_FUNCTION_ARGS)
+{
+       bytea      *string = PG_GETARG_BYTEA_PP(0);
+       bytea      *set = PG_GETARG_BYTEA_PP(1);
+       bytea      *ret;
+
+       ret = dobyteatrim(string, set, false, true);
 
        PG_RETURN_BYTEA_P(ret);
 }
index db803b4388124e2cb660c753587bbbb04791d3b5..8a1fbda57227e2f40a80963851c79d2f681c9dc9 100644 (file)
@@ -9680,6 +9680,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
                        appendStringInfoChar(buf, ')');
                        return true;
 
+               case F_LTRIM_BYTEA_BYTEA:
                case F_LTRIM_TEXT:
                case F_LTRIM_TEXT_TEXT:
                        /* TRIM() */
@@ -9694,6 +9695,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
                        appendStringInfoChar(buf, ')');
                        return true;
 
+               case F_RTRIM_BYTEA_BYTEA:
                case F_RTRIM_TEXT:
                case F_RTRIM_TEXT_TEXT:
                        /* TRIM() */
index 385d108c292b01728b7ed82b3755d7401486aee7..ee0049dc9718fac83b6eabb00a52c6e249eff3b2 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202101171
+#define CATALOG_VERSION_NO     202101181
 
 #endif
index dd64c3bd60bd4406a7e2fdc0f48c8987e4854dcb..b5f52d4e4a3eafb355887664aa132061dc4a3bdb 100644 (file)
 { oid => '2014', descr => 'position of substring',
   proname => 'position', prorettype => 'int4', proargtypes => 'bytea bytea',
   prosrc => 'byteapos' },
-{ oid => '2015', descr => 'trim both ends of string',
+{ oid => '2015', descr => 'trim selected bytes from both ends of string',
   proname => 'btrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
   prosrc => 'byteatrim' },
+{ oid => '9612', descr => 'trim selected bytes from left end of string',
+  proname => 'ltrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
+  prosrc => 'bytealtrim' },
+{ oid => '9613', descr => 'trim selected bytes from right end of string',
+  proname => 'rtrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
+  prosrc => 'byteartrim' },
 
 { oid => '2019', descr => 'convert timestamp with time zone to time',
   proname => 'time', provolatile => 's', prorettype => 'time',
index b234d2d4f9fe19b875f8a1c8f992834925c3fa9e..bd5fe604504af7a4580fa592581c0a9329b63560 100644 (file)
@@ -1735,7 +1735,10 @@ select
   substring('foo' from 'oo') as ssf,  -- historically-permitted abuse
   trim(' ' from ' foo ') as bt,
   trim(leading ' ' from ' foo ') as lt,
-  trim(trailing ' foo ') as rt;
+  trim(trailing ' foo ') as rt,
+  trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea) as btb,
+  trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea) as ltb,
+  trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea) as rtb;
 select pg_get_viewdef('tt201v', true);
                                         pg_get_viewdef                                         
 -----------------------------------------------------------------------------------------------
@@ -1753,7 +1756,10 @@ select pg_get_viewdef('tt201v', true);
      "substring"('foo'::text, 'oo'::text) AS ssf,                                             +
      TRIM(BOTH ' '::text FROM ' foo '::text) AS bt,                                           +
      TRIM(LEADING ' '::text FROM ' foo '::text) AS lt,                                        +
-     TRIM(TRAILING FROM ' foo '::text) AS rt;
+     TRIM(TRAILING FROM ' foo '::text) AS rt,                                                 +
+     TRIM(BOTH '\x00'::bytea FROM '\x00546f6d00'::bytea) AS btb,                              +
+     TRIM(LEADING '\x00'::bytea FROM '\x00546f6d00'::bytea) AS ltb,                           +
+     TRIM(TRAILING '\x00'::bytea FROM '\x00546f6d00'::bytea) AS rtb;
 (1 row)
 
 -- corner cases with empty join conditions
index 595bd2446e5257819fdf0c8d67874c7f887ac1ef..7c91afa6e4a9a6d4e1ec357ddd0b961f8f49f091 100644 (file)
@@ -2131,6 +2131,18 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
  Tom
 (1 row)
 
+SELECT trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea);
+  ltrim  
+---------
+ Tom\000
+(1 row)
+
+SELECT trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea);
+  rtrim  
+---------
+ \000Tom
+(1 row)
+
 SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
  btrim 
 -------
index 6d4dd539655286a9d45c46ec67234d0467f65c1c..fbd1313b9ceeffc585f4d037eecc639b576184b9 100644 (file)
@@ -605,7 +605,10 @@ select
   substring('foo' from 'oo') as ssf,  -- historically-permitted abuse
   trim(' ' from ' foo ') as bt,
   trim(leading ' ' from ' foo ') as lt,
-  trim(trailing ' foo ') as rt;
+  trim(trailing ' foo ') as rt,
+  trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea) as btb,
+  trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea) as ltb,
+  trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea) as rtb;
 select pg_get_viewdef('tt201v', true);
 
 -- corner cases with empty join conditions
index ca465d050f70cd6281ed00da8578c831879fc7dd..ef4bfb008acec89b7253cf837d6e222675a9d583 100644 (file)
@@ -722,6 +722,8 @@ SELECT SUBSTRING('string'::bytea FROM -10 FOR 2147483646) AS "string";
 SELECT SUBSTRING('string'::bytea FROM -10 FOR -2147483646) AS "error";
 
 SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
+SELECT trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea);
+SELECT trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea);
 SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
 SELECT btrim(''::bytea, E'\\000'::bytea);
 SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);