1use std::sync::Arc;
24
25use memchr::memmem;
26use radixdb_core::CompactArc;
27
28use radixdb_core::{DataType, Error, Result, Value, ValueSet};
29use radixdb_functions::{NativeFn1, ScalarFunction};
30
31#[derive(Debug, Clone)]
33pub enum CompiledPattern {
34 Exact(String),
36 Prefix(String),
38 Suffix(String),
40 Contains(String),
42 PrefixSuffix(String, String),
44 Regex(regex::Regex),
46 MatchAll,
48 SingleChar,
50}
51
52impl CompiledPattern {
53 pub fn compile(pattern: &str, case_insensitive: bool) -> Result<Self> {
55 let pat = if case_insensitive {
56 pattern.to_lowercase()
57 } else {
58 pattern.to_string()
59 };
60
61 let has_percent = pat.contains('%');
63 let has_underscore = pat.contains('_');
64 let has_escape = pat.contains('\\');
65
66 if !has_percent && !has_underscore && !has_escape {
67 return Ok(CompiledPattern::Exact(pat));
68 }
69
70 if !has_escape {
74 if pat == "%" {
75 return Ok(CompiledPattern::MatchAll);
76 }
77
78 if pat == "_" {
79 return Ok(CompiledPattern::SingleChar);
80 }
81
82 if pat.ends_with('%') && !pat[..pat.len() - 1].contains('%') && !has_underscore {
84 return Ok(CompiledPattern::Prefix(pat[..pat.len() - 1].to_string()));
85 }
86
87 if pat.starts_with('%') && !pat[1..].contains('%') && !has_underscore {
89 return Ok(CompiledPattern::Suffix(pat[1..].to_string()));
90 }
91
92 if pat.starts_with('%') && pat.ends_with('%') && pat.len() > 2 {
94 let middle = &pat[1..pat.len() - 1];
95 if !middle.contains('%') && !middle.contains('_') {
96 return Ok(CompiledPattern::Contains(middle.to_string()));
97 }
98 }
99
100 if has_percent && !has_underscore {
102 let parts: Vec<&str> = pat.split('%').collect();
103 if parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() {
104 return Ok(CompiledPattern::PrefixSuffix(
105 parts[0].to_string(),
106 parts[1].to_string(),
107 ));
108 }
109 }
110 }
111
112 let regex_pattern = Self::like_to_regex(&pat);
114 let regex = if case_insensitive {
115 regex::Regex::new(&format!("(?i)^{}$", regex_pattern))
116 } else {
117 regex::Regex::new(&format!("^{}$", regex_pattern))
118 };
119
120 regex
121 .map(CompiledPattern::Regex)
122 .map_err(|error| Error::invalid_argument(format!("invalid LIKE pattern: {error}")))
123 }
124
125 pub fn compile_glob(pattern: &str) -> Result<Self> {
128 let pat = pattern.to_string();
129
130 let has_star = pat.contains('*');
132 let has_question = pat.contains('?');
133
134 if !has_star && !has_question {
135 return Ok(CompiledPattern::Exact(pat));
136 }
137
138 if pat == "*" {
139 return Ok(CompiledPattern::MatchAll);
140 }
141
142 if pat == "?" {
143 return Ok(CompiledPattern::SingleChar);
144 }
145
146 if pat.ends_with('*') && !pat[..pat.len() - 1].contains('*') && !has_question {
148 return Ok(CompiledPattern::Prefix(pat[..pat.len() - 1].to_string()));
149 }
150
151 if pat.starts_with('*') && !pat[1..].contains('*') && !has_question {
153 return Ok(CompiledPattern::Suffix(pat[1..].to_string()));
154 }
155
156 if pat.starts_with('*') && pat.ends_with('*') && pat.len() > 2 {
158 let middle = &pat[1..pat.len() - 1];
159 if !middle.contains('*') && !middle.contains('?') {
160 return Ok(CompiledPattern::Contains(middle.to_string()));
161 }
162 }
163
164 if has_star && !has_question {
166 let parts: Vec<&str> = pat.split('*').collect();
167 if parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() {
168 return Ok(CompiledPattern::PrefixSuffix(
169 parts[0].to_string(),
170 parts[1].to_string(),
171 ));
172 }
173 }
174
175 let regex_pattern = Self::glob_to_regex(&pat);
177 let regex = regex::Regex::new(&format!("^{}$", regex_pattern));
178
179 regex
180 .map(CompiledPattern::Regex)
181 .map_err(|error| Error::invalid_argument(format!("invalid GLOB pattern: {error}")))
182 }
183
184 fn glob_to_regex(pattern: &str) -> String {
186 let mut result = String::with_capacity(pattern.len() * 2);
187 let mut chars = pattern.chars().peekable();
188
189 while let Some(c) = chars.next() {
190 match c {
191 '*' => result.push_str(".*"),
192 '?' => result.push('.'),
193 '\\' => {
194 if let Some(&next) = chars.peek() {
196 if next == '*' || next == '?' || next == '\\' {
197 result.push_str(®ex::escape(&next.to_string()));
198 chars.next();
199 } else {
200 result.push_str(®ex::escape("\\"));
201 }
202 }
203 }
204 '[' => {
205 result.push('[');
207 for c in chars.by_ref() {
208 if c == ']' {
209 result.push(']');
210 break;
211 }
212 result.push(c);
213 }
214 }
215 _ => result.push_str(®ex::escape(&c.to_string())),
216 }
217 }
218
219 result
220 }
221
222 fn like_to_regex(pattern: &str) -> String {
224 let mut result = String::with_capacity(pattern.len() * 2);
225 let mut chars = pattern.chars().peekable();
226
227 while let Some(c) = chars.next() {
228 match c {
229 '%' => result.push_str(".*"),
230 '_' => result.push('.'),
231 '\\' => {
232 if let Some(&next) = chars.peek() {
234 if next == '%' || next == '_' || next == '\\' {
235 chars.next();
236 result.push_str(®ex::escape(&next.to_string()));
237 } else {
238 result.push_str(®ex::escape("\\"));
239 }
240 } else {
241 result.push_str(®ex::escape("\\"));
243 }
244 }
245 _ => result.push_str(®ex::escape(&c.to_string())),
246 }
247 }
248
249 result
250 }
251
252 #[inline]
254 pub fn matches(&self, text: &str, case_insensitive: bool) -> bool {
255 if !case_insensitive {
257 return match self {
258 CompiledPattern::Exact(p) => text == p,
259 CompiledPattern::Prefix(p) => text.starts_with(p),
260 CompiledPattern::Suffix(p) => text.ends_with(p),
261 CompiledPattern::Contains(p) => {
263 memmem::find(text.as_bytes(), p.as_bytes()).is_some()
264 }
265 CompiledPattern::PrefixSuffix(prefix, suffix) => {
266 text.starts_with(prefix)
267 && text.ends_with(suffix)
268 && text.len() >= prefix.len() + suffix.len()
269 }
270 CompiledPattern::Regex(re) => re.is_match(text),
271 CompiledPattern::MatchAll => true,
272 CompiledPattern::SingleChar => text.chars().count() == 1,
273 };
274 }
275
276 match self {
280 CompiledPattern::Exact(p) => {
281 if text.is_ascii() && p.is_ascii() {
282 text.eq_ignore_ascii_case(p)
283 } else {
284 text.to_lowercase() == *p
286 }
287 }
288 CompiledPattern::Prefix(p) => {
289 if text.len() < p.len() {
290 return false;
291 }
292 if text.is_ascii() && p.is_ascii() {
293 text[..p.len()].eq_ignore_ascii_case(p)
294 } else {
295 text.to_lowercase().starts_with(p)
297 }
298 }
299 CompiledPattern::Suffix(p) => {
300 if text.len() < p.len() {
301 return false;
302 }
303 if text.is_ascii() && p.is_ascii() {
304 text[text.len() - p.len()..].eq_ignore_ascii_case(p)
305 } else {
306 text.to_lowercase().ends_with(p)
308 }
309 }
310 CompiledPattern::Contains(p) => {
311 if p.len() > text.len() {
312 return false;
313 }
314 if text.is_ascii() && p.is_ascii() {
315 text.as_bytes()
317 .windows(p.len())
318 .any(|window| window.eq_ignore_ascii_case(p.as_bytes()))
319 } else {
320 text.to_lowercase().contains(p)
322 }
323 }
324 CompiledPattern::PrefixSuffix(prefix, suffix) => {
325 if text.len() < prefix.len() + suffix.len() {
326 return false;
327 }
328 if text.is_ascii() && prefix.is_ascii() && suffix.is_ascii() {
329 text[..prefix.len()].eq_ignore_ascii_case(prefix)
330 && text[text.len() - suffix.len()..].eq_ignore_ascii_case(suffix)
331 } else {
332 let text_lower = text.to_lowercase();
334 text_lower.starts_with(prefix) && text_lower.ends_with(suffix)
335 }
336 }
337 CompiledPattern::Regex(re) => re.is_match(text),
339 CompiledPattern::MatchAll => true,
340 CompiledPattern::SingleChar => text.chars().count() == 1,
341 }
342 }
343}
344
345#[derive(Clone)]
350pub enum Op {
351 LoadColumn(u16),
357
358 LoadColumn2(u16),
361
362 LoadOuterColumn(CompactArc<str>),
366
367 LoadConst(Value),
370
371 LoadParam(u16),
374
375 LoadNamedParam(CompactArc<str>),
378
379 LoadNull(DataType),
382
383 Eq,
388 Ne,
390 Lt,
392 Le,
394 Gt,
396 Ge,
398
399 IsNull,
402
403 IsNotNull,
406
407 IsDistinctFrom,
410
411 IsNotDistinctFrom,
414
415 EqColumnConst(u16, Value),
422
423 NeColumnConst(u16, Value),
426
427 LtColumnConst(u16, Value),
430
431 LeColumnConst(u16, Value),
434
435 GtColumnConst(u16, Value),
438
439 GeColumnConst(u16, Value),
442
443 IsNullColumn(u16),
446
447 IsNotNullColumn(u16),
450
451 LikeColumn(u16, Arc<CompiledPattern>, bool), InSetColumn(u16, CompactArc<ValueSet>, bool), BetweenColumnConst(u16, Value, Value), And(u16), Or(u16), Not,
479
480 Xor,
483
484 AndFinalize,
487
488 OrFinalize,
491
492 Add,
496 Sub,
497 Mul,
498 Div,
499 Mod,
500
501 Neg,
504
505 BitAnd,
509 BitOr,
510 BitXor,
511 BitNot,
512 Shl,
513 Shr,
514
515 Concat,
521
522 ConcatN(u8),
526
527 Like(Arc<CompiledPattern>, bool), Glob(Arc<CompiledPattern>),
534
535 Regexp(Arc<regex::Regex>),
538
539 LikeEscape(Arc<CompiledPattern>, bool, char), LikeDynamic(bool), LikeDynamicEscape(bool, char), GlobDynamic,
554
555 RegexpDynamic,
558
559 JsonAccess,
565
566 JsonAccessText,
569
570 TimestampAddInterval,
576
577 TimestampSubInterval,
580
581 TimestampDiff,
584
585 TimestampAddDays,
588
589 TimestampSubDays,
592
593 VectorDistanceL2,
599
600 VectorDistanceCosine,
603
604 VectorDistanceIP,
607
608 InSet(CompactArc<ValueSet>, bool), NotInSet(CompactArc<ValueSet>, bool), Between,
622
623 NotBetween,
626
627 InTupleSet {
631 tuple_size: u8,
632 values: Arc<Vec<Vec<Value>>>, negated: bool,
634 },
635
636 IsTrue,
642
643 IsNotTrue,
646
647 IsFalse,
650
651 IsNotFalse,
654
655 CallScalar {
661 func: Arc<dyn ScalarFunction>,
662 arg_count: u8,
663 },
664
665 CallStored {
668 name: CompactArc<str>,
669 arg_count: u8,
670 },
671
672 Coalesce(u8), NullIf,
679
680 Greatest(u8),
683
684 Least(u8),
687
688 NativeFn1(NativeFn1),
694
695 Cast(DataType),
701
702 CastExternal(CompactArc<str>),
706
707 TruncateToDate,
711
712 CaseStart,
717
718 CaseWhen(u16), CaseThen(u16), CaseElse,
728
729 CaseEnd,
731
732 CaseCompare,
735
736 Jump(u16),
741
742 JumpIfTrue(u16),
744
745 JumpIfFalse(u16),
747
748 JumpIfNull(u16),
750
751 JumpIfNotNull(u16),
754
755 PopJumpIfTrue(u16),
757
758 PopJumpIfFalse(u16),
760
761 Dup,
763
764 Pop,
766
767 Swap,
769
770 LoadAggregateResult(u16),
777
778 LoadTransactionId,
782
783 Nop,
788
789 Return,
791
792 ReturnTrue,
794
795 ReturnFalse,
797
798 ReturnNull(DataType),
800}
801
802impl std::fmt::Debug for Op {
804 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805 match self {
806 Op::LoadColumn(idx) => write!(f, "LoadColumn({})", idx),
807 Op::LoadColumn2(idx) => write!(f, "LoadColumn2({})", idx),
808 Op::LoadOuterColumn(name) => write!(f, "LoadOuterColumn({})", name),
809 Op::LoadConst(v) => write!(f, "LoadConst({:?})", v),
810 Op::LoadParam(idx) => write!(f, "LoadParam({})", idx),
811 Op::LoadNamedParam(name) => write!(f, "LoadNamedParam({})", name),
812 Op::LoadNull(dt) => write!(f, "LoadNull({:?})", dt),
813 Op::Eq => write!(f, "Eq"),
814 Op::Ne => write!(f, "Ne"),
815 Op::Lt => write!(f, "Lt"),
816 Op::Le => write!(f, "Le"),
817 Op::Gt => write!(f, "Gt"),
818 Op::Ge => write!(f, "Ge"),
819 Op::IsNull => write!(f, "IsNull"),
820 Op::IsNotNull => write!(f, "IsNotNull"),
821 Op::IsDistinctFrom => write!(f, "IsDistinctFrom"),
822 Op::IsNotDistinctFrom => write!(f, "IsNotDistinctFrom"),
823 Op::EqColumnConst(col, val) => write!(f, "EqColumnConst({}, {:?})", col, val),
824 Op::NeColumnConst(col, val) => write!(f, "NeColumnConst({}, {:?})", col, val),
825 Op::LtColumnConst(col, val) => write!(f, "LtColumnConst({}, {:?})", col, val),
826 Op::LeColumnConst(col, val) => write!(f, "LeColumnConst({}, {:?})", col, val),
827 Op::GtColumnConst(col, val) => write!(f, "GtColumnConst({}, {:?})", col, val),
828 Op::GeColumnConst(col, val) => write!(f, "GeColumnConst({}, {:?})", col, val),
829 Op::IsNullColumn(col) => write!(f, "IsNullColumn({})", col),
830 Op::IsNotNullColumn(col) => write!(f, "IsNotNullColumn({})", col),
831 Op::LikeColumn(col, _, ci) => write!(f, "LikeColumn({}, case_insensitive={})", col, ci),
832 Op::InSetColumn(col, set, has_null) => {
833 write!(
834 f,
835 "InSetColumn({}, len={}, has_null={})",
836 col,
837 set.len(),
838 has_null
839 )
840 }
841 Op::BetweenColumnConst(col, low, high) => {
842 write!(f, "BetweenColumnConst({}, {:?}, {:?})", col, low, high)
843 }
844 Op::And(target) => write!(f, "And(jump={})", target),
845 Op::Or(target) => write!(f, "Or(jump={})", target),
846 Op::Not => write!(f, "Not"),
847 Op::Xor => write!(f, "Xor"),
848 Op::AndFinalize => write!(f, "AndFinalize"),
849 Op::OrFinalize => write!(f, "OrFinalize"),
850 Op::Add => write!(f, "Add"),
851 Op::Sub => write!(f, "Sub"),
852 Op::Mul => write!(f, "Mul"),
853 Op::Div => write!(f, "Div"),
854 Op::Mod => write!(f, "Mod"),
855 Op::Neg => write!(f, "Neg"),
856 Op::BitAnd => write!(f, "BitAnd"),
857 Op::BitOr => write!(f, "BitOr"),
858 Op::BitXor => write!(f, "BitXor"),
859 Op::BitNot => write!(f, "BitNot"),
860 Op::Shl => write!(f, "Shl"),
861 Op::Shr => write!(f, "Shr"),
862 Op::Concat => write!(f, "Concat"),
863 Op::Like(_, ci) => write!(f, "Like(case_insensitive={})", ci),
864 Op::Glob(_) => write!(f, "Glob"),
865 Op::Regexp(_) => write!(f, "Regexp"),
866 Op::LikeEscape(_, ci, esc) => {
867 write!(f, "LikeEscape(case_insensitive={}, escape='{}')", ci, esc)
868 }
869 Op::LikeDynamic(ci) => write!(f, "LikeDynamic(case_insensitive={})", ci),
870 Op::LikeDynamicEscape(ci, esc) => {
871 write!(
872 f,
873 "LikeDynamicEscape(case_insensitive={}, escape='{}')",
874 ci, esc
875 )
876 }
877 Op::GlobDynamic => write!(f, "GlobDynamic"),
878 Op::RegexpDynamic => write!(f, "RegexpDynamic"),
879 Op::JsonAccess => write!(f, "JsonAccess"),
880 Op::JsonAccessText => write!(f, "JsonAccessText"),
881 Op::TimestampAddInterval => write!(f, "TimestampAddInterval"),
882 Op::TimestampSubInterval => write!(f, "TimestampSubInterval"),
883 Op::TimestampDiff => write!(f, "TimestampDiff"),
884 Op::TimestampAddDays => write!(f, "TimestampAddDays"),
885 Op::TimestampSubDays => write!(f, "TimestampSubDays"),
886 Op::VectorDistanceL2 => write!(f, "VectorDistanceL2"),
887 Op::VectorDistanceCosine => write!(f, "VectorDistanceCosine"),
888 Op::VectorDistanceIP => write!(f, "VectorDistanceIP"),
889 Op::InSet(set, has_null) => {
890 write!(f, "InSet(len={}, has_null={})", set.len(), has_null)
891 }
892 Op::NotInSet(set, has_null) => {
893 write!(f, "NotInSet(len={}, has_null={})", set.len(), has_null)
894 }
895 Op::Between => write!(f, "Between"),
896 Op::NotBetween => write!(f, "NotBetween"),
897 Op::InTupleSet {
898 tuple_size,
899 values,
900 negated,
901 } => {
902 write!(
903 f,
904 "InTupleSet(size={}, tuples={}, negated={})",
905 tuple_size,
906 values.len(),
907 negated
908 )
909 }
910 Op::IsTrue => write!(f, "IsTrue"),
911 Op::IsNotTrue => write!(f, "IsNotTrue"),
912 Op::IsFalse => write!(f, "IsFalse"),
913 Op::IsNotFalse => write!(f, "IsNotFalse"),
914 Op::CallScalar { arg_count, .. } => write!(f, "CallScalar(args={})", arg_count),
915 Op::CallStored { name, arg_count } => {
916 write!(f, "CallStored({name}, args={arg_count})")
917 }
918 Op::Coalesce(n) => write!(f, "Coalesce({})", n),
919 Op::NullIf => write!(f, "NullIf"),
920 Op::Greatest(n) => write!(f, "Greatest({})", n),
921 Op::Least(n) => write!(f, "Least({})", n),
922 Op::Cast(dt) => write!(f, "Cast({:?})", dt),
923 Op::CastExternal(name) => write!(f, "CastExternal({name})"),
924 Op::TruncateToDate => write!(f, "TruncateToDate"),
925 Op::CaseStart => write!(f, "CaseStart"),
926 Op::CaseWhen(target) => write!(f, "CaseWhen(jump={})", target),
927 Op::CaseThen(target) => write!(f, "CaseThen(jump={})", target),
928 Op::CaseElse => write!(f, "CaseElse"),
929 Op::CaseEnd => write!(f, "CaseEnd"),
930 Op::CaseCompare => write!(f, "CaseCompare"),
931 Op::Jump(target) => write!(f, "Jump({})", target),
932 Op::JumpIfTrue(target) => write!(f, "JumpIfTrue({})", target),
933 Op::JumpIfFalse(target) => write!(f, "JumpIfFalse({})", target),
934 Op::JumpIfNull(target) => write!(f, "JumpIfNull({})", target),
935 Op::JumpIfNotNull(target) => write!(f, "JumpIfNotNull({})", target),
936 Op::PopJumpIfTrue(target) => write!(f, "PopJumpIfTrue({})", target),
937 Op::PopJumpIfFalse(target) => write!(f, "PopJumpIfFalse({})", target),
938 Op::Dup => write!(f, "Dup"),
939 Op::Pop => write!(f, "Pop"),
940 Op::Swap => write!(f, "Swap"),
941 Op::LoadAggregateResult(idx) => write!(f, "LoadAggregateResult({})", idx),
942 Op::LoadTransactionId => write!(f, "LoadTransactionId"),
943 Op::Nop => write!(f, "Nop"),
944 Op::Return => write!(f, "Return"),
945 Op::ReturnTrue => write!(f, "ReturnTrue"),
946 Op::ReturnFalse => write!(f, "ReturnFalse"),
947 Op::ReturnNull(dt) => write!(f, "ReturnNull({:?})", dt),
948 Op::NativeFn1(_) => write!(f, "NativeFn1(...)"),
949 Op::ConcatN(n) => write!(f, "ConcatN({})", n),
950 }
951 }
952}
953
954#[cfg(test)]
955mod tests {
956 use super::*;
957 use std::mem;
958
959 #[test]
960 fn test_op_size() {
961 let size = mem::size_of::<Op>();
962 println!("Op enum size: {} bytes", size);
963 println!("Op alignment: {} bytes", mem::align_of::<Op>());
964
965 assert!(size <= 64, "Op enum is too large: {} bytes", size);
973 }
974
975 #[test]
980 fn test_pattern_exact() {
981 let pattern = CompiledPattern::compile("hello", false).unwrap();
982 assert!(matches!(pattern, CompiledPattern::Exact(ref s) if s == "hello"));
983 assert!(pattern.matches("hello", false));
984 assert!(!pattern.matches("Hello", false));
985 assert!(!pattern.matches("hello world", false));
986 }
987
988 #[test]
989 fn test_pattern_exact_case_insensitive() {
990 let pattern = CompiledPattern::compile("Hello", true).unwrap();
991 assert!(matches!(pattern, CompiledPattern::Exact(ref s) if s == "hello"));
992 assert!(pattern.matches("hello", true));
993 assert!(pattern.matches("HELLO", true));
994 assert!(pattern.matches("HeLLo", true));
995 }
996
997 #[test]
998 fn test_pattern_match_all() {
999 let pattern = CompiledPattern::compile("%", false).unwrap();
1000 assert!(matches!(pattern, CompiledPattern::MatchAll));
1001 assert!(pattern.matches("", false));
1002 assert!(pattern.matches("anything", false));
1003 assert!(pattern.matches("with spaces and 123", false));
1004 }
1005
1006 #[test]
1007 fn test_pattern_single_char() {
1008 let pattern = CompiledPattern::compile("_", false).unwrap();
1009 assert!(matches!(pattern, CompiledPattern::SingleChar));
1010 assert!(pattern.matches("a", false));
1011 assert!(pattern.matches("Z", false));
1012 assert!(!pattern.matches("", false));
1013 assert!(!pattern.matches("ab", false));
1014 }
1015
1016 #[test]
1017 fn test_pattern_prefix() {
1018 let pattern = CompiledPattern::compile("hello%", false).unwrap();
1019 assert!(matches!(pattern, CompiledPattern::Prefix(ref s) if s == "hello"));
1020 assert!(pattern.matches("hello", false));
1021 assert!(pattern.matches("hello world", false));
1022 assert!(pattern.matches("hellooooo", false));
1023 assert!(!pattern.matches("Hello", false));
1024 assert!(!pattern.matches("say hello", false));
1025 }
1026
1027 #[test]
1028 fn test_pattern_prefix_case_insensitive() {
1029 let pattern = CompiledPattern::compile("Hello%", true).unwrap();
1030 assert!(pattern.matches("hello world", true));
1031 assert!(pattern.matches("HELLO WORLD", true));
1032 assert!(!pattern.matches("say hello", true));
1033 }
1034
1035 #[test]
1036 fn test_pattern_suffix() {
1037 let pattern = CompiledPattern::compile("%world", false).unwrap();
1038 assert!(matches!(pattern, CompiledPattern::Suffix(ref s) if s == "world"));
1039 assert!(pattern.matches("world", false));
1040 assert!(pattern.matches("hello world", false));
1041 assert!(!pattern.matches("World", false));
1042 assert!(!pattern.matches("world!", false));
1043 }
1044
1045 #[test]
1046 fn test_pattern_suffix_case_insensitive() {
1047 let pattern = CompiledPattern::compile("%World", true).unwrap();
1048 assert!(pattern.matches("hello world", true));
1049 assert!(pattern.matches("HELLO WORLD", true));
1050 assert!(!pattern.matches("world!", true));
1051 }
1052
1053 #[test]
1054 fn test_pattern_contains() {
1055 let pattern = CompiledPattern::compile("%ello%", false).unwrap();
1056 assert!(matches!(pattern, CompiledPattern::Contains(ref s) if s == "ello"));
1057 assert!(pattern.matches("hello", false));
1058 assert!(pattern.matches("yellow", false));
1059 assert!(pattern.matches("hello world", false));
1060 assert!(!pattern.matches("HELLO", false));
1061 }
1062
1063 #[test]
1064 fn test_pattern_contains_case_insensitive() {
1065 let pattern = CompiledPattern::compile("%ELLO%", true).unwrap();
1066 assert!(pattern.matches("hello", true));
1067 assert!(pattern.matches("YELLOW", true));
1068 assert!(!pattern.matches("hi", true));
1069 }
1070
1071 #[test]
1072 fn test_pattern_prefix_suffix() {
1073 let pattern = CompiledPattern::compile("hello%world", false).unwrap();
1074 assert!(
1075 matches!(pattern, CompiledPattern::PrefixSuffix(ref p, ref s) if p == "hello" && s == "world")
1076 );
1077 assert!(pattern.matches("helloworld", false));
1078 assert!(pattern.matches("hello world", false));
1079 assert!(pattern.matches("hello beautiful world", false));
1080 assert!(!pattern.matches("hello", false));
1081 assert!(!pattern.matches("world", false));
1082 }
1083
1084 #[test]
1085 fn test_pattern_prefix_suffix_case_insensitive() {
1086 let pattern = CompiledPattern::compile("Hello%World", true).unwrap();
1087 assert!(pattern.matches("helloworld", true));
1088 assert!(pattern.matches("HELLO WORLD", true));
1089 assert!(!pattern.matches("hello", true));
1090 }
1091
1092 #[test]
1093 fn test_pattern_prefix_suffix_too_short() {
1094 let pattern = CompiledPattern::compile("abc%xyz", false).unwrap();
1095 assert!(!pattern.matches("abcxy", false)); assert!(pattern.matches("abcxyz", false)); assert!(pattern.matches("abc123xyz", false));
1099 }
1100
1101 #[test]
1102 fn test_pattern_complex_regex() {
1103 let pattern = CompiledPattern::compile("a%b%c", false).unwrap();
1105 assert!(matches!(pattern, CompiledPattern::Regex(_)));
1106 assert!(pattern.matches("abc", false));
1107 assert!(pattern.matches("aXbYc", false));
1108 assert!(pattern.matches("aXXXbYYYc", false));
1109 assert!(!pattern.matches("ac", false));
1110 }
1111
1112 #[test]
1113 fn test_pattern_underscore_regex() {
1114 let pattern = CompiledPattern::compile("a_c", false).unwrap();
1115 assert!(matches!(pattern, CompiledPattern::Regex(_)));
1116 assert!(pattern.matches("abc", false));
1117 assert!(pattern.matches("aXc", false));
1118 assert!(!pattern.matches("ac", false));
1119 assert!(!pattern.matches("abbc", false));
1120 }
1121
1122 #[test]
1123 fn test_pattern_mixed_wildcards() {
1124 let pattern = CompiledPattern::compile("a_%b", false).unwrap();
1125 assert!(matches!(pattern, CompiledPattern::Regex(_)));
1126 assert!(pattern.matches("aXb", false));
1127 assert!(pattern.matches("aXYZb", false));
1128 assert!(!pattern.matches("ab", false));
1129 }
1130
1131 #[test]
1136 fn test_glob_exact() {
1137 let pattern = CompiledPattern::compile_glob("hello").unwrap();
1138 assert!(matches!(pattern, CompiledPattern::Exact(ref s) if s == "hello"));
1139 assert!(pattern.matches("hello", false));
1140 assert!(!pattern.matches("Hello", false));
1141 }
1142
1143 #[test]
1144 fn test_glob_match_all() {
1145 let pattern = CompiledPattern::compile_glob("*").unwrap();
1146 assert!(matches!(pattern, CompiledPattern::MatchAll));
1147 assert!(pattern.matches("anything", false));
1148 }
1149
1150 #[test]
1151 fn test_glob_single_char() {
1152 let pattern = CompiledPattern::compile_glob("?").unwrap();
1153 assert!(matches!(pattern, CompiledPattern::SingleChar));
1154 assert!(pattern.matches("a", false));
1155 assert!(!pattern.matches("ab", false));
1156 }
1157
1158 #[test]
1159 fn test_glob_prefix() {
1160 let pattern = CompiledPattern::compile_glob("hello*").unwrap();
1161 assert!(matches!(pattern, CompiledPattern::Prefix(ref s) if s == "hello"));
1162 assert!(pattern.matches("hello", false));
1163 assert!(pattern.matches("hello world", false));
1164 }
1165
1166 #[test]
1167 fn test_glob_suffix() {
1168 let pattern = CompiledPattern::compile_glob("*world").unwrap();
1169 assert!(matches!(pattern, CompiledPattern::Suffix(ref s) if s == "world"));
1170 assert!(pattern.matches("world", false));
1171 assert!(pattern.matches("hello world", false));
1172 }
1173
1174 #[test]
1175 fn test_glob_contains() {
1176 let pattern = CompiledPattern::compile_glob("*ello*").unwrap();
1177 assert!(matches!(pattern, CompiledPattern::Contains(ref s) if s == "ello"));
1178 assert!(pattern.matches("hello", false));
1179 assert!(pattern.matches("yellow", false));
1180 }
1181
1182 #[test]
1183 fn test_glob_prefix_suffix() {
1184 let pattern = CompiledPattern::compile_glob("hello*world").unwrap();
1185 assert!(
1186 matches!(pattern, CompiledPattern::PrefixSuffix(ref p, ref s) if p == "hello" && s == "world")
1187 );
1188 assert!(pattern.matches("helloworld", false));
1189 assert!(pattern.matches("hello beautiful world", false));
1190 }
1191
1192 #[test]
1193 fn test_glob_complex_regex() {
1194 let pattern = CompiledPattern::compile_glob("a*b*c").unwrap();
1195 assert!(matches!(pattern, CompiledPattern::Regex(_)));
1196 assert!(pattern.matches("abc", false));
1197 assert!(pattern.matches("aXbYc", false));
1198 }
1199
1200 #[test]
1201 fn test_glob_question_mark() {
1202 let pattern = CompiledPattern::compile_glob("a?c").unwrap();
1203 assert!(matches!(pattern, CompiledPattern::Regex(_)));
1204 assert!(pattern.matches("abc", false));
1205 assert!(!pattern.matches("ac", false));
1206 }
1207
1208 #[test]
1209 fn test_glob_character_class() {
1210 let pattern = CompiledPattern::compile_glob("a[bc]d").unwrap();
1212 assert!(matches!(pattern, CompiledPattern::Exact(_)));
1213
1214 let pattern2 = CompiledPattern::compile_glob("a[bc]*").unwrap();
1216 assert!(matches!(pattern2, CompiledPattern::Prefix(ref s) if s == "a[bc]"));
1217
1218 let pattern3 = CompiledPattern::compile_glob("*[bc]*").unwrap();
1220 assert!(matches!(pattern3, CompiledPattern::Contains(ref s) if s == "[bc]"));
1221 }
1222
1223 #[test]
1224 fn test_glob_escape_edge_cases() {
1225 let pattern1 = CompiledPattern::compile_glob("a\\*b").unwrap();
1227 assert!(matches!(pattern1, CompiledPattern::PrefixSuffix(_, _)));
1228
1229 let pattern2 = CompiledPattern::compile_glob("*a*b*").unwrap();
1231 assert!(matches!(pattern2, CompiledPattern::Regex(_)));
1232 assert!(pattern2.matches("XaYbZ", false));
1233 assert!(pattern2.matches("ab", false));
1234 }
1235
1236 #[test]
1237 fn invalid_regex_compilation_is_an_error_not_literal_fallback() {
1238 assert!(CompiledPattern::compile_glob("[?").is_err());
1239 }
1240
1241 #[test]
1246 fn test_op_debug_format() {
1247 assert_eq!(format!("{:?}", Op::LoadColumn(5)), "LoadColumn(5)");
1249 assert_eq!(format!("{:?}", Op::LoadColumn2(3)), "LoadColumn2(3)");
1250 assert_eq!(format!("{:?}", Op::Eq), "Eq");
1251 assert_eq!(format!("{:?}", Op::Ne), "Ne");
1252 assert_eq!(format!("{:?}", Op::Lt), "Lt");
1253 assert_eq!(format!("{:?}", Op::Le), "Le");
1254 assert_eq!(format!("{:?}", Op::Gt), "Gt");
1255 assert_eq!(format!("{:?}", Op::Ge), "Ge");
1256 assert_eq!(format!("{:?}", Op::Add), "Add");
1257 assert_eq!(format!("{:?}", Op::Sub), "Sub");
1258 assert_eq!(format!("{:?}", Op::Mul), "Mul");
1259 assert_eq!(format!("{:?}", Op::Div), "Div");
1260 assert_eq!(format!("{:?}", Op::Mod), "Mod");
1261 assert_eq!(format!("{:?}", Op::Not), "Not");
1262 assert_eq!(format!("{:?}", Op::Neg), "Neg");
1263 assert_eq!(format!("{:?}", Op::IsNull), "IsNull");
1264 assert_eq!(format!("{:?}", Op::IsNotNull), "IsNotNull");
1265 assert_eq!(format!("{:?}", Op::Return), "Return");
1266 assert_eq!(format!("{:?}", Op::ReturnTrue), "ReturnTrue");
1267 assert_eq!(format!("{:?}", Op::ReturnFalse), "ReturnFalse");
1268 assert_eq!(format!("{:?}", Op::Nop), "Nop");
1269 assert_eq!(format!("{:?}", Op::Dup), "Dup");
1270 assert_eq!(format!("{:?}", Op::Pop), "Pop");
1271 assert_eq!(format!("{:?}", Op::Swap), "Swap");
1272 }
1273
1274 #[test]
1275 fn test_op_debug_format_with_values() {
1276 assert_eq!(
1277 format!("{:?}", Op::LoadConst(Value::Integer(42))),
1278 "LoadConst(Integer(42))"
1279 );
1280 assert_eq!(
1281 format!("{:?}", Op::EqColumnConst(0, Value::Integer(10))),
1282 "EqColumnConst(0, Integer(10))"
1283 );
1284 assert_eq!(format!("{:?}", Op::And(5)), "And(jump=5)");
1285 assert_eq!(format!("{:?}", Op::Or(10)), "Or(jump=10)");
1286 assert_eq!(format!("{:?}", Op::Jump(15)), "Jump(15)");
1287 assert_eq!(format!("{:?}", Op::JumpIfTrue(20)), "JumpIfTrue(20)");
1288 assert_eq!(format!("{:?}", Op::JumpIfFalse(25)), "JumpIfFalse(25)");
1289 }
1290
1291 #[test]
1292 fn test_op_debug_format_special() {
1293 assert_eq!(format!("{:?}", Op::Coalesce(3)), "Coalesce(3)");
1294 assert_eq!(format!("{:?}", Op::NullIf), "NullIf");
1295 assert_eq!(format!("{:?}", Op::Greatest(2)), "Greatest(2)");
1296 assert_eq!(format!("{:?}", Op::Least(4)), "Least(4)");
1297 assert_eq!(format!("{:?}", Op::Between), "Between");
1298 assert_eq!(format!("{:?}", Op::NotBetween), "NotBetween");
1299 assert_eq!(format!("{:?}", Op::Concat), "Concat");
1300 assert_eq!(format!("{:?}", Op::JsonAccess), "JsonAccess");
1301 assert_eq!(format!("{:?}", Op::JsonAccessText), "JsonAccessText");
1302 }
1303
1304 #[test]
1305 fn test_op_debug_bitwise() {
1306 assert_eq!(format!("{:?}", Op::BitAnd), "BitAnd");
1307 assert_eq!(format!("{:?}", Op::BitOr), "BitOr");
1308 assert_eq!(format!("{:?}", Op::BitXor), "BitXor");
1309 assert_eq!(format!("{:?}", Op::BitNot), "BitNot");
1310 assert_eq!(format!("{:?}", Op::Shl), "Shl");
1311 assert_eq!(format!("{:?}", Op::Shr), "Shr");
1312 }
1313
1314 #[test]
1315 fn test_op_debug_case() {
1316 assert_eq!(format!("{:?}", Op::CaseStart), "CaseStart");
1317 assert_eq!(format!("{:?}", Op::CaseWhen(5)), "CaseWhen(jump=5)");
1318 assert_eq!(format!("{:?}", Op::CaseThen(10)), "CaseThen(jump=10)");
1319 assert_eq!(format!("{:?}", Op::CaseElse), "CaseElse");
1320 assert_eq!(format!("{:?}", Op::CaseEnd), "CaseEnd");
1321 assert_eq!(format!("{:?}", Op::CaseCompare), "CaseCompare");
1322 }
1323
1324 #[test]
1325 fn test_op_debug_timestamp() {
1326 assert_eq!(
1327 format!("{:?}", Op::TimestampAddInterval),
1328 "TimestampAddInterval"
1329 );
1330 assert_eq!(
1331 format!("{:?}", Op::TimestampSubInterval),
1332 "TimestampSubInterval"
1333 );
1334 assert_eq!(format!("{:?}", Op::TimestampDiff), "TimestampDiff");
1335 assert_eq!(format!("{:?}", Op::TimestampAddDays), "TimestampAddDays");
1336 assert_eq!(format!("{:?}", Op::TimestampSubDays), "TimestampSubDays");
1337 }
1338
1339 #[test]
1340 fn test_op_debug_boolean_checks() {
1341 assert_eq!(format!("{:?}", Op::IsTrue), "IsTrue");
1342 assert_eq!(format!("{:?}", Op::IsNotTrue), "IsNotTrue");
1343 assert_eq!(format!("{:?}", Op::IsFalse), "IsFalse");
1344 assert_eq!(format!("{:?}", Op::IsNotFalse), "IsNotFalse");
1345 assert_eq!(format!("{:?}", Op::IsDistinctFrom), "IsDistinctFrom");
1346 assert_eq!(format!("{:?}", Op::IsNotDistinctFrom), "IsNotDistinctFrom");
1347 }
1348
1349 #[test]
1350 fn test_op_debug_sets() {
1351 use radixdb_core::ValueSet;
1352 let set = CompactArc::new(ValueSet::default());
1353 assert!(format!("{:?}", Op::InSet(set.clone(), false)).contains("InSet"));
1354 assert!(format!("{:?}", Op::NotInSet(set.clone(), true)).contains("NotInSet"));
1355 assert!(format!("{:?}", Op::InSetColumn(0, set, false)).contains("InSetColumn"));
1356 }
1357
1358 #[test]
1359 fn test_op_debug_like_glob() {
1360 let pattern = Arc::new(CompiledPattern::compile("test%", false).unwrap());
1361 assert!(format!("{:?}", Op::Like(pattern.clone(), false)).contains("Like"));
1362 assert!(format!("{:?}", Op::Glob(pattern.clone())).contains("Glob"));
1363 assert!(format!("{:?}", Op::LikeColumn(0, pattern, false)).contains("LikeColumn"));
1364 }
1365}