Commit a6cf3df4 authored by Tom Lane's avatar Tom Lane

Add bytea equivalents of ltrim() and rtrim().

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
parent a3ed4d1e
...@@ -3948,15 +3948,16 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); ...@@ -3948,15 +3948,16 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
<indexterm> <indexterm>
<primary>trim</primary> <primary>trim</primary>
</indexterm> </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>bytesremoved</parameter> <type>bytea</type> <literal>FROM</literal>
<parameter>bytes</parameter> <type>bytea</type> ) <parameter>bytes</parameter> <type>bytea</type> )
<returnvalue>bytea</returnvalue> <returnvalue>bytea</returnvalue>
</para> </para>
<para> <para>
Removes the longest string containing only bytes appearing in Removes the longest string containing only bytes appearing in
<parameter>bytesremoved</parameter> from the start <parameter>bytesremoved</parameter> from the start,
and end of <parameter>bytes</parameter>. end, or both ends (<literal>BOTH</literal> is the default)
of <parameter>bytes</parameter>.
</para> </para>
<para> <para>
<literal>trim('\x9012'::bytea from '\x1234567890'::bytea)</literal> <literal>trim('\x9012'::bytea from '\x1234567890'::bytea)</literal>
...@@ -3966,7 +3967,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); ...@@ -3966,7 +3967,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
<row> <row>
<entry role="func_table_entry"><para role="func_signature"> <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>bytes</parameter> <type>bytea</type>,
<parameter>bytesremoved</parameter> <type>bytea</type> ) <parameter>bytesremoved</parameter> <type>bytea</type> )
<returnvalue>bytea</returnvalue> <returnvalue>bytea</returnvalue>
...@@ -4109,6 +4110,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); ...@@ -4109,6 +4110,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
</para></entry> </para></entry>
</row> </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> <row>
<entry role="func_table_entry"><para role="func_signature"> <entry role="func_table_entry"><para role="func_signature">
<indexterm> <indexterm>
...@@ -4127,6 +4148,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); ...@@ -4127,6 +4148,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
</para></entry> </para></entry>
</row> </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> <row>
<entry role="func_table_entry"><para role="func_signature"> <entry role="func_table_entry"><para role="func_signature">
<indexterm> <indexterm>
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
static text *dotrim(const char *string, int stringlen, static text *dotrim(const char *string, int stringlen,
const char *set, int setlen, const char *set, int setlen,
bool doltrim, bool dortrim); 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, ...@@ -521,27 +523,12 @@ dotrim(const char *string, int stringlen,
return cstring_to_text_with_len(string, stringlen); return cstring_to_text_with_len(string, stringlen);
} }
/******************************************************************** /*
* * Common implementation for bytea versions of btrim, ltrim, rtrim
* byteatrim */
* bytea *
* Syntax: dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
*
* 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; bytea *ret;
char *ptr, char *ptr,
*end, *end,
...@@ -556,7 +543,7 @@ byteatrim(PG_FUNCTION_ARGS) ...@@ -556,7 +543,7 @@ byteatrim(PG_FUNCTION_ARGS)
setlen = VARSIZE_ANY_EXHDR(set); setlen = VARSIZE_ANY_EXHDR(set);
if (stringlen <= 0 || setlen <= 0) if (stringlen <= 0 || setlen <= 0)
PG_RETURN_BYTEA_P(string); return string;
m = stringlen; m = stringlen;
ptr = VARDATA_ANY(string); ptr = VARDATA_ANY(string);
...@@ -564,39 +551,126 @@ byteatrim(PG_FUNCTION_ARGS) ...@@ -564,39 +551,126 @@ byteatrim(PG_FUNCTION_ARGS)
ptr2start = VARDATA_ANY(set); ptr2start = VARDATA_ANY(set);
end2 = ptr2start + setlen - 1; end2 = ptr2start + setlen - 1;
while (m > 0) if (doltrim)
{ {
ptr2 = ptr2start; while (m > 0)
while (ptr2 <= end2)
{ {
if (*ptr == *ptr2) ptr2 = ptr2start;
while (ptr2 <= end2)
{
if (*ptr == *ptr2)
break;
++ptr2;
}
if (ptr2 > end2)
break; break;
++ptr2; ptr++;
m--;
} }
if (ptr2 > end2)
break;
ptr++;
m--;
} }
while (m > 0) if (dortrim)
{ {
ptr2 = ptr2start; while (m > 0)
while (ptr2 <= end2)
{ {
if (*end == *ptr2) ptr2 = ptr2start;
while (ptr2 <= end2)
{
if (*end == *ptr2)
break;
++ptr2;
}
if (ptr2 > end2)
break; break;
++ptr2; end--;
m--;
} }
if (ptr2 > end2)
break;
end--;
m--;
} }
ret = (bytea *) palloc(VARHDRSZ + m); ret = (bytea *) palloc(VARHDRSZ + m);
SET_VARSIZE(ret, VARHDRSZ + m); SET_VARSIZE(ret, VARHDRSZ + m);
memcpy(VARDATA(ret), ptr, 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); PG_RETURN_BYTEA_P(ret);
} }
......
...@@ -9680,6 +9680,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context) ...@@ -9680,6 +9680,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
return true; return true;
case F_LTRIM_BYTEA_BYTEA:
case F_LTRIM_TEXT: case F_LTRIM_TEXT:
case F_LTRIM_TEXT_TEXT: case F_LTRIM_TEXT_TEXT:
/* TRIM() */ /* TRIM() */
...@@ -9694,6 +9695,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context) ...@@ -9694,6 +9695,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
return true; return true;
case F_RTRIM_BYTEA_BYTEA:
case F_RTRIM_TEXT: case F_RTRIM_TEXT:
case F_RTRIM_TEXT_TEXT: case F_RTRIM_TEXT_TEXT:
/* TRIM() */ /* TRIM() */
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202101171 #define CATALOG_VERSION_NO 202101181
#endif #endif
...@@ -5779,9 +5779,15 @@ ...@@ -5779,9 +5779,15 @@
{ oid => '2014', descr => 'position of substring', { oid => '2014', descr => 'position of substring',
proname => 'position', prorettype => 'int4', proargtypes => 'bytea bytea', proname => 'position', prorettype => 'int4', proargtypes => 'bytea bytea',
prosrc => 'byteapos' }, 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', proname => 'btrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
prosrc => 'byteatrim' }, 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', { oid => '2019', descr => 'convert timestamp with time zone to time',
proname => 'time', provolatile => 's', prorettype => 'time', proname => 'time', provolatile => 's', prorettype => 'time',
......
...@@ -1735,7 +1735,10 @@ select ...@@ -1735,7 +1735,10 @@ select
substring('foo' from 'oo') as ssf, -- historically-permitted abuse substring('foo' from 'oo') as ssf, -- historically-permitted abuse
trim(' ' from ' foo ') as bt, trim(' ' from ' foo ') as bt,
trim(leading ' ' from ' foo ') as lt, 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); select pg_get_viewdef('tt201v', true);
pg_get_viewdef pg_get_viewdef
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
...@@ -1753,7 +1756,10 @@ select pg_get_viewdef('tt201v', true); ...@@ -1753,7 +1756,10 @@ select pg_get_viewdef('tt201v', true);
"substring"('foo'::text, 'oo'::text) AS ssf, + "substring"('foo'::text, 'oo'::text) AS ssf, +
TRIM(BOTH ' '::text FROM ' foo '::text) AS bt, + TRIM(BOTH ' '::text FROM ' foo '::text) AS bt, +
TRIM(LEADING ' '::text FROM ' foo '::text) AS lt, + 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) (1 row)
-- corner cases with empty join conditions -- corner cases with empty join conditions
......
...@@ -2131,6 +2131,18 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea); ...@@ -2131,6 +2131,18 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
Tom Tom
(1 row) (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); SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
btrim btrim
------- -------
......
...@@ -605,7 +605,10 @@ select ...@@ -605,7 +605,10 @@ select
substring('foo' from 'oo') as ssf, -- historically-permitted abuse substring('foo' from 'oo') as ssf, -- historically-permitted abuse
trim(' ' from ' foo ') as bt, trim(' ' from ' foo ') as bt,
trim(leading ' ' from ' foo ') as lt, 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); select pg_get_viewdef('tt201v', true);
-- corner cases with empty join conditions -- corner cases with empty join conditions
......
...@@ -722,6 +722,8 @@ SELECT SUBSTRING('string'::bytea FROM -10 FOR 2147483646) AS "string"; ...@@ -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 SUBSTRING('string'::bytea FROM -10 FOR -2147483646) AS "error";
SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea); 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(E'\\000trim\\000'::bytea, E'\\000'::bytea);
SELECT btrim(''::bytea, E'\\000'::bytea); SELECT btrim(''::bytea, E'\\000'::bytea);
SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment