From 4368e8767ba363768bfb7ab25fed1f2293e9e7a2 Mon Sep 17 00:00:00 2001 From: psvri Date: Thu, 26 Dec 2024 22:38:54 +0530 Subject: [PATCH 1/3] Fix like function giving wrong results --- core/Cargo.toml | 3 ++- core/vdbe/mod.rs | 48 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 4d731eb2b..1f2285f5f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -44,7 +44,8 @@ sieve-cache = "0.1.4" sqlite3-parser = { path = "../vendored/sqlite3-parser" } thiserror = "1.0.61" getrandom = { version = "0.2.15", features = ["js"] } -regex = "1.10.5" +regex = "1.11.1" +regex-syntax = { version = "0.8.5", default-features = false, features = ["unicode"] } chrono = "0.4.38" julian_day_converter = "0.3.2" jsonb = { version = "0.4.4", optional = true } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 427770155..f75df4a5f 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -46,7 +46,7 @@ use datetime::{exec_date, exec_time, exec_unixepoch}; use rand::distributions::{Distribution, Uniform}; use rand::{thread_rng, Rng}; -use regex::Regex; +use regex::{Regex, RegexBuilder}; use std::borrow::{Borrow, BorrowMut}; use std::cell::RefCell; use std::collections::{BTreeMap, HashMap}; @@ -3166,10 +3166,32 @@ fn exec_char(values: Vec) -> OwnedValue { } fn construct_like_regex(pattern: &str) -> Regex { - let mut regex_pattern = String::from("(?i)^"); - regex_pattern.push_str(&pattern.replace('%', ".*").replace('_', ".")); + + let mut regex_pattern = String::with_capacity(pattern.len() * 2); + + regex_pattern.push('^'); + + for c in pattern.chars() { + match c { + '\\' => regex_pattern.push_str("\\\\"), + '%' => regex_pattern.push_str(".*"), + '_' => regex_pattern.push('.'), + ch => { + if regex_syntax::is_meta_character(c) { + regex_pattern.push('\\'); + } + regex_pattern.push(ch); + } + } + } + regex_pattern.push('$'); - Regex::new(®ex_pattern).unwrap() + + RegexBuilder::new(®ex_pattern) + .case_insensitive(true) + .dot_matches_new_line(true) + .build() + .unwrap() } // Implements LIKE pattern matching. Caches the constructed regex if a cache is provided @@ -4316,12 +4338,18 @@ mod tests { ); } + #[test] + fn test_like_with_escape_or_regexmeta_chars() { + assert!(exec_like(None, r#"\%A"#, r#"\A"#)); + assert!(exec_like(None, "%a%a", "aaaa")); + } + #[test] fn test_like_no_cache() { assert!(exec_like(None, "a%", "aaaa")); assert!(exec_like(None, "%a%a", "aaaa")); - assert!(exec_like(None, "%a.a", "aaaa")); - assert!(exec_like(None, "a.a%", "aaaa")); + assert!(!exec_like(None, "%a.a", "aaaa")); + assert!(!exec_like(None, "a.a%", "aaaa")); assert!(!exec_like(None, "%a.ab", "aaaa")); } @@ -4330,15 +4358,15 @@ mod tests { let mut cache = HashMap::new(); assert!(exec_like(Some(&mut cache), "a%", "aaaa")); assert!(exec_like(Some(&mut cache), "%a%a", "aaaa")); - assert!(exec_like(Some(&mut cache), "%a.a", "aaaa")); - assert!(exec_like(Some(&mut cache), "a.a%", "aaaa")); + assert!(!exec_like(Some(&mut cache), "%a.a", "aaaa")); + assert!(!exec_like(Some(&mut cache), "a.a%", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.ab", "aaaa")); // again after values have been cached assert!(exec_like(Some(&mut cache), "a%", "aaaa")); assert!(exec_like(Some(&mut cache), "%a%a", "aaaa")); - assert!(exec_like(Some(&mut cache), "%a.a", "aaaa")); - assert!(exec_like(Some(&mut cache), "a.a%", "aaaa")); + assert!(!exec_like(Some(&mut cache), "%a.a", "aaaa")); + assert!(!exec_like(Some(&mut cache), "a.a%", "aaaa")); assert!(!exec_like(Some(&mut cache), "%a.ab", "aaaa")); } From 12e49da1d0b9f66c6353f2141f071d6e41c61f51 Mon Sep 17 00:00:00 2001 From: psvri Date: Thu, 26 Dec 2024 22:42:46 +0530 Subject: [PATCH 2/3] fmt fixes --- core/vdbe/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index f75df4a5f..e9df3a139 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -3166,7 +3166,6 @@ fn exec_char(values: Vec) -> OwnedValue { } fn construct_like_regex(pattern: &str) -> Regex { - let mut regex_pattern = String::with_capacity(pattern.len() * 2); regex_pattern.push('^'); From 5470ea2344424f3b64ce5ef440a894cb1c678948 Mon Sep 17 00:00:00 2001 From: psvri Date: Fri, 27 Dec 2024 21:49:26 +0530 Subject: [PATCH 3/3] Add tests in like.test --- testing/like.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/testing/like.test b/testing/like.test index edd6ba5e5..a52b90b60 100755 --- a/testing/like.test +++ b/testing/like.test @@ -77,3 +77,15 @@ Robert|Roberts} do_execsql_test where-like-impossible { select * from products where 'foobar' like 'fooba'; } {} + +do_execsql_test like-with-backslash { + select like('\%A', '\A') +} {1} + +do_execsql_test like-with-dollar { + select like('A$%', 'A$') +} {1} + +do_execsql_test like-with-dot { + select like('%a.a', 'aaaa') +} {0}