MCPcopy Create free account
hub / github.com/apache/arrow-rs / regex_like

Function regex_like

arrow-string/src/predicate.rs:247–303  ·  view source on GitHub ↗

Transforms a like `pattern` to a regex compatible pattern. To achieve that, it does: 1. Replace `LIKE` multi-character wildcards `%` => `.*` (unless they're at the start or end of the pattern, where the regex is just truncated - e.g. `%foo%` => `foo` rather than `^.*foo.*$`) 2. Replace `LIKE` single-character wildcards `_` => `.` 3. Escape regex meta characters to match them and not be evaluated

(pattern: &str, case_insensitive: bool)

Source from the content-addressed store, hash-verified

245/// 3. Escape regex meta characters to match them and not be evaluated as regex special chars. e.g. `.` => `\\.`
246/// 4. Replace escaped `LIKE` wildcards removing the escape characters to be able to match it as a regex. e.g. `\\%` => `%`
247fn regex_like(pattern: &str, case_insensitive: bool) -> Result<Regex, ArrowError> {
248 let mut result = String::with_capacity(pattern.len() * 2);
249 let mut chars_iter = pattern.chars().peekable();
250 match chars_iter.peek() {
251 // if the pattern starts with `%`, we avoid starting the regex with a slow but meaningless `^.*`
252 Some('%') => {
253 chars_iter.next();
254 }
255 _ => result.push('^'),
256 };
257
258 while let Some(c) = chars_iter.next() {
259 match c {
260 '\\' => {
261 match chars_iter.peek() {
262 Some(&next) => {
263 if regex_syntax::is_meta_character(next) {
264 result.push('\\');
265 }
266 result.push(next);
267 // Skipping the next char as it is already appended
268 chars_iter.next();
269 }
270 None => {
271 // Trailing backslash in the pattern. E.g. PostgreSQL and Trino treat it as an error, but e.g. Snowflake treats it as a literal backslash
272 result.push('\\');
273 result.push('\\');
274 }
275 }
276 }
277 '%' => result.push_str(".*"),
278 '_' => result.push('.'),
279 c => {
280 if regex_syntax::is_meta_character(c) {
281 result.push('\\');
282 }
283 result.push(c);
284 }
285 }
286 }
287 // instead of ending the regex with `.*$` and making it needlessly slow, we just end the regex
288 if result.ends_with(".*") {
289 result.pop();
290 result.pop();
291 } else {
292 result.push('$');
293 }
294 RegexBuilder::new(&result)
295 .case_insensitive(case_insensitive)
296 .dot_matches_new_line(true)
297 .build()
298 .map_err(|e| {
299 ArrowError::InvalidArgumentError(format!(
300 "Unable to build regex from LIKE pattern: {e}"
301 ))
302 })
303}
304

Callers 3

likeMethod · 0.85
ilikeMethod · 0.85
test_regex_likeFunction · 0.85

Calls 5

lenMethod · 0.45
peekMethod · 0.45
nextMethod · 0.45
pushMethod · 0.45
buildMethod · 0.45

Tested by 1

test_regex_likeFunction · 0.68