sql_cli/sql/functions/
string_methods.rs

1use anyhow::{anyhow, Result};
2
3use super::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
4use crate::data::datatable::DataValue;
5
6/// Trait for method-style functions that operate on a column/value
7/// These are called with dot notation: column.Method(args)
8pub trait MethodFunction: SqlFunction {
9    /// Check if this method function handles the given method name
10    fn handles_method(&self, method_name: &str) -> bool;
11
12    /// Get the method name this function handles
13    fn method_name(&self) -> &'static str;
14
15    /// Evaluate as a method (first arg is implicit 'self')
16    fn evaluate_method(&self, receiver: &DataValue, args: &[DataValue]) -> Result<DataValue> {
17        // Default implementation: prepend receiver to args and call evaluate
18        let mut full_args = vec![receiver.clone()];
19        full_args.extend_from_slice(args);
20        self.evaluate(&full_args)
21    }
22}
23
24/// `ToUpper` method function
25pub struct ToUpperMethod;
26
27impl SqlFunction for ToUpperMethod {
28    fn signature(&self) -> FunctionSignature {
29        FunctionSignature {
30            name: "TOUPPER",
31            category: FunctionCategory::String,
32            arg_count: ArgCount::Fixed(1),
33            description: "Converts string to uppercase",
34            returns: "STRING",
35            examples: vec![
36                "SELECT name.ToUpper() FROM users",
37                "SELECT TOUPPER(name) FROM users",
38            ],
39        }
40    }
41
42    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
43        self.validate_args(args)?;
44
45        match &args[0] {
46            DataValue::String(s) => Ok(DataValue::String(s.to_uppercase())),
47            DataValue::InternedString(s) => Ok(DataValue::String(s.to_uppercase())),
48            DataValue::Null => Ok(DataValue::Null),
49            _ => Err(anyhow!("ToUpper expects a string argument")),
50        }
51    }
52}
53
54impl MethodFunction for ToUpperMethod {
55    fn handles_method(&self, method_name: &str) -> bool {
56        method_name.eq_ignore_ascii_case("ToUpper")
57            || method_name.eq_ignore_ascii_case("ToUpperCase")
58    }
59
60    fn method_name(&self) -> &'static str {
61        "ToUpper"
62    }
63}
64
65/// `ToLower` method function
66pub struct ToLowerMethod;
67
68impl SqlFunction for ToLowerMethod {
69    fn signature(&self) -> FunctionSignature {
70        FunctionSignature {
71            name: "TOLOWER",
72            category: FunctionCategory::String,
73            arg_count: ArgCount::Fixed(1),
74            description: "Converts string to lowercase",
75            returns: "STRING",
76            examples: vec![
77                "SELECT name.ToLower() FROM users",
78                "SELECT TOLOWER(name) FROM users",
79            ],
80        }
81    }
82
83    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
84        self.validate_args(args)?;
85
86        match &args[0] {
87            DataValue::String(s) => Ok(DataValue::String(s.to_lowercase())),
88            DataValue::InternedString(s) => Ok(DataValue::String(s.to_lowercase())),
89            DataValue::Null => Ok(DataValue::Null),
90            _ => Err(anyhow!("ToLower expects a string argument")),
91        }
92    }
93}
94
95impl MethodFunction for ToLowerMethod {
96    fn handles_method(&self, method_name: &str) -> bool {
97        method_name.eq_ignore_ascii_case("ToLower")
98            || method_name.eq_ignore_ascii_case("ToLowerCase")
99    }
100
101    fn method_name(&self) -> &'static str {
102        "ToLower"
103    }
104}
105
106/// Trim method function
107pub struct TrimMethod;
108
109impl SqlFunction for TrimMethod {
110    fn signature(&self) -> FunctionSignature {
111        FunctionSignature {
112            name: "TRIM",
113            category: FunctionCategory::String,
114            arg_count: ArgCount::Fixed(1),
115            description: "Removes leading and trailing whitespace",
116            returns: "STRING",
117            examples: vec![
118                "SELECT name.Trim() FROM users",
119                "SELECT TRIM(name) FROM users",
120            ],
121        }
122    }
123
124    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
125        self.validate_args(args)?;
126
127        match &args[0] {
128            DataValue::String(s) => Ok(DataValue::String(s.trim().to_string())),
129            DataValue::InternedString(s) => Ok(DataValue::String(s.trim().to_string())),
130            DataValue::Null => Ok(DataValue::Null),
131            _ => Err(anyhow!("Trim expects a string argument")),
132        }
133    }
134}
135
136impl MethodFunction for TrimMethod {
137    fn handles_method(&self, method_name: &str) -> bool {
138        method_name.eq_ignore_ascii_case("Trim")
139    }
140
141    fn method_name(&self) -> &'static str {
142        "Trim"
143    }
144}
145
146/// Length method function (returns integer)
147pub struct LengthMethod;
148
149impl SqlFunction for LengthMethod {
150    fn signature(&self) -> FunctionSignature {
151        FunctionSignature {
152            name: "LENGTH",
153            category: FunctionCategory::String,
154            arg_count: ArgCount::Fixed(1),
155            description: "Returns the length of a string",
156            returns: "INTEGER",
157            examples: vec![
158                "SELECT name.Length() FROM users",
159                "SELECT LENGTH(name) FROM users",
160            ],
161        }
162    }
163
164    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
165        self.validate_args(args)?;
166
167        match &args[0] {
168            DataValue::String(s) => Ok(DataValue::Integer(s.len() as i64)),
169            DataValue::InternedString(s) => Ok(DataValue::Integer(s.len() as i64)),
170            DataValue::Null => Ok(DataValue::Null),
171            _ => Err(anyhow!("Length expects a string argument")),
172        }
173    }
174}
175
176impl MethodFunction for LengthMethod {
177    fn handles_method(&self, method_name: &str) -> bool {
178        method_name.eq_ignore_ascii_case("Length") || method_name.eq_ignore_ascii_case("Len")
179    }
180
181    fn method_name(&self) -> &'static str {
182        "Length"
183    }
184}
185
186/// Contains method function (returns boolean)
187pub struct ContainsMethod;
188
189impl SqlFunction for ContainsMethod {
190    fn signature(&self) -> FunctionSignature {
191        FunctionSignature {
192            name: "CONTAINS",
193            category: FunctionCategory::String,
194            arg_count: ArgCount::Fixed(2),
195            description: "Checks if string contains substring",
196            returns: "BOOLEAN",
197            examples: vec![
198                "SELECT * FROM users WHERE name.Contains('john')",
199                "SELECT CONTAINS(name, 'john') FROM users",
200            ],
201        }
202    }
203
204    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
205        self.validate_args(args)?;
206
207        let haystack = match &args[0] {
208            DataValue::String(s) => s.as_str(),
209            DataValue::InternedString(s) => s.as_str(),
210            DataValue::Null => return Ok(DataValue::Boolean(false)),
211            _ => return Err(anyhow!("Contains expects string arguments")),
212        };
213
214        let needle = match &args[1] {
215            DataValue::String(s) => s.as_str(),
216            DataValue::InternedString(s) => s.as_str(),
217            DataValue::Null => return Ok(DataValue::Boolean(false)),
218            _ => return Err(anyhow!("Contains expects string arguments")),
219        };
220
221        Ok(DataValue::Boolean(haystack.contains(needle)))
222    }
223}
224
225impl MethodFunction for ContainsMethod {
226    fn handles_method(&self, method_name: &str) -> bool {
227        method_name.eq_ignore_ascii_case("Contains")
228    }
229
230    fn method_name(&self) -> &'static str {
231        "Contains"
232    }
233}
234
235/// `StartsWith` method function
236pub struct StartsWithMethod;
237
238impl SqlFunction for StartsWithMethod {
239    fn signature(&self) -> FunctionSignature {
240        FunctionSignature {
241            name: "STARTSWITH",
242            category: FunctionCategory::String,
243            arg_count: ArgCount::Fixed(2),
244            description: "Checks if string starts with prefix",
245            returns: "BOOLEAN",
246            examples: vec![
247                "SELECT * FROM users WHERE name.StartsWith('John')",
248                "SELECT STARTSWITH(name, 'John') FROM users",
249            ],
250        }
251    }
252
253    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
254        self.validate_args(args)?;
255
256        let string = match &args[0] {
257            DataValue::String(s) => s.as_str(),
258            DataValue::InternedString(s) => s.as_str(),
259            DataValue::Null => return Ok(DataValue::Boolean(false)),
260            _ => return Err(anyhow!("StartsWith expects string arguments")),
261        };
262
263        let prefix = match &args[1] {
264            DataValue::String(s) => s.as_str(),
265            DataValue::InternedString(s) => s.as_str(),
266            DataValue::Null => return Ok(DataValue::Boolean(false)),
267            _ => return Err(anyhow!("StartsWith expects string arguments")),
268        };
269
270        Ok(DataValue::Boolean(string.starts_with(prefix)))
271    }
272}
273
274impl MethodFunction for StartsWithMethod {
275    fn handles_method(&self, method_name: &str) -> bool {
276        method_name.eq_ignore_ascii_case("StartsWith")
277    }
278
279    fn method_name(&self) -> &'static str {
280        "StartsWith"
281    }
282}
283
284/// `EndsWith` method function
285pub struct EndsWithMethod;
286
287impl SqlFunction for EndsWithMethod {
288    fn signature(&self) -> FunctionSignature {
289        FunctionSignature {
290            name: "ENDSWITH",
291            category: FunctionCategory::String,
292            arg_count: ArgCount::Fixed(2),
293            description: "Checks if string ends with suffix",
294            returns: "BOOLEAN",
295            examples: vec![
296                "SELECT * FROM users WHERE email.EndsWith('.com')",
297                "SELECT ENDSWITH(email, '.com') FROM users",
298            ],
299        }
300    }
301
302    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
303        self.validate_args(args)?;
304
305        let string = match &args[0] {
306            DataValue::String(s) => s.as_str(),
307            DataValue::InternedString(s) => s.as_str(),
308            DataValue::Null => return Ok(DataValue::Boolean(false)),
309            _ => return Err(anyhow!("EndsWith expects string arguments")),
310        };
311
312        let suffix = match &args[1] {
313            DataValue::String(s) => s.as_str(),
314            DataValue::InternedString(s) => s.as_str(),
315            DataValue::Null => return Ok(DataValue::Boolean(false)),
316            _ => return Err(anyhow!("EndsWith expects string arguments")),
317        };
318
319        Ok(DataValue::Boolean(string.ends_with(suffix)))
320    }
321}
322
323impl MethodFunction for EndsWithMethod {
324    fn handles_method(&self, method_name: &str) -> bool {
325        method_name.eq_ignore_ascii_case("EndsWith")
326    }
327
328    fn method_name(&self) -> &'static str {
329        "EndsWith"
330    }
331}
332
333/// Substring method function
334pub struct SubstringMethod;
335
336impl SqlFunction for SubstringMethod {
337    fn signature(&self) -> FunctionSignature {
338        FunctionSignature {
339            name: "SUBSTRING",
340            category: FunctionCategory::String,
341            arg_count: ArgCount::Range(2, 3),
342            description: "Extracts substring from string",
343            returns: "STRING",
344            examples: vec![
345                "SELECT name.Substring(0, 5) FROM users",
346                "SELECT SUBSTRING(name, 0, 5) FROM users",
347            ],
348        }
349    }
350
351    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
352        if args.len() < 2 || args.len() > 3 {
353            return Err(anyhow!("Substring expects 2 or 3 arguments"));
354        }
355
356        let string = match &args[0] {
357            DataValue::String(s) => s.as_str(),
358            DataValue::InternedString(s) => s.as_str(),
359            DataValue::Null => return Ok(DataValue::Null),
360            _ => return Err(anyhow!("Substring expects a string as first argument")),
361        };
362
363        let start = match &args[1] {
364            DataValue::Integer(i) => *i as usize,
365            _ => return Err(anyhow!("Substring expects integer start position")),
366        };
367
368        let result = if args.len() == 3 {
369            let length = match &args[2] {
370                DataValue::Integer(i) => *i as usize,
371                _ => return Err(anyhow!("Substring expects integer length")),
372            };
373
374            let end = (start + length).min(string.len());
375            string.chars().skip(start).take(end - start).collect()
376        } else {
377            string.chars().skip(start).collect()
378        };
379
380        Ok(DataValue::String(result))
381    }
382}
383
384impl MethodFunction for SubstringMethod {
385    fn handles_method(&self, method_name: &str) -> bool {
386        method_name.eq_ignore_ascii_case("Substring") || method_name.eq_ignore_ascii_case("Substr")
387    }
388
389    fn method_name(&self) -> &'static str {
390        "Substring"
391    }
392}
393
394/// Replace method function
395pub struct ReplaceMethod;
396
397impl SqlFunction for ReplaceMethod {
398    fn signature(&self) -> FunctionSignature {
399        FunctionSignature {
400            name: "REPLACE",
401            category: FunctionCategory::String,
402            arg_count: ArgCount::Fixed(3),
403            description: "Replaces all occurrences of a substring",
404            returns: "STRING",
405            examples: vec![
406                "SELECT name.Replace('John', 'Jane') FROM users",
407                "SELECT REPLACE(name, 'John', 'Jane') FROM users",
408            ],
409        }
410    }
411
412    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
413        self.validate_args(args)?;
414
415        let string = match &args[0] {
416            DataValue::String(s) => s.as_str(),
417            DataValue::InternedString(s) => s.as_str(),
418            DataValue::Null => return Ok(DataValue::Null),
419            _ => return Err(anyhow!("Replace expects string arguments")),
420        };
421
422        let from = match &args[1] {
423            DataValue::String(s) => s.as_str(),
424            DataValue::InternedString(s) => s.as_str(),
425            _ => return Err(anyhow!("Replace expects string arguments")),
426        };
427
428        let to = match &args[2] {
429            DataValue::String(s) => s.as_str(),
430            DataValue::InternedString(s) => s.as_str(),
431            _ => return Err(anyhow!("Replace expects string arguments")),
432        };
433
434        Ok(DataValue::String(string.replace(from, to)))
435    }
436}
437
438impl MethodFunction for ReplaceMethod {
439    fn handles_method(&self, method_name: &str) -> bool {
440        method_name.eq_ignore_ascii_case("Replace")
441    }
442
443    fn method_name(&self) -> &'static str {
444        "Replace"
445    }
446}
447
448/// MID function - Extract substring (SQL/Excel compatible, 1-based indexing)
449pub struct MidFunction;
450
451impl SqlFunction for MidFunction {
452    fn signature(&self) -> FunctionSignature {
453        FunctionSignature {
454            name: "MID",
455            category: FunctionCategory::String,
456            arg_count: ArgCount::Fixed(3),
457            description: "Extract substring from text (1-based indexing)",
458            returns: "STRING",
459            examples: vec![
460                "SELECT MID('Hello', 1, 3)", // Returns 'Hel'
461                "SELECT MID('World', 2, 3)", // Returns 'orl'
462                "SELECT MID(name, 1, 5) FROM table",
463            ],
464        }
465    }
466
467    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
468        self.validate_args(args)?;
469
470        // Get the string
471        let text = match &args[0] {
472            DataValue::String(s) => s.clone(),
473            DataValue::InternedString(s) => s.to_string(),
474            DataValue::Integer(n) => n.to_string(),
475            DataValue::Float(f) => f.to_string(),
476            DataValue::Null => String::new(),
477            _ => return Err(anyhow!("MID first argument must be convertible to text")),
478        };
479
480        // Get start position (1-based)
481        let start_pos = match &args[1] {
482            DataValue::Integer(n) => *n,
483            DataValue::Float(f) => *f as i64,
484            _ => return Err(anyhow!("MID start position must be a number")),
485        };
486
487        // Get length
488        let length = match &args[2] {
489            DataValue::Integer(n) => *n,
490            DataValue::Float(f) => *f as i64,
491            _ => return Err(anyhow!("MID length must be a number")),
492        };
493
494        // Validate arguments
495        if start_pos < 1 {
496            return Err(anyhow!("MID start position must be >= 1"));
497        }
498        if length < 0 {
499            return Err(anyhow!("MID length must be >= 0"));
500        }
501
502        // Convert to 0-based index
503        let start_idx = (start_pos - 1) as usize;
504        let chars: Vec<char> = text.chars().collect();
505
506        // If start position is beyond string length, return empty string
507        if start_idx >= chars.len() {
508            return Ok(DataValue::String(String::new()));
509        }
510
511        // Extract substring
512        let end_idx = std::cmp::min(start_idx + length as usize, chars.len());
513        let result: String = chars[start_idx..end_idx].iter().collect();
514
515        Ok(DataValue::String(result))
516    }
517}
518
519/// UPPER function - Convert string to uppercase
520pub struct UpperFunction;
521
522impl SqlFunction for UpperFunction {
523    fn signature(&self) -> FunctionSignature {
524        FunctionSignature {
525            name: "UPPER",
526            category: FunctionCategory::String,
527            arg_count: ArgCount::Fixed(1),
528            description: "Convert string to uppercase",
529            returns: "STRING",
530            examples: vec![
531                "SELECT UPPER('hello')", // Returns 'HELLO'
532                "SELECT UPPER(name) FROM table",
533            ],
534        }
535    }
536
537    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
538        self.validate_args(args)?;
539
540        match &args[0] {
541            DataValue::String(s) => Ok(DataValue::String(s.to_uppercase())),
542            DataValue::InternedString(s) => Ok(DataValue::String(s.to_uppercase())),
543            DataValue::Null => Ok(DataValue::Null),
544            _ => Err(anyhow!("UPPER expects a string argument")),
545        }
546    }
547}
548
549/// LOWER function - Convert string to lowercase
550pub struct LowerFunction;
551
552impl SqlFunction for LowerFunction {
553    fn signature(&self) -> FunctionSignature {
554        FunctionSignature {
555            name: "LOWER",
556            category: FunctionCategory::String,
557            arg_count: ArgCount::Fixed(1),
558            description: "Convert string to lowercase",
559            returns: "STRING",
560            examples: vec![
561                "SELECT LOWER('HELLO')", // Returns 'hello'
562                "SELECT LOWER(name) FROM table",
563            ],
564        }
565    }
566
567    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
568        self.validate_args(args)?;
569
570        match &args[0] {
571            DataValue::String(s) => Ok(DataValue::String(s.to_lowercase())),
572            DataValue::InternedString(s) => Ok(DataValue::String(s.to_lowercase())),
573            DataValue::Null => Ok(DataValue::Null),
574            _ => Err(anyhow!("LOWER expects a string argument")),
575        }
576    }
577}
578
579/// TRIM function - Remove leading and trailing whitespace
580pub struct TrimFunction;
581
582impl SqlFunction for TrimFunction {
583    fn signature(&self) -> FunctionSignature {
584        FunctionSignature {
585            name: "TRIM",
586            category: FunctionCategory::String,
587            arg_count: ArgCount::Fixed(1),
588            description: "Remove leading and trailing whitespace",
589            returns: "STRING",
590            examples: vec![
591                "SELECT TRIM('  hello  ')", // Returns 'hello'
592                "SELECT TRIM(description) FROM table",
593            ],
594        }
595    }
596
597    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
598        self.validate_args(args)?;
599
600        match &args[0] {
601            DataValue::String(s) => Ok(DataValue::String(s.trim().to_string())),
602            DataValue::InternedString(s) => Ok(DataValue::String(s.trim().to_string())),
603            DataValue::Null => Ok(DataValue::Null),
604            _ => Err(anyhow!("TRIM expects a string argument")),
605        }
606    }
607}
608
609/// TEXTJOIN function - Join multiple text values with a delimiter
610pub struct TextJoinFunction;
611
612impl SqlFunction for TextJoinFunction {
613    fn signature(&self) -> FunctionSignature {
614        FunctionSignature {
615            name: "TEXTJOIN",
616            category: FunctionCategory::String,
617            arg_count: ArgCount::Variadic,
618            description: "Join multiple text values with a delimiter",
619            returns: "STRING",
620            examples: vec![
621                "SELECT TEXTJOIN(',', 1, 'a', 'b', 'c')", // Returns 'a,b,c'
622                "SELECT TEXTJOIN(' - ', 1, name, city) FROM table",
623                "SELECT TEXTJOIN('|', 0, col1, col2, col3) FROM table",
624            ],
625        }
626    }
627
628    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
629        if args.len() < 3 {
630            return Err(anyhow!("TEXTJOIN requires at least 3 arguments: delimiter, ignore_empty, text1, [text2, ...]"));
631        }
632
633        // First argument: delimiter
634        let delimiter = match &args[0] {
635            DataValue::String(s) => s.clone(),
636            DataValue::InternedString(s) => s.to_string(),
637            DataValue::Integer(n) => n.to_string(),
638            DataValue::Float(f) => f.to_string(),
639            DataValue::Boolean(b) => b.to_string(),
640            DataValue::Null => String::new(),
641            _ => String::new(),
642        };
643
644        // Second argument: ignore_empty (treat as boolean - 0 is false, anything else is true)
645        let ignore_empty = match &args[1] {
646            DataValue::Integer(n) => *n != 0,
647            DataValue::Float(f) => *f != 0.0,
648            DataValue::Boolean(b) => *b,
649            DataValue::String(s) => !s.is_empty() && s != "0" && s.to_lowercase() != "false",
650            DataValue::InternedString(s) => {
651                !s.is_empty() && s.as_str() != "0" && s.to_lowercase() != "false"
652            }
653            DataValue::Null => false,
654            _ => true,
655        };
656
657        // Remaining arguments: values to join
658        let mut values = Vec::new();
659        for i in 2..args.len() {
660            let string_value = match &args[i] {
661                DataValue::String(s) => Some(s.clone()),
662                DataValue::InternedString(s) => Some(s.to_string()),
663                DataValue::Integer(n) => Some(n.to_string()),
664                DataValue::Float(f) => Some(f.to_string()),
665                DataValue::Boolean(b) => Some(b.to_string()),
666                DataValue::DateTime(dt) => Some(dt.clone()),
667                DataValue::Null => {
668                    if ignore_empty {
669                        None
670                    } else {
671                        Some(String::new())
672                    }
673                }
674                _ => {
675                    if ignore_empty {
676                        None
677                    } else {
678                        Some(String::new())
679                    }
680                }
681            };
682
683            if let Some(s) = string_value {
684                if !ignore_empty || !s.is_empty() {
685                    values.push(s);
686                }
687            }
688        }
689
690        Ok(DataValue::String(values.join(&delimiter)))
691    }
692}
693
694/// Edit distance (Levenshtein distance) function
695pub struct EditDistanceFunction;
696
697impl EditDistanceFunction {
698    /// Calculate the Levenshtein distance between two strings
699    #[must_use]
700    pub fn calculate_edit_distance(s1: &str, s2: &str) -> usize {
701        let len1 = s1.len();
702        let len2 = s2.len();
703        let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
704
705        for i in 0..=len1 {
706            matrix[i][0] = i;
707        }
708        for j in 0..=len2 {
709            matrix[0][j] = j;
710        }
711
712        for (i, c1) in s1.chars().enumerate() {
713            for (j, c2) in s2.chars().enumerate() {
714                let cost = usize::from(c1 != c2);
715                matrix[i + 1][j + 1] = std::cmp::min(
716                    matrix[i][j + 1] + 1, // deletion
717                    std::cmp::min(
718                        matrix[i + 1][j] + 1, // insertion
719                        matrix[i][j] + cost,  // substitution
720                    ),
721                );
722            }
723        }
724
725        matrix[len1][len2]
726    }
727}
728
729impl SqlFunction for EditDistanceFunction {
730    fn signature(&self) -> FunctionSignature {
731        FunctionSignature {
732            name: "EDIT_DISTANCE",
733            category: FunctionCategory::String,
734            arg_count: ArgCount::Fixed(2),
735            description: "Calculate the Levenshtein edit distance between two strings",
736            returns: "INTEGER",
737            examples: vec![
738                "SELECT EDIT_DISTANCE('kitten', 'sitting')",
739                "SELECT EDIT_DISTANCE(name, 'John') FROM users",
740                "SELECT * FROM users WHERE EDIT_DISTANCE(name, 'Smith') <= 2",
741            ],
742        }
743    }
744
745    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
746        self.validate_args(args)?;
747
748        let s1 = match &args[0] {
749            DataValue::String(s) => s.clone(),
750            DataValue::InternedString(s) => s.to_string(),
751            DataValue::Null => return Ok(DataValue::Null),
752            _ => return Err(anyhow!("EDIT_DISTANCE expects string arguments")),
753        };
754
755        let s2 = match &args[1] {
756            DataValue::String(s) => s.clone(),
757            DataValue::InternedString(s) => s.to_string(),
758            DataValue::Null => return Ok(DataValue::Null),
759            _ => return Err(anyhow!("EDIT_DISTANCE expects string arguments")),
760        };
761
762        let distance = Self::calculate_edit_distance(&s1, &s2);
763        Ok(DataValue::Integer(distance as i64))
764    }
765}
766
767/// Register all string method functions
768pub fn register_string_methods(registry: &mut super::FunctionRegistry) {
769    use std::sync::Arc;
770
771    // Register new string functions (non-method versions)
772    registry.register(Box::new(MidFunction));
773    registry.register(Box::new(UpperFunction));
774    registry.register(Box::new(LowerFunction));
775    registry.register(Box::new(TrimFunction));
776    registry.register(Box::new(TextJoinFunction));
777    registry.register(Box::new(EditDistanceFunction));
778
779    // Register ToUpper
780    let to_upper = Arc::new(ToUpperMethod);
781    registry.register(Box::new(ToUpperMethod));
782    registry.register_method(to_upper);
783
784    // Register ToLower
785    let to_lower = Arc::new(ToLowerMethod);
786    registry.register(Box::new(ToLowerMethod));
787    registry.register_method(to_lower);
788
789    // Register Trim
790    let trim = Arc::new(TrimMethod);
791    registry.register(Box::new(TrimMethod));
792    registry.register_method(trim);
793
794    // Register Length
795    let length = Arc::new(LengthMethod);
796    registry.register(Box::new(LengthMethod));
797    registry.register_method(length);
798
799    // Register Contains
800    let contains = Arc::new(ContainsMethod);
801    registry.register(Box::new(ContainsMethod));
802    registry.register_method(contains);
803
804    // Register StartsWith
805    let starts_with = Arc::new(StartsWithMethod);
806    registry.register(Box::new(StartsWithMethod));
807    registry.register_method(starts_with);
808
809    // Register EndsWith
810    let ends_with = Arc::new(EndsWithMethod);
811    registry.register(Box::new(EndsWithMethod));
812    registry.register_method(ends_with);
813
814    // Register Substring
815    let substring = Arc::new(SubstringMethod);
816    registry.register(Box::new(SubstringMethod));
817    registry.register_method(substring);
818
819    // Register Replace
820    let replace = Arc::new(ReplaceMethod);
821    registry.register(Box::new(ReplaceMethod));
822    registry.register_method(replace);
823}