Difference between revisions of "Multi Replace plpgsql"

From PostgreSQL wiki
Jump to: navigation, search
(Created page with "{{SnippetInfo|Replace multiple strings in a single pass|lang=PL/PgSQL|category=Library}} <source lang="sql"> /* Substitute a set of substrings within a larger string. When ...")
 
(add quote_meta)
Line 1: Line 1:
 
{{SnippetInfo|Replace multiple strings in a single pass|lang=PL/PgSQL|category=Library}}
 
{{SnippetInfo|Replace multiple strings in a single pass|lang=PL/PgSQL|category=Library}}
 +
<source lang="sql">
 +
/* This function quotes characters that may be interpreted as special in a regular expression.
 +
  It's used by the function below and declared separately for clarity. */
 +
CREATE FUNCTION quote_meta(text) RETURNS text AS $$
 +
  select regexp_replace($1, '([\[\]\\\^\$\.\|\?\*\+\(\)])', '\\\1', 'g');
 +
$$ language sql strict immutable;
 +
</source>
 +
 
<source lang="sql">
 
<source lang="sql">
 
/* Substitute a set of substrings within a larger string.
 
/* Substitute a set of substrings within a larger string.

Revision as of 12:26, 30 December 2019

Library Snippets

Replace multiple strings in a single pass

Works with PostgreSQL

Any version

Written in

PL/PgSQL

Depends on

Nothing

/* This function quotes characters that may be interpreted as special in a regular expression.
   It's used by the function below and declared separately for clarity. */
CREATE FUNCTION quote_meta(text) RETURNS text AS $$
  select regexp_replace($1, '([\[\]\\\^\$\.\|\?\*\+\(\)])', '\\\1', 'g');
$$ language sql strict immutable;
/* Substitute a set of substrings within a larger string.
   When several strings match, the longest wins.
   Similar to php's strtr(string $str, array $replace_pairs).
   Example:
   select multi_replace('foo and bar is not foobar',
             '{"bar":"foo", "foo":"bar", "foobar":"foobar"}'::jsonb);
   => 'bar and foo is not foobar'
 */
CREATE FUNCTION multi_replace(str text, substitutions jsonb)
RETURNS text
as $$
DECLARE
 rx text;
 s_left text;
 s_tail text;
 res text:='';
BEGIN
 select string_agg(quote_meta(term), '|' )
 from jsonb_object_keys(substitutions) as x(term)
   where term <> ''
 into rx;

 if (coalesce(rx, '') = '') then
   -- the loop on the RE can't work with an empty alternation
   return str;
 end if;

 rx := concat('^(.*?)(', rx, ')(.*)$'); -- match no more than 1 row   

 loop
   s_tail := str;
   select 
       concat(matches[1], substitutions->>matches[2]),
       matches[3]
    from
      regexp_matches(str, rx, 'g') as matches
    into s_left, str;
    
   exit when s_left is null;
   res := res || s_left;

 end loop;

 res := res || s_tail;
 return res;

END 
$$ LANGUAGE plpgsql strict immutable;