7

PostgreSQL/plpgsql 中是否有与 javascripts encodeURI 相同的函数/存储过程?

这是什么意思?Javascript 有一个方便的内置函数来编码任何类型的 url:

encodeURI(url) -> 返回编码后的 url

例如: encodeURI('http://hu.wikipedia.org/wiki/São_Paulo')-> 返回一个字符串,它是"http://hu.wikipedia.org/wiki/S%C3%A3o_Paulo"

我在找一模一样的。

我不想分别编码每个参数。我不想要一个像 javascript encodeURIComponent 这样不一样的函数。上面的示例产生了不同的输出

encodeURIComponent('http://hu.wikipedia.org/wiki/São_Paulo')

->"http%3A%2F%2Fhu.wikipedia.org%2Fwiki%2FS%C3%A3o_Paulo"

它对整个字符串进行编码,而不仅仅是路径部分。所以这不是我要找的。我需要一个 plpgsql 函数,它会产生与 javascript 函数 encodeURI 等效的输出。

谢谢!

4

8 回答 8

15

慢且低效,考虑做C这个函数的版本:

CREATE OR REPLACE FUNCTION urlencode(in_str text, OUT _result text)
    STRICT IMMUTABLE AS $urlencode$
DECLARE
    _i      int4;
    _temp   varchar;
    _ascii  int4;
BEGIN
    _result = '';
    FOR _i IN 1 .. length(in_str) LOOP
        _temp := substr(in_str, _i, 1);
        IF _temp ~ '[0-9a-zA-Z:/@._?#-]+' THEN
            _result := _result || _temp;
        ELSE
            _ascii := ascii(_temp);
            IF _ascii > x'07ff'::int4 THEN
                RAISE EXCEPTION 'Won''t deal with 3 (or more) byte sequences.';
            END IF;
            IF _ascii <= x'07f'::int4 THEN
                _temp := '%'||to_hex(_ascii);
            ELSE
                _temp := '%'||to_hex((_ascii & x'03f'::int4)+x'80'::int4);
                _ascii := _ascii >> 6;
                _temp := '%'||to_hex((_ascii & x'01f'::int4)+x'c0'::int4)
                            ||_temp;
            END IF;
            _result := _result || upper(_temp);
        END IF;
    END LOOP;
    RETURN ;
END;
$urlencode$ LANGUAGE plpgsql;

结果:

# select urlencode('http://hu.wikipedia.org/wiki/São_Paulo');
-[ RECORD 1 ]------------------------------------------
urlencode | http://hu.wikipedia.org/wiki/S%C3%A3o_Paulo
于 2012-04-25T18:58:43.527 回答
6

我写了 PostgreSQL 扩展url_encode来解决这个问题。

postgres=# select url_encode('http://hu.wikipedia.org/wiki/São_Paulo');
                      url_encode                       
───────────────────────────────────────────────────────
http%3A%2F%2Fhu.wikipedia.org%2Fwiki%2FS%C3%A3o_Paulo

或者

postgres=# select uri_encode('http://hu.wikipedia.org/wiki/São_Paulo');
               uri_encode                  
---------------------------------------------
http://hu.wikipedia.org/wiki/S%C3%A3o_Paulo
于 2012-04-29T14:21:51.320 回答
6

使用 PL/V8...作弊?

create function encode_uri(text) returns text language plv8 strict immutable as $$
  return encodeURI($1);
$$;
于 2014-09-24T22:35:15.107 回答
5

今天我遇到了“不会处理 3 个(或更多)字节序列”。对于韩语字符,而我一直在使用@vyegorov 的答案很长一段时间,超过一年,需要更改它只是转储带有“%”前缀的 bytea 十六进制字符串。

CREATE OR REPLACE FUNCTION urlencode(in_str text, OUT _result text)
    STRICT IMMUTABLE AS $urlencode$
DECLARE
    _i      int4;
    _temp   varchar;
    _hex    varchar;
    _ascii  int4;
BEGIN
    _result = '';
    FOR _i IN 1 .. length(in_str) LOOP
        _temp := substr(in_str, _i, 1);
        IF _temp ~ '[0-9a-zA-Z:/@._?#-]+' THEN
            _result := _result || _temp;
        ELSE
            _hex := encode(_temp::bytea, 'hex');
            _temp := '';
            WHILE LENGTH(_hex) > 0 LOOP
                _temp := _temp || '%' || SUBSTRING(_hex, 1, 2);
                _hex := SUBSTRING(_hex, 3, 999);
            END LOOP;
            _result := _result || upper(_temp);
        END IF;
    END LOOP;
    RETURN ;
END;
$urlencode$ LANGUAGE plpgsql;

例子,

SELECT urlencode('a') UNION ALL  --> "a"
SELECT urlencode('À') UNION ALL  --> "%C3%80"
SELECT urlencode('Ā') UNION ALL  --> "%C4%80"
SELECT urlencode('ə') UNION ALL  --> "%C9%99"
SELECT urlencode('α') UNION ALL  --> "%CE%B1"
SELECT urlencode('가') UNION ALL --> "%EA%B0%80"
SELECT urlencode('上') UNION ALL --> "%E4%B8%8A"
SELECT urlencode('い')           --> "%E3%81%84"
于 2016-11-23T11:10:09.673 回答
2

在 tsohr 和 Nick 的答案中使用 ::bytea 是错误的。做一个 SELECT '\'::bytea 看看为什么。

在这两种情况下 convert_to(x, 'utf-8') 都会给出想要的结果。

于 2019-07-01T19:28:14.863 回答
2

这是一个非常古老的线程,但令人惊讶的是,以前发布的解决方案似乎都没有符合相关的RFC 3986。所以这里有两个 PostgreSQL 函数encode_uri(如果你想编码一个完整的 URI)和encode_uri_component(如果你只想编码 URI 的一个组件,例如查询参数的键或值),它们结合了Nick 的错误解决方案ElDiabolo 的注释,以及Kev 从相关线程中的部分解决方案 到单个工作 SQL-only 解决方案。

它是如何工作的:首先,将字符串分解为单个字符,然后对每个多字节字符和每个不允许的字符进行十六进制编码。

编码 URI 组件:

create or replace function encode_uri_component(text) returns text as $$
    select string_agg(
        case
            when bytes > 1 or c !~ '[0-9a-zA-Z_.!~*''()-]+' then 
                regexp_replace(encode(convert_to(c, 'utf-8')::bytea, 'hex'), '(..)', E'%\\1', 'g')
            else 
                c
        end,
        ''
    )
    from (
        select c, octet_length(c) bytes
        from regexp_split_to_table($1, '') c
    ) q;
$$ language sql immutable strict;

编码完整的 URI:

create or replace function encode_uri(text) returns text as $$
    select string_agg(
        case
            when bytes > 1 or c !~ '[0-9a-zA-Z_.!~*''();,/?:@&=+$#-]+' then 
                regexp_replace(encode(convert_to(c, 'utf-8')::bytea, 'hex'), '(..)', E'%\\1', 'g')
            else 
                c
        end,
        ''
    )
    from (
        select c, octet_length(c) bytes
        from regexp_split_to_table($1, '') c
    ) q;
$$ language sql immutable strict;
于 2020-02-17T10:14:23.527 回答
2

PLPython3 提供了一种简单的方法。

如果您还没有 plpython3,请安装它。命令行的示例包安装:

apt-get install postgresql-plpython3-9.5

创建语言(如果您还没有为其他功能完成它):

CREATE LANGUAGE plpython3u;

之后的简单功能:

CREATE OR REPLACE FUNCTION encode_uri(input_string text)
  RETURNS text
AS $$
  import urllib.parse
  return urllib.parse.quote_plus(input_string, safe='~@#$&()*!+=:;,.?/\\''')
$$ LANGUAGE plpython3u immutable strict;
于 2020-09-24T19:55:50.117 回答
1

这是支持多字节字符(包括 3 字节和 4 字节表情符号)的“纯 SQL”(不需要 plv8、plpython 甚至 plpgsql)实现:

create or replace function urlencode(text) returns text as $$
  select
    string_agg(
      case
        when ol>1 or ch !~ '[0-9a-zA-Z:/@._?#-]+' 
          then regexp_replace(upper(substring(ch::bytea::text, 3)), '(..)', E'%\\1', 'g')
        else ch
      end,
      ''
    )
  from (
    select ch, octet_length(ch) as ol
    from regexp_split_to_table($1, '') as ch
  ) as s;
$$ language sql immutable strict;

(来源:https ://github.com/NikolayS/postgrest-google-translate/pull/8 )

于 2016-10-19T02:36:17.890 回答