Hyperscan UDF

Список функций

  • Hyperscan::Grep(String) -> (String?) -> Bool
  • Hyperscan::Match(String) -> (String?) -> Bool
  • Hyperscan::BacktrackingGrep(String) -> (String?) -> Bool
  • Hyperscan::BacktrackingMatch(String) -> (String?) -> Bool
  • Hyperscan::MultiGrep(String) -> (String?) -> Tuple<Bool, Bool, ...>
  • Hyperscan::MultiMatch(String) -> (String?) -> Tuple<Bool, Bool, ...>
  • Hyperscan::Capture(String) -> (String?) -> String?
  • Hyperscan::Replace(String) -> (String?, String) -> String?

Hyperscan является opensource библиотекой для поиска по регулярным выражениям, за разработкой которой стоит Intel. Внутри имеет 4 реализации c использованием разных наборов процессорных инструкций (SSE3, SSE4.2, AVX2 и AVX512), среди которых автоматически выбирается нужная в соответствии с текущим процессором.

По умолчанию все функции работают в однобайтовом режиме, но если регулярное выражение является валидной UTF-8 строкой, но не является валидной ASCII строкой, — автоматически включается режим UTF-8.

Синтаксис вызова

При вызове напрямую, чтобы избежать компиляции регулярного выражения на каждой строке таблицы, необходимо обернуть вызов функции в именованное выражение:

$re = Hyperscan::Grep("\\d+");      -- создаем вызываемое значение для проверки конкретного регулярного выражения
SELECT * FROM table WHERE $re(key); -- используем его для фильтрации таблицы

Обратите внимание на экранирование спецсимволов в регулярном выражении. Второй слеш нужен, так как все стандартные строковые литералы в SQL могут принимать С-escaped строки, а последовательность \d не является валидной последовательностью, и даже если бы являлась — не приводила бы к ожидаемому эффекту поиска чисел.

Есть возможность отключить чувствительность к регистру (то есть включить case-insensitive режим), указав в начале регулярного выражения флаг (?i).

$value = "xaaxaaXaa";

$match = Hyperscan::Match("a.*");
$grep = Hyperscan::Grep("axa");
$insensitive_grep = Hyperscan::Grep("(?i)axaa$");
$multi_match = Hyperscan::MultiMatch(@@a.*
.*a.*
.*a
.*axa.*@@);

$capture = Hyperscan::Capture(".*a{2}.*");
$capture_many = Hyperscan::Capture(".*x(a+).*");
$replace = Hyperscan::Replace("xa");

SELECT
    $match($value) AS match,
    $grep($value) AS grep,
    $insensitive_grep($value) AS insensitive_grep,
    $multi_match($value) AS multi_match,
    $multi_match($value).0 AS some_multi_match,
    $capture($value) AS capture,
    $capture_many($value) AS capture_many,
    $replace($value, "b") AS replace
;

/*
- match: false
- grep: true
- insensitive_grep: true
- multi_match: (false, true, true, true)
- some_multi_match: false
- capture: "xaaa"
- capture_many: "xaa"
- replace: "babaXaa"
*/

Grep

Проверяет совпадение регулярного выражения с частью строки (произвольной подстрокой).

Функцию Hyperscan::Grep можно вызвать с помощью выражения REGEXP (см. описание базового синтаксиса выражений).

Например, следующие два запроса эквивалентны (в том числе по эффективности вычислений):

  • $grep = Hyperscan::Grep("b+"); SELECT $grep("aaabccc");
  • SELECT "aaabccc" REGEXP "b+";

Match

Проверяет совпадение регулярного выражения со строкой целиком.

Чтобы получить результат, аналогиный Grep (где учитывается совпадание с подстрокой), нужно обрамлять регулярное выражение с обоих сторон в .*, например .*foo.* вместо foo, но с точки зрения читабельности обычно лучше поменять функцию.

BacktrackingGrep / BacktrackingMatch

По принципу работы полностью совпадают с одноимёнными функциями без префикса Backtracking, но поддерживают более широкий ассортимент регулярных выражений за счет того, что если конкретное регулярное выражение в полном объеме не поддерживается Hyperscan, то библиотека переключается в режим предварительной фильтрации (prefilter). В этом случае она отвечает не «да» или «нет», а «точно нет» или «может быть». Ответы «может быть» затем автоматически перепроверяются на уровне UDF с помощью медленной, но более функциональной библиотеки libpcre.

Именно эти функции на данный момент вызываются в бинарных операторах REGEXP и MATCH.

MultiGrep / MultiMatch

Библиотека Hyperscan предоставляет возможность за один проход по тексту проверить несколько регулярных выражений и получить по каждому из них отдельный ответ. Но если вас интересует совпадение строки с любым из перечисленных выражений (результаты объединялись бы через «или»), то намного эффективнее сделать одно регулярное выражение, объединив части с помощью оператора |, и использовать для поиска обычный Grep или Match.

При вызове функций MultiGrep/MultiMatch регулярные выражения передаются по одному на строку с использованием многострочных строковых литералов:

$multi_match = Hyperscan::MultiMatch(@@a.*
.*x.*
.*axa.*@@);

SELECT
$multi_match("a") AS a,
$multi_match("axa") AS axa;

/*
- a: `(true, false, false)`
- axa: `(true, true, true)`
*/

Capture и Replace

В библиотеке Hyperscan отсутствует развитая функциональность для подобных операций, так что Hyperscan::Capture и Hyperscan::Replace хоть и реализованы для единообразия, но для сколько-либо нетривиальных поисков и замен лучше использовать одноимённые функции из Re2 UDF: