create or replace function hex2chr(pxx) as replace(pxx,'%','').from_hex().decode() ; create or replace function urldecode(url) as ( WITH RECURSIVE cte AS ( -- Base case: set xx as the first sequence of %XX encodings if any SELECT replace(url, '+', ' ') AS decoded, regexp_extract(replace(url, '+', ' '), '(%[0-9A-Fa-f]{2})+') AS xx UNION ALL -- Recursive case: replace the xx encodings with the decoded character(s) SELECT replace(decoded, xx, xx.hex2chr()) AS decoded, regexp_extract(replace(decoded, xx, ' '), '(%[0-9A-Fa-f]{2})+') as xx FROM cte WHERE xx != '' ) SELECT decoded FROM cte WHERE xx = '' ); SELECT s, url_decode(s), urldecode(s) FROM unnest( [ 'http%3A%2F%2Ffoo%20bar%2F', 'google.com/search?q=%60Abdu%27l-Bah%C3%A1', '%25%32%35' ]) _(s);