Как использовать группы захвата регулярных выражений в хранимых процедурах postgres (если вообще возможно)? - PullRequest
0 голосов
/ 13 апреля 2020

В системе я использую стандартный urn ( RFC8141 ) в качестве одного из полей. Из этой урны можно получить уникальный идентификатор. Странная вещь в урнах, описанных в RFC8141, заключается в том, что у вас может быть две разные урны, которые равны.

Чтобы проверить уникальные ключи, мне нужно извлечь разные части урны, которые составляют уникальный ключ. Для этого у меня есть это регулярное выражение ( регулярное выражение, которое соответствует URN по rfc8141 ):

\A(?i:urn:(?!urn:)(?<nid>[a-z0-9][a-z0-9-]{1,31}[^-]):(?<nss>(?:[-a-z0-9()+,.:=@;$_!*'&~\/]|%[0-9a-f]{2})+)(?:\?\+(?<rcomponent>.*?))?(?:\?=(?<qcomponent>.*?))?(?:#(?<fcomponent>.*?))?)\z

, что приводит к пяти именованным группам захвата (nid , nss, rcomponent, qcomponent en fcomponent). Только nid и nss важны для проверки уникальности / равенства. Или: даже если компоненты изменяются, если nid и nss одинаковы, два элемента / записи равны (независимо от значений компонентов). nid проверяется без учета регистра, nss проверяется с учетом регистра.

Теперь, чтобы проверить уникальность / равенство, я определяю «очищенную урну», которая является первичным ключом. , Я добавил триггер, чтобы я мог извлечь различные группы захвата. Я хотел бы сделать следующее:

  1. извлечь nid и nss (см. Регулярное выражение) из urn
  2. захватить их по имени. Здесь я не знаю, как это сделать: как я могу захватить эти две группы захвата в postgresql хранимой процедуре?
  3. добавить их как «очищенную урну», нижний регистр nid (чтобы иметь нечувствительность к регистру в этой части) и url-кодирование или url-декодирование строки (одно из двух, это не имеет значения, если оно согласовано). (Я также не уверен, есть ли функция кодирования / декодирования url в Postgres, но я задам другой вопрос, как только предыдущий будет решен :)).

Пример:

  • все эти urn с равны / эквивалентны (и я хочу, чтобы первичный ключ был urn:example:a123,z456):

    • urn:example:a123,z456
    • URN:example:a123,z456
    • urn:EXAMPLE:a123,z456
    • urn:example:a123,z456?+abc (?+ обозначает начало r-компонента)
    • urn:example:a123,z456?=xyz/something (?= обозначает начало компонента q)
    • urn:example:a123,z456#789 (# обозначает начало компонента f)
    • urn:example:a123%2Cz456
    • URN:EXAMPLE:a123%2cz456
  • urn:example:A123,z456 и urn:Example:A123,z456 оба имеют ключ urn:example:A123,z456, который отличается от предыдущих примеров (из-за чувствительности к регистру A123,z456).
  • только для полноты : urn:example:a123,z456?=xyz/something отличается от urn:example:a123,z456/something?=xyz: все после ?= (или ?+ или #) можно опустить, поэтому /something является частью первичного ключа в последнем случае, но не в бывший. (Это то, что регулярное выражение уже записывает.)

== РЕДАКТИРОВАТЬ 1: неназванные группы захвата ==

с неназванными группами захвата, это будет делаем то же самое:

select 
  g[2] as nid, 
  g[3] as nss, 
  g[4] as rcomp, 
  g[5] as qcomp, 
  g[6] as fcomp 
from (
  select regexp_matches('uRn:example:a123,z456?=xyz/something', 
                        '\A(urn:(?!urn:)([a-z0-9][a-z0-9-]{1,31}[^-]):((?:[-a-z0-9()+,.:=@;$_!*''&~\/]|%[0-9a-f]{2})+)(?:\?\+(.*?))?(?:\?=(.*?))?(?:#(.*?))?)$', 'i')
    g) 
  as ar;

(g[1] - полное совпадение, которое мне не нужно)

Я обновил запрос:

  • case нечувствительное сопоставление должно выполняться как флаг
  • без групп захвата (postgres, похоже, возникают проблемы с именами групп захвата)

и делает выборку в массиве, разбивая массив на столбцы.

1 Ответ

0 голосов
/ 15 апреля 2020

Именованный захват, похоже, не поддерживается, и, похоже, есть некоторые проблемы с жадным / ленивым поиском и отрицательным прогнозом. Итак, вот решение, которое отлично работает:

DO $$
BEGIN
    if not exists (SELECT 1 FROM pg_type WHERE typname = 'urn') then
        CREATE TYPE urn AS (nid text, nss text, rcomp text, qcomp text, fcomp text);
    end if;
END
$$;

CREATE or REPLACE FUNCTION spliturn(urnstring text) 
    RETURNS urn as $$
    DECLARE
      urn urn;
      urnregex text = concat(
        '\A(urn:(?!urn:)',
        '([a-z0-9][a-z0-9-]{1,31}[^-]):',
        '((?:[-a-z0-9()+,.:=@;$_!*''&~\/]|%[0-9a-f]{2})+)',
        '(?:\?\+(.*?))??',
        '(?:\?=(.*?))??',
        '(?:#(.*?))??',
        ')$');
    BEGIN
      select 
        lower(g[2]) as nid, 
        g[3] as nss, 
        g[4] as rcomp, 
        g[5] as qcomp, 
        g[6] as fcomp
      into urn
      from (select regexp_matches(urnstring, urnregex, 'i')
      g) as ar;
    RETURN urn;
    END;
$$ language 'plpgsql' immutable strict;

note

  • без именованных групп (?<...>)
  • указывают на поиск без учета регистра с флагом
  • замена \z на $ для соответствия концу строки
  • экранирование кавычки с другой кавычкой ('') для разрешения кавычек
  • двойного ?? для не жадный поиск (Таблица 9-14)
...