Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I mimic the php urldecode function in postgresql?

Tags:

postgresql

I have a column url encoded with urlencode in php. I wish to make a select like this SELECT some_mix_of_functions(...) AS Decoded FROM table

Replace is not a good solution because I will have to add all the decoding by hand. Any other solution to get the desire result ?

like image 592
johnlemon Avatar asked Dec 13 '22 12:12

johnlemon


1 Answers

Yes you can:

CREATE OR REPLACE FUNCTION decode_url_part(p varchar) RETURNS varchar AS $$
SELECT convert_from(CAST(E'\\x' || string_agg(CASE WHEN length(r.m[1]) = 1 THEN encode(convert_to(r.m[1], 'SQL_ASCII'), 'hex') ELSE substring(r.m[1] from 2 for 2) END, '') AS bytea), 'UTF8')
FROM regexp_matches($1, '%[0-9a-f][0-9a-f]|.', 'gi') AS r(m);
$$ LANGUAGE SQL IMMUTABLE STRICT;

This creates a function decode_url_part, then you can use it like that:

SELECT decode_url_part('your%20urlencoded%20string')

Or you can just use the mix of functions and subqueries from the body of the above function.

This doesn't handle '+' characters (representing whitespace), but I guess adding this is quite easy (if you ever need it).

Also, this assumes utf-8 encoding for non-ascii characters, but you can replace 'UTF8' with your own encoding if you want.

It should be noted that the above code relies on undocumented postgresql feature, namely that the results of regexp_matches function are processed in the order they occur in the original string (which is natural, but not specified in docs).

As Pablo Santa Cruz notes, string_agg is a PostgreSQL 9.0 aggregate function. The equivalent code below doesn't use it (I hope it works for 8.x):

SELECT convert_from(CAST(E'\\x' || array_to_string(ARRAY(
    SELECT CASE WHEN length(r.m[1]) = 1 THEN encode(convert_to(r.m[1], 'SQL_ASCII'), 'hex') ELSE substring(r.m[1] from 2 for 2) END
    FROM regexp_matches($1, '%[0-9a-f][0-9a-f]|.', 'gi') AS r(m)
), '') AS bytea), 'UTF8');
like image 119
Mike Avatar answered Jan 11 '23 13:01

Mike