Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a6cf3df

Browse files
committed
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
1 parent a3ed4d1 commit a6cf3df

File tree

9 files changed

+194
-48
lines changed

9 files changed

+194
-48
lines changed

doc/src/sgml/func.sgml

+45-4
Original file line numberDiff line numberDiff line change
@@ -3948,15 +3948,16 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
39483948
<indexterm>
39493949
<primary>trim</primary>
39503950
</indexterm>
3951-
<function>trim</function> ( <optional> <literal>BOTH</literal> </optional>
3951+
<function>trim</function> ( <optional> <literal>LEADING</literal> | <literal>TRAILING</literal> | <literal>BOTH</literal> </optional>
39523952
<parameter>bytesremoved</parameter> <type>bytea</type> <literal>FROM</literal>
39533953
<parameter>bytes</parameter> <type>bytea</type> )
39543954
<returnvalue>bytea</returnvalue>
39553955
</para>
39563956
<para>
39573957
Removes the longest string containing only bytes appearing in
3958-
<parameter>bytesremoved</parameter> from the start
3959-
and end of <parameter>bytes</parameter>.
3958+
<parameter>bytesremoved</parameter> from the start,
3959+
end, or both ends (<literal>BOTH</literal> is the default)
3960+
of <parameter>bytes</parameter>.
39603961
</para>
39613962
<para>
39623963
<literal>trim('\x9012'::bytea from '\x1234567890'::bytea)</literal>
@@ -3966,7 +3967,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
39663967

39673968
<row>
39683969
<entry role="func_table_entry"><para role="func_signature">
3969-
<function>trim</function> ( <optional> <literal>BOTH</literal> </optional> <optional> <literal>FROM</literal> </optional>
3970+
<function>trim</function> ( <optional> <literal>LEADING</literal> | <literal>TRAILING</literal> | <literal>BOTH</literal> </optional> <optional> <literal>FROM</literal> </optional>
39703971
<parameter>bytes</parameter> <type>bytea</type>,
39713972
<parameter>bytesremoved</parameter> <type>bytea</type> )
39723973
<returnvalue>bytea</returnvalue>
@@ -4109,6 +4110,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
41094110
</para></entry>
41104111
</row>
41114112

4113+
<row>
4114+
<entry role="func_table_entry"><para role="func_signature">
4115+
<indexterm>
4116+
<primary>ltrim</primary>
4117+
</indexterm>
4118+
<function>ltrim</function> ( <parameter>bytes</parameter> <type>bytea</type>,
4119+
<parameter>bytesremoved</parameter> <type>bytea</type> )
4120+
<returnvalue>bytea</returnvalue>
4121+
</para>
4122+
<para>
4123+
Removes the longest string containing only bytes appearing in
4124+
<parameter>bytesremoved</parameter> from the start of
4125+
<parameter>bytes</parameter>.
4126+
</para>
4127+
<para>
4128+
<literal>ltrim('\x1234567890'::bytea, '\x9012'::bytea)</literal>
4129+
<returnvalue>\x34567890</returnvalue>
4130+
</para></entry>
4131+
</row>
4132+
41124133
<row>
41134134
<entry role="func_table_entry"><para role="func_signature">
41144135
<indexterm>
@@ -4127,6 +4148,26 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
41274148
</para></entry>
41284149
</row>
41294150

4151+
<row>
4152+
<entry role="func_table_entry"><para role="func_signature">
4153+
<indexterm>
4154+
<primary>rtrim</primary>
4155+
</indexterm>
4156+
<function>rtrim</function> ( <parameter>bytes</parameter> <type>bytea</type>,
4157+
<parameter>bytesremoved</parameter> <type>bytea</type> )
4158+
<returnvalue>bytea</returnvalue>
4159+
</para>
4160+
<para>
4161+
Removes the longest string containing only bytes appearing in
4162+
<parameter>bytesremoved</parameter> from the end of
4163+
<parameter>bytes</parameter>.
4164+
</para>
4165+
<para>
4166+
<literal>rtrim('\x1234567890'::bytea, '\x9012'::bytea)</literal>
4167+
<returnvalue>\x12345678</returnvalue>
4168+
</para></entry>
4169+
</row>
4170+
41304171
<row>
41314172
<entry role="func_table_entry"><para role="func_signature">
41324173
<indexterm>

src/backend/utils/adt/oracle_compat.c

+113-39
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
static text *dotrim(const char *string, int stringlen,
2525
const char *set, int setlen,
2626
bool doltrim, bool dortrim);
27+
static bytea *dobyteatrim(bytea *string, bytea *set,
28+
bool doltrim, bool dortrim);
2729

2830

2931
/********************************************************************
@@ -521,27 +523,12 @@ dotrim(const char *string, int stringlen,
521523
return cstring_to_text_with_len(string, stringlen);
522524
}
523525

524-
/********************************************************************
525-
*
526-
* byteatrim
527-
*
528-
* Syntax:
529-
*
530-
* bytea byteatrim(bytea string, bytea set)
531-
*
532-
* Purpose:
533-
*
534-
* Returns string with characters removed from the front and back
535-
* up to the first character not in set.
536-
*
537-
* Cloned from btrim and modified as required.
538-
********************************************************************/
539-
540-
Datum
541-
byteatrim(PG_FUNCTION_ARGS)
526+
/*
527+
* Common implementation for bytea versions of btrim, ltrim, rtrim
528+
*/
529+
bytea *
530+
dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
542531
{
543-
bytea *string = PG_GETARG_BYTEA_PP(0);
544-
bytea *set = PG_GETARG_BYTEA_PP(1);
545532
bytea *ret;
546533
char *ptr,
547534
*end,
@@ -556,47 +543,134 @@ byteatrim(PG_FUNCTION_ARGS)
556543
setlen = VARSIZE_ANY_EXHDR(set);
557544

558545
if (stringlen <= 0 || setlen <= 0)
559-
PG_RETURN_BYTEA_P(string);
546+
return string;
560547

561548
m = stringlen;
562549
ptr = VARDATA_ANY(string);
563550
end = ptr + stringlen - 1;
564551
ptr2start = VARDATA_ANY(set);
565552
end2 = ptr2start + setlen - 1;
566553

567-
while (m > 0)
554+
if (doltrim)
568555
{
569-
ptr2 = ptr2start;
570-
while (ptr2 <= end2)
556+
while (m > 0)
571557
{
572-
if (*ptr == *ptr2)
558+
ptr2 = ptr2start;
559+
while (ptr2 <= end2)
560+
{
561+
if (*ptr == *ptr2)
562+
break;
563+
++ptr2;
564+
}
565+
if (ptr2 > end2)
573566
break;
574-
++ptr2;
567+
ptr++;
568+
m--;
575569
}
576-
if (ptr2 > end2)
577-
break;
578-
ptr++;
579-
m--;
580570
}
581571

582-
while (m > 0)
572+
if (dortrim)
583573
{
584-
ptr2 = ptr2start;
585-
while (ptr2 <= end2)
574+
while (m > 0)
586575
{
587-
if (*end == *ptr2)
576+
ptr2 = ptr2start;
577+
while (ptr2 <= end2)
578+
{
579+
if (*end == *ptr2)
580+
break;
581+
++ptr2;
582+
}
583+
if (ptr2 > end2)
588584
break;
589-
++ptr2;
585+
end--;
586+
m--;
590587
}
591-
if (ptr2 > end2)
592-
break;
593-
end--;
594-
m--;
595588
}
596589

597590
ret = (bytea *) palloc(VARHDRSZ + m);
598591
SET_VARSIZE(ret, VARHDRSZ + m);
599592
memcpy(VARDATA(ret), ptr, m);
593+
return ret;
594+
}
595+
596+
/********************************************************************
597+
*
598+
* byteatrim
599+
*
600+
* Syntax:
601+
*
602+
* bytea byteatrim(bytea string, bytea set)
603+
*
604+
* Purpose:
605+
*
606+
* Returns string with characters removed from the front and back
607+
* up to the first character not in set.
608+
*
609+
* Cloned from btrim and modified as required.
610+
********************************************************************/
611+
612+
Datum
613+
byteatrim(PG_FUNCTION_ARGS)
614+
{
615+
bytea *string = PG_GETARG_BYTEA_PP(0);
616+
bytea *set = PG_GETARG_BYTEA_PP(1);
617+
bytea *ret;
618+
619+
ret = dobyteatrim(string, set, true, true);
620+
621+
PG_RETURN_BYTEA_P(ret);
622+
}
623+
624+
/********************************************************************
625+
*
626+
* bytealtrim
627+
*
628+
* Syntax:
629+
*
630+
* bytea bytealtrim(bytea string, bytea set)
631+
*
632+
* Purpose:
633+
*
634+
* Returns string with initial characters removed up to the first
635+
* character not in set.
636+
*
637+
********************************************************************/
638+
639+
Datum
640+
bytealtrim(PG_FUNCTION_ARGS)
641+
{
642+
bytea *string = PG_GETARG_BYTEA_PP(0);
643+
bytea *set = PG_GETARG_BYTEA_PP(1);
644+
bytea *ret;
645+
646+
ret = dobyteatrim(string, set, true, false);
647+
648+
PG_RETURN_BYTEA_P(ret);
649+
}
650+
651+
/********************************************************************
652+
*
653+
* byteartrim
654+
*
655+
* Syntax:
656+
*
657+
* bytea byteartrim(bytea string, bytea set)
658+
*
659+
* Purpose:
660+
*
661+
* Returns string with final characters removed after the last
662+
* character not in set.
663+
*
664+
********************************************************************/
665+
666+
Datum
667+
byteartrim(PG_FUNCTION_ARGS)
668+
{
669+
bytea *string = PG_GETARG_BYTEA_PP(0);
670+
bytea *set = PG_GETARG_BYTEA_PP(1);
671+
bytea *ret;
672+
673+
ret = dobyteatrim(string, set, false, true);
600674

601675
PG_RETURN_BYTEA_P(ret);
602676
}

src/backend/utils/adt/ruleutils.c

+2
Original file line numberDiff line numberDiff line change
@@ -9680,6 +9680,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
96809680
appendStringInfoChar(buf, ')');
96819681
return true;
96829682

9683+
case F_LTRIM_BYTEA_BYTEA:
96839684
case F_LTRIM_TEXT:
96849685
case F_LTRIM_TEXT_TEXT:
96859686
/* TRIM() */
@@ -9694,6 +9695,7 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
96949695
appendStringInfoChar(buf, ')');
96959696
return true;
96969697

9698+
case F_RTRIM_BYTEA_BYTEA:
96979699
case F_RTRIM_TEXT:
96989700
case F_RTRIM_TEXT_TEXT:
96999701
/* TRIM() */

src/include/catalog/catversion.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 202101171
56+
#define CATALOG_VERSION_NO 202101181
5757

5858
#endif

src/include/catalog/pg_proc.dat

+7-1
Original file line numberDiff line numberDiff line change
@@ -5779,9 +5779,15 @@
57795779
{ oid => '2014', descr => 'position of substring',
57805780
proname => 'position', prorettype => 'int4', proargtypes => 'bytea bytea',
57815781
prosrc => 'byteapos' },
5782-
{ oid => '2015', descr => 'trim both ends of string',
5782+
{ oid => '2015', descr => 'trim selected bytes from both ends of string',
57835783
proname => 'btrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
57845784
prosrc => 'byteatrim' },
5785+
{ oid => '9612', descr => 'trim selected bytes from left end of string',
5786+
proname => 'ltrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
5787+
prosrc => 'bytealtrim' },
5788+
{ oid => '9613', descr => 'trim selected bytes from right end of string',
5789+
proname => 'rtrim', prorettype => 'bytea', proargtypes => 'bytea bytea',
5790+
prosrc => 'byteartrim' },
57855791

57865792
{ oid => '2019', descr => 'convert timestamp with time zone to time',
57875793
proname => 'time', provolatile => 's', prorettype => 'time',

src/test/regress/expected/create_view.out

+8-2
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,10 @@ select
17351735
substring('foo' from 'oo') as ssf, -- historically-permitted abuse
17361736
trim(' ' from ' foo ') as bt,
17371737
trim(leading ' ' from ' foo ') as lt,
1738-
trim(trailing ' foo ') as rt;
1738+
trim(trailing ' foo ') as rt,
1739+
trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea) as btb,
1740+
trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea) as ltb,
1741+
trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea) as rtb;
17391742
select pg_get_viewdef('tt201v', true);
17401743
pg_get_viewdef
17411744
-----------------------------------------------------------------------------------------------
@@ -1753,7 +1756,10 @@ select pg_get_viewdef('tt201v', true);
17531756
"substring"('foo'::text, 'oo'::text) AS ssf, +
17541757
TRIM(BOTH ' '::text FROM ' foo '::text) AS bt, +
17551758
TRIM(LEADING ' '::text FROM ' foo '::text) AS lt, +
1756-
TRIM(TRAILING FROM ' foo '::text) AS rt;
1759+
TRIM(TRAILING FROM ' foo '::text) AS rt, +
1760+
TRIM(BOTH '\x00'::bytea FROM '\x00546f6d00'::bytea) AS btb, +
1761+
TRIM(LEADING '\x00'::bytea FROM '\x00546f6d00'::bytea) AS ltb, +
1762+
TRIM(TRAILING '\x00'::bytea FROM '\x00546f6d00'::bytea) AS rtb;
17571763
(1 row)
17581764

17591765
-- corner cases with empty join conditions

src/test/regress/expected/strings.out

+12
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,18 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
21312131
Tom
21322132
(1 row)
21332133

2134+
SELECT trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea);
2135+
ltrim
2136+
---------
2137+
Tom\000
2138+
(1 row)
2139+
2140+
SELECT trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea);
2141+
rtrim
2142+
---------
2143+
\000Tom
2144+
(1 row)
2145+
21342146
SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
21352147
btrim
21362148
-------

src/test/regress/sql/create_view.sql

+4-1
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,10 @@ select
605605
substring('foo' from 'oo') as ssf, -- historically-permitted abuse
606606
trim(' ' from ' foo ') as bt,
607607
trim(leading ' ' from ' foo ') as lt,
608-
trim(trailing ' foo ') as rt;
608+
trim(trailing ' foo ') as rt,
609+
trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea) as btb,
610+
trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea) as ltb,
611+
trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea) as rtb;
609612
select pg_get_viewdef('tt201v', true);
610613

611614
-- corner cases with empty join conditions

src/test/regress/sql/strings.sql

+2
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ SELECT SUBSTRING('string'::bytea FROM -10 FOR 2147483646) AS "string";
722722
SELECT SUBSTRING('string'::bytea FROM -10 FOR -2147483646) AS "error";
723723

724724
SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
725+
SELECT trim(leading E'\\000'::bytea from E'\\000Tom\\000'::bytea);
726+
SELECT trim(trailing E'\\000'::bytea from E'\\000Tom\\000'::bytea);
725727
SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
726728
SELECT btrim(''::bytea, E'\\000'::bytea);
727729
SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);

0 commit comments

Comments
 (0)