Skip to main content

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/// TrimStart method function
147pub struct TrimStartMethod;
148
149impl SqlFunction for TrimStartMethod {
150    fn signature(&self) -> FunctionSignature {
151        FunctionSignature {
152            name: "TRIMSTART",
153            category: FunctionCategory::String,
154            arg_count: ArgCount::Fixed(1),
155            description: "Removes leading whitespace",
156            returns: "STRING",
157            examples: vec![
158                "SELECT name.TrimStart() FROM users",
159                "SELECT TRIMSTART(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::String(s.trim_start().to_string())),
169            DataValue::InternedString(s) => Ok(DataValue::String(s.trim_start().to_string())),
170            DataValue::Null => Ok(DataValue::Null),
171            _ => Err(anyhow!("TrimStart expects a string argument")),
172        }
173    }
174}
175
176impl MethodFunction for TrimStartMethod {
177    fn handles_method(&self, method_name: &str) -> bool {
178        method_name.eq_ignore_ascii_case("TrimStart")
179    }
180
181    fn method_name(&self) -> &'static str {
182        "TrimStart"
183    }
184}
185
186/// TrimEnd method function
187pub struct TrimEndMethod;
188
189impl SqlFunction for TrimEndMethod {
190    fn signature(&self) -> FunctionSignature {
191        FunctionSignature {
192            name: "TRIMEND",
193            category: FunctionCategory::String,
194            arg_count: ArgCount::Fixed(1),
195            description: "Removes trailing whitespace",
196            returns: "STRING",
197            examples: vec![
198                "SELECT name.TrimEnd() FROM users",
199                "SELECT TRIMEND(name) FROM users",
200            ],
201        }
202    }
203
204    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
205        self.validate_args(args)?;
206
207        match &args[0] {
208            DataValue::String(s) => Ok(DataValue::String(s.trim_end().to_string())),
209            DataValue::InternedString(s) => Ok(DataValue::String(s.trim_end().to_string())),
210            DataValue::Null => Ok(DataValue::Null),
211            _ => Err(anyhow!("TrimEnd expects a string argument")),
212        }
213    }
214}
215
216impl MethodFunction for TrimEndMethod {
217    fn handles_method(&self, method_name: &str) -> bool {
218        method_name.eq_ignore_ascii_case("TrimEnd")
219    }
220
221    fn method_name(&self) -> &'static str {
222        "TrimEnd"
223    }
224}
225
226/// Length method function (returns integer)
227pub struct LengthMethod;
228
229impl SqlFunction for LengthMethod {
230    fn signature(&self) -> FunctionSignature {
231        FunctionSignature {
232            name: "LENGTH",
233            category: FunctionCategory::String,
234            arg_count: ArgCount::Fixed(1),
235            description: "Returns the length of a string",
236            returns: "INTEGER",
237            examples: vec![
238                "SELECT name.Length() FROM users",
239                "SELECT LENGTH(name) FROM users",
240            ],
241        }
242    }
243
244    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
245        self.validate_args(args)?;
246
247        match &args[0] {
248            DataValue::String(s) => Ok(DataValue::Integer(s.len() as i64)),
249            DataValue::InternedString(s) => Ok(DataValue::Integer(s.len() as i64)),
250            DataValue::Null => Ok(DataValue::Null),
251            _ => Err(anyhow!("Length expects a string argument")),
252        }
253    }
254}
255
256impl MethodFunction for LengthMethod {
257    fn handles_method(&self, method_name: &str) -> bool {
258        method_name.eq_ignore_ascii_case("Length") || method_name.eq_ignore_ascii_case("Len")
259    }
260
261    fn method_name(&self) -> &'static str {
262        "Length"
263    }
264}
265
266/// Contains method function (returns boolean)
267pub struct ContainsMethod;
268
269impl SqlFunction for ContainsMethod {
270    fn signature(&self) -> FunctionSignature {
271        FunctionSignature {
272            name: "CONTAINS",
273            category: FunctionCategory::String,
274            arg_count: ArgCount::Fixed(2),
275            description: "Checks if string contains substring",
276            returns: "BOOLEAN",
277            examples: vec![
278                "SELECT * FROM users WHERE name.Contains('john')",
279                "SELECT CONTAINS(name, 'john') FROM users",
280            ],
281        }
282    }
283
284    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
285        self.validate_args(args)?;
286
287        let haystack = match &args[0] {
288            DataValue::String(s) => s.as_str(),
289            DataValue::InternedString(s) => s.as_str(),
290            DataValue::Null => return Ok(DataValue::Boolean(false)),
291            _ => return Err(anyhow!("Contains expects string arguments")),
292        };
293
294        let needle = match &args[1] {
295            DataValue::String(s) => s.as_str(),
296            DataValue::InternedString(s) => s.as_str(),
297            DataValue::Null => return Ok(DataValue::Boolean(false)),
298            _ => return Err(anyhow!("Contains expects string arguments")),
299        };
300
301        Ok(DataValue::Boolean(haystack.contains(needle)))
302    }
303}
304
305impl MethodFunction for ContainsMethod {
306    fn handles_method(&self, method_name: &str) -> bool {
307        method_name.eq_ignore_ascii_case("Contains")
308    }
309
310    fn method_name(&self) -> &'static str {
311        "Contains"
312    }
313}
314
315/// `StartsWith` method function
316pub struct StartsWithMethod;
317
318impl SqlFunction for StartsWithMethod {
319    fn signature(&self) -> FunctionSignature {
320        FunctionSignature {
321            name: "STARTSWITH",
322            category: FunctionCategory::String,
323            arg_count: ArgCount::Fixed(2),
324            description: "Checks if string starts with prefix",
325            returns: "BOOLEAN",
326            examples: vec![
327                "SELECT * FROM users WHERE name.StartsWith('John')",
328                "SELECT STARTSWITH(name, 'John') FROM users",
329            ],
330        }
331    }
332
333    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
334        self.validate_args(args)?;
335
336        let string = match &args[0] {
337            DataValue::String(s) => s.as_str(),
338            DataValue::InternedString(s) => s.as_str(),
339            DataValue::Null => return Ok(DataValue::Boolean(false)),
340            _ => return Err(anyhow!("StartsWith expects string arguments")),
341        };
342
343        let prefix = match &args[1] {
344            DataValue::String(s) => s.as_str(),
345            DataValue::InternedString(s) => s.as_str(),
346            DataValue::Null => return Ok(DataValue::Boolean(false)),
347            _ => return Err(anyhow!("StartsWith expects string arguments")),
348        };
349
350        Ok(DataValue::Boolean(string.starts_with(prefix)))
351    }
352}
353
354impl MethodFunction for StartsWithMethod {
355    fn handles_method(&self, method_name: &str) -> bool {
356        method_name.eq_ignore_ascii_case("StartsWith")
357    }
358
359    fn method_name(&self) -> &'static str {
360        "StartsWith"
361    }
362}
363
364/// `EndsWith` method function
365pub struct EndsWithMethod;
366
367impl SqlFunction for EndsWithMethod {
368    fn signature(&self) -> FunctionSignature {
369        FunctionSignature {
370            name: "ENDSWITH",
371            category: FunctionCategory::String,
372            arg_count: ArgCount::Fixed(2),
373            description: "Checks if string ends with suffix",
374            returns: "BOOLEAN",
375            examples: vec![
376                "SELECT * FROM users WHERE email.EndsWith('.com')",
377                "SELECT ENDSWITH(email, '.com') FROM users",
378            ],
379        }
380    }
381
382    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
383        self.validate_args(args)?;
384
385        let string = match &args[0] {
386            DataValue::String(s) => s.as_str(),
387            DataValue::InternedString(s) => s.as_str(),
388            DataValue::Null => return Ok(DataValue::Boolean(false)),
389            _ => return Err(anyhow!("EndsWith expects string arguments")),
390        };
391
392        let suffix = match &args[1] {
393            DataValue::String(s) => s.as_str(),
394            DataValue::InternedString(s) => s.as_str(),
395            DataValue::Null => return Ok(DataValue::Boolean(false)),
396            _ => return Err(anyhow!("EndsWith expects string arguments")),
397        };
398
399        Ok(DataValue::Boolean(string.ends_with(suffix)))
400    }
401}
402
403impl MethodFunction for EndsWithMethod {
404    fn handles_method(&self, method_name: &str) -> bool {
405        method_name.eq_ignore_ascii_case("EndsWith")
406    }
407
408    fn method_name(&self) -> &'static str {
409        "EndsWith"
410    }
411}
412
413/// Substring method function
414pub struct SubstringMethod;
415
416impl SqlFunction for SubstringMethod {
417    fn signature(&self) -> FunctionSignature {
418        FunctionSignature {
419            name: "SUBSTRING",
420            category: FunctionCategory::String,
421            arg_count: ArgCount::Range(2, 3),
422            description: "Extracts substring from string",
423            returns: "STRING",
424            examples: vec![
425                // C# method syntax is 0-based; SQL function syntax is 1-based.
426                "SELECT name.Substring(0, 5) FROM users",
427                "SELECT SUBSTRING(name, 1, 5) FROM users",
428            ],
429        }
430    }
431
432    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
433        // SQL function form: 1-based, mirrors the SQL standard / DuckDB / SQL Server.
434        Self::extract(args, true)
435    }
436}
437
438impl SubstringMethod {
439    /// Shared substring logic. `one_based` selects SQL semantics (start at 1) vs
440    /// C# `.Substring()` semantics (start at 0).
441    ///
442    /// With 1-based indexing a `start` below 1 still anchors the window at the
443    /// first character but consumes part of the requested length (matching
444    /// DuckDB), so `SUBSTRING('hello', 0, 2)` yields `'h'`.
445    fn extract(args: &[DataValue], one_based: bool) -> Result<DataValue> {
446        if args.len() < 2 || args.len() > 3 {
447            return Err(anyhow!("Substring expects 2 or 3 arguments"));
448        }
449
450        let string = match &args[0] {
451            DataValue::String(s) => s.as_str(),
452            DataValue::InternedString(s) => s.as_str(),
453            DataValue::Null => return Ok(DataValue::Null),
454            _ => return Err(anyhow!("Substring expects a string as first argument")),
455        };
456
457        let raw_start = match &args[1] {
458            DataValue::Integer(i) => *i,
459            _ => return Err(anyhow!("Substring expects integer start position")),
460        };
461
462        // Normalize to a 0-based start; clamp negatives/zero to the string head.
463        let zero_based_start = if one_based { raw_start - 1 } else { raw_start };
464        let skip = zero_based_start.max(0) as usize;
465
466        let result: String = if args.len() == 3 {
467            let length = match &args[2] {
468                DataValue::Integer(i) => *i,
469                _ => return Err(anyhow!("Substring expects integer length")),
470            };
471            // A start before the string still spends length: take = length + start
472            // when start < 0 (so positions before the head are "consumed").
473            let consumed_before = (-zero_based_start).max(0);
474            let take = (length - consumed_before).max(0) as usize;
475            string.chars().skip(skip).take(take).collect()
476        } else {
477            string.chars().skip(skip).collect()
478        };
479
480        Ok(DataValue::String(result))
481    }
482}
483
484impl MethodFunction for SubstringMethod {
485    fn handles_method(&self, method_name: &str) -> bool {
486        method_name.eq_ignore_ascii_case("Substring") || method_name.eq_ignore_ascii_case("Substr")
487    }
488
489    fn method_name(&self) -> &'static str {
490        "Substring"
491    }
492
493    fn evaluate_method(&self, receiver: &DataValue, args: &[DataValue]) -> Result<DataValue> {
494        // C# `.Substring()` form keeps 0-based indexing (.NET semantics).
495        let mut full_args = vec![receiver.clone()];
496        full_args.extend_from_slice(args);
497        Self::extract(&full_args, false)
498    }
499}
500
501/// Replace method function
502pub struct ReplaceMethod;
503
504impl SqlFunction for ReplaceMethod {
505    fn signature(&self) -> FunctionSignature {
506        FunctionSignature {
507            name: "REPLACE",
508            category: FunctionCategory::String,
509            arg_count: ArgCount::Fixed(3),
510            description: "Replaces all occurrences of a substring",
511            returns: "STRING",
512            examples: vec![
513                "SELECT name.Replace('John', 'Jane') FROM users",
514                "SELECT REPLACE(name, 'John', 'Jane') FROM users",
515            ],
516        }
517    }
518
519    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
520        self.validate_args(args)?;
521
522        let string = match &args[0] {
523            DataValue::String(s) => s.as_str(),
524            DataValue::InternedString(s) => s.as_str(),
525            DataValue::Null => return Ok(DataValue::Null),
526            _ => return Err(anyhow!("Replace expects string arguments")),
527        };
528
529        let from = match &args[1] {
530            DataValue::String(s) => s.as_str(),
531            DataValue::InternedString(s) => s.as_str(),
532            _ => return Err(anyhow!("Replace expects string arguments")),
533        };
534
535        let to = match &args[2] {
536            DataValue::String(s) => s.as_str(),
537            DataValue::InternedString(s) => s.as_str(),
538            _ => return Err(anyhow!("Replace expects string arguments")),
539        };
540
541        Ok(DataValue::String(string.replace(from, to)))
542    }
543}
544
545impl MethodFunction for ReplaceMethod {
546    fn handles_method(&self, method_name: &str) -> bool {
547        method_name.eq_ignore_ascii_case("Replace")
548    }
549
550    fn method_name(&self) -> &'static str {
551        "Replace"
552    }
553}
554
555/// MID function - Extract substring (SQL/Excel compatible, 1-based indexing)
556pub struct MidFunction;
557
558impl SqlFunction for MidFunction {
559    fn signature(&self) -> FunctionSignature {
560        FunctionSignature {
561            name: "MID",
562            category: FunctionCategory::String,
563            arg_count: ArgCount::Fixed(3),
564            description: "Extract substring from text (1-based indexing)",
565            returns: "STRING",
566            examples: vec![
567                "SELECT MID('Hello', 1, 3)", // Returns 'Hel'
568                "SELECT MID('World', 2, 3)", // Returns 'orl'
569                "SELECT MID(name, 1, 5) FROM table",
570            ],
571        }
572    }
573
574    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
575        self.validate_args(args)?;
576
577        // Get the string
578        let text = match &args[0] {
579            DataValue::String(s) => s.clone(),
580            DataValue::InternedString(s) => s.to_string(),
581            DataValue::Integer(n) => n.to_string(),
582            DataValue::Float(f) => f.to_string(),
583            DataValue::Null => String::new(),
584            _ => return Err(anyhow!("MID first argument must be convertible to text")),
585        };
586
587        // Get start position (1-based)
588        let start_pos = match &args[1] {
589            DataValue::Integer(n) => *n,
590            DataValue::Float(f) => *f as i64,
591            _ => return Err(anyhow!("MID start position must be a number")),
592        };
593
594        // Get length
595        let length = match &args[2] {
596            DataValue::Integer(n) => *n,
597            DataValue::Float(f) => *f as i64,
598            _ => return Err(anyhow!("MID length must be a number")),
599        };
600
601        // Validate arguments
602        if start_pos < 1 {
603            return Err(anyhow!("MID start position must be >= 1"));
604        }
605        if length < 0 {
606            return Err(anyhow!("MID length must be >= 0"));
607        }
608
609        // Convert to 0-based index
610        let start_idx = (start_pos - 1) as usize;
611        let chars: Vec<char> = text.chars().collect();
612
613        // If start position is beyond string length, return empty string
614        if start_idx >= chars.len() {
615            return Ok(DataValue::String(String::new()));
616        }
617
618        // Extract substring
619        let end_idx = std::cmp::min(start_idx + length as usize, chars.len());
620        let result: String = chars[start_idx..end_idx].iter().collect();
621
622        Ok(DataValue::String(result))
623    }
624}
625
626/// UPPER function - Convert string to uppercase
627pub struct UpperFunction;
628
629impl SqlFunction for UpperFunction {
630    fn signature(&self) -> FunctionSignature {
631        FunctionSignature {
632            name: "UPPER",
633            category: FunctionCategory::String,
634            arg_count: ArgCount::Fixed(1),
635            description: "Convert string to uppercase",
636            returns: "STRING",
637            examples: vec![
638                "SELECT UPPER('hello')", // Returns 'HELLO'
639                "SELECT UPPER(name) FROM table",
640            ],
641        }
642    }
643
644    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
645        self.validate_args(args)?;
646
647        match &args[0] {
648            DataValue::String(s) => Ok(DataValue::String(s.to_uppercase())),
649            DataValue::InternedString(s) => Ok(DataValue::String(s.to_uppercase())),
650            DataValue::Null => Ok(DataValue::Null),
651            _ => Err(anyhow!("UPPER expects a string argument")),
652        }
653    }
654}
655
656/// LOWER function - Convert string to lowercase
657pub struct LowerFunction;
658
659impl SqlFunction for LowerFunction {
660    fn signature(&self) -> FunctionSignature {
661        FunctionSignature {
662            name: "LOWER",
663            category: FunctionCategory::String,
664            arg_count: ArgCount::Fixed(1),
665            description: "Convert string to lowercase",
666            returns: "STRING",
667            examples: vec![
668                "SELECT LOWER('HELLO')", // Returns 'hello'
669                "SELECT LOWER(name) FROM table",
670            ],
671        }
672    }
673
674    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
675        self.validate_args(args)?;
676
677        match &args[0] {
678            DataValue::String(s) => Ok(DataValue::String(s.to_lowercase())),
679            DataValue::InternedString(s) => Ok(DataValue::String(s.to_lowercase())),
680            DataValue::Null => Ok(DataValue::Null),
681            _ => Err(anyhow!("LOWER expects a string argument")),
682        }
683    }
684}
685
686/// TRIM function - Remove leading and trailing whitespace
687pub struct TrimFunction;
688
689impl SqlFunction for TrimFunction {
690    fn signature(&self) -> FunctionSignature {
691        FunctionSignature {
692            name: "TRIM",
693            category: FunctionCategory::String,
694            arg_count: ArgCount::Fixed(1),
695            description: "Remove leading and trailing whitespace",
696            returns: "STRING",
697            examples: vec![
698                "SELECT TRIM('  hello  ')", // Returns 'hello'
699                "SELECT TRIM(description) FROM table",
700            ],
701        }
702    }
703
704    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
705        self.validate_args(args)?;
706
707        match &args[0] {
708            DataValue::String(s) => Ok(DataValue::String(s.trim().to_string())),
709            DataValue::InternedString(s) => Ok(DataValue::String(s.trim().to_string())),
710            DataValue::Null => Ok(DataValue::Null),
711            _ => Err(anyhow!("TRIM expects a string argument")),
712        }
713    }
714}
715
716/// TEXTJOIN function - Join multiple text values with a delimiter
717pub struct TextJoinFunction;
718
719impl SqlFunction for TextJoinFunction {
720    fn signature(&self) -> FunctionSignature {
721        FunctionSignature {
722            name: "TEXTJOIN",
723            category: FunctionCategory::String,
724            arg_count: ArgCount::Variadic,
725            description: "Join multiple text values with a delimiter",
726            returns: "STRING",
727            examples: vec![
728                "SELECT TEXTJOIN(',', 1, 'a', 'b', 'c')", // Returns 'a,b,c'
729                "SELECT TEXTJOIN(' - ', 1, name, city) FROM table",
730                "SELECT TEXTJOIN('|', 0, col1, col2, col3) FROM table",
731            ],
732        }
733    }
734
735    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
736        if args.len() < 3 {
737            return Err(anyhow!("TEXTJOIN requires at least 3 arguments: delimiter, ignore_empty, text1, [text2, ...]"));
738        }
739
740        // First argument: delimiter
741        let delimiter = match &args[0] {
742            DataValue::String(s) => s.clone(),
743            DataValue::InternedString(s) => s.to_string(),
744            DataValue::Integer(n) => n.to_string(),
745            DataValue::Float(f) => f.to_string(),
746            DataValue::Boolean(b) => b.to_string(),
747            DataValue::Null => String::new(),
748            _ => String::new(),
749        };
750
751        // Second argument: ignore_empty (treat as boolean - 0 is false, anything else is true)
752        let ignore_empty = match &args[1] {
753            DataValue::Integer(n) => *n != 0,
754            DataValue::Float(f) => *f != 0.0,
755            DataValue::Boolean(b) => *b,
756            DataValue::String(s) => !s.is_empty() && s != "0" && s.to_lowercase() != "false",
757            DataValue::InternedString(s) => {
758                !s.is_empty() && s.as_str() != "0" && s.to_lowercase() != "false"
759            }
760            DataValue::Null => false,
761            _ => true,
762        };
763
764        // Remaining arguments: values to join
765        let mut values = Vec::new();
766        for i in 2..args.len() {
767            let string_value = match &args[i] {
768                DataValue::String(s) => Some(s.clone()),
769                DataValue::InternedString(s) => Some(s.to_string()),
770                DataValue::Integer(n) => Some(n.to_string()),
771                DataValue::Float(f) => Some(f.to_string()),
772                DataValue::Boolean(b) => Some(b.to_string()),
773                DataValue::DateTime(dt) => Some(dt.clone()),
774                DataValue::Vector(v) => {
775                    let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
776                    Some(format!("[{}]", components.join(",")))
777                }
778                DataValue::Null => {
779                    if ignore_empty {
780                        None
781                    } else {
782                        Some(String::new())
783                    }
784                }
785            };
786
787            if let Some(s) = string_value {
788                if !ignore_empty || !s.is_empty() {
789                    values.push(s);
790                }
791            }
792        }
793
794        Ok(DataValue::String(values.join(&delimiter)))
795    }
796}
797
798/// Edit distance (Levenshtein distance) function
799pub struct EditDistanceFunction;
800
801impl EditDistanceFunction {
802    /// Calculate the Levenshtein distance between two strings
803    #[must_use]
804    pub fn calculate_edit_distance(s1: &str, s2: &str) -> usize {
805        let len1 = s1.len();
806        let len2 = s2.len();
807        let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
808
809        for i in 0..=len1 {
810            matrix[i][0] = i;
811        }
812        for j in 0..=len2 {
813            matrix[0][j] = j;
814        }
815
816        for (i, c1) in s1.chars().enumerate() {
817            for (j, c2) in s2.chars().enumerate() {
818                let cost = usize::from(c1 != c2);
819                matrix[i + 1][j + 1] = std::cmp::min(
820                    matrix[i][j + 1] + 1, // deletion
821                    std::cmp::min(
822                        matrix[i + 1][j] + 1, // insertion
823                        matrix[i][j] + cost,  // substitution
824                    ),
825                );
826            }
827        }
828
829        matrix[len1][len2]
830    }
831}
832
833impl SqlFunction for EditDistanceFunction {
834    fn signature(&self) -> FunctionSignature {
835        FunctionSignature {
836            name: "EDIT_DISTANCE",
837            category: FunctionCategory::String,
838            arg_count: ArgCount::Fixed(2),
839            description: "Calculate the Levenshtein edit distance between two strings",
840            returns: "INTEGER",
841            examples: vec![
842                "SELECT EDIT_DISTANCE('kitten', 'sitting')",
843                "SELECT EDIT_DISTANCE(name, 'John') FROM users",
844                "SELECT * FROM users WHERE EDIT_DISTANCE(name, 'Smith') <= 2",
845            ],
846        }
847    }
848
849    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
850        self.validate_args(args)?;
851
852        let s1 = match &args[0] {
853            DataValue::String(s) => s.clone(),
854            DataValue::InternedString(s) => s.to_string(),
855            DataValue::Null => return Ok(DataValue::Null),
856            _ => return Err(anyhow!("EDIT_DISTANCE expects string arguments")),
857        };
858
859        let s2 = match &args[1] {
860            DataValue::String(s) => s.clone(),
861            DataValue::InternedString(s) => s.to_string(),
862            DataValue::Null => return Ok(DataValue::Null),
863            _ => return Err(anyhow!("EDIT_DISTANCE expects string arguments")),
864        };
865
866        let distance = Self::calculate_edit_distance(&s1, &s2);
867        Ok(DataValue::Integer(distance as i64))
868    }
869}
870
871/// FREQUENCY function - Count occurrences of a substring in a string
872pub struct FrequencyFunction;
873
874impl SqlFunction for FrequencyFunction {
875    fn signature(&self) -> FunctionSignature {
876        FunctionSignature {
877            name: "FREQUENCY",
878            category: FunctionCategory::String,
879            arg_count: ArgCount::Fixed(2),
880            description: "Count occurrences of a substring within a string",
881            returns: "INTEGER",
882            examples: vec![
883                "SELECT FREQUENCY('hello world', 'o')",  // Returns 2
884                "SELECT FREQUENCY('mississippi', 'ss')", // Returns 2
885                "SELECT FREQUENCY(text_column, 'error') FROM logs",
886                "SELECT name, FREQUENCY(name, 'a') as a_count FROM users",
887            ],
888        }
889    }
890
891    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
892        self.validate_args(args)?;
893
894        // Get the string to search in
895        let text = match &args[0] {
896            DataValue::String(s) => s.clone(),
897            DataValue::InternedString(s) => s.to_string(),
898            DataValue::Null => return Ok(DataValue::Integer(0)),
899            _ => return Err(anyhow!("FREQUENCY expects string as first argument")),
900        };
901
902        // Get the substring to search for
903        let search = match &args[1] {
904            DataValue::String(s) => s.clone(),
905            DataValue::InternedString(s) => s.to_string(),
906            DataValue::Null => return Ok(DataValue::Integer(0)),
907            _ => return Err(anyhow!("FREQUENCY expects string as second argument")),
908        };
909
910        // Empty search string returns 0
911        if search.is_empty() {
912            return Ok(DataValue::Integer(0));
913        }
914
915        // Count occurrences
916        let count = text.matches(&search).count();
917        Ok(DataValue::Integer(count as i64))
918    }
919}
920
921/// IndexOf method function - finds the position of a substring
922pub struct IndexOfMethod;
923
924impl SqlFunction for IndexOfMethod {
925    fn signature(&self) -> FunctionSignature {
926        FunctionSignature {
927            name: "INDEXOF",
928            category: FunctionCategory::String,
929            arg_count: ArgCount::Fixed(2),
930            description: "Returns the position of the first occurrence of a substring (0-based)",
931            returns: "INTEGER",
932            examples: vec![
933                "SELECT email.IndexOf('@') FROM users",
934                "SELECT INDEXOF(email, '@') FROM users",
935            ],
936        }
937    }
938
939    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
940        self.validate_args(args)?;
941
942        let string = match &args[0] {
943            DataValue::String(s) => s.as_str(),
944            DataValue::InternedString(s) => s.as_str(),
945            DataValue::Null => return Ok(DataValue::Null),
946            _ => return Err(anyhow!("IndexOf expects string arguments")),
947        };
948
949        let substring = match &args[1] {
950            DataValue::String(s) => s.as_str(),
951            DataValue::InternedString(s) => s.as_str(),
952            DataValue::Null => return Ok(DataValue::Null),
953            _ => return Err(anyhow!("IndexOf expects string arguments")),
954        };
955
956        match string.find(substring) {
957            Some(pos) => Ok(DataValue::Integer(pos as i64)),
958            None => Ok(DataValue::Integer(-1)), // Return -1 if not found
959        }
960    }
961}
962
963impl MethodFunction for IndexOfMethod {
964    fn handles_method(&self, method_name: &str) -> bool {
965        method_name.eq_ignore_ascii_case("IndexOf")
966    }
967
968    fn method_name(&self) -> &'static str {
969        "IndexOf"
970    }
971}
972
973/// INSTR function - SQL standard function for finding substring position
974/// Returns 1-based position for SQL compatibility
975pub struct InstrFunction;
976
977impl SqlFunction for InstrFunction {
978    fn signature(&self) -> FunctionSignature {
979        FunctionSignature {
980            name: "INSTR",
981            category: FunctionCategory::String,
982            arg_count: ArgCount::Fixed(2),
983            description: "Returns the position of the first occurrence of a substring (1-based, SQL standard)",
984            returns: "INTEGER",
985            examples: vec![
986                "SELECT INSTR(email, '@') FROM users",
987                "SELECT SUBSTRING(email, INSTR(email, '@') + 1) FROM users",
988            ],
989        }
990    }
991
992    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
993        self.validate_args(args)?;
994
995        let string = match &args[0] {
996            DataValue::String(s) => s.as_str(),
997            DataValue::InternedString(s) => s.as_str(),
998            DataValue::Null => return Ok(DataValue::Null),
999            _ => return Err(anyhow!("INSTR expects string arguments")),
1000        };
1001
1002        let substring = match &args[1] {
1003            DataValue::String(s) => s.as_str(),
1004            DataValue::InternedString(s) => s.as_str(),
1005            DataValue::Null => return Ok(DataValue::Null),
1006            _ => return Err(anyhow!("INSTR expects string arguments")),
1007        };
1008
1009        match string.find(substring) {
1010            Some(pos) => Ok(DataValue::Integer((pos + 1) as i64)), // 1-based for SQL
1011            None => Ok(DataValue::Integer(0)), // Return 0 if not found (SQL standard)
1012        }
1013    }
1014}
1015
1016/// LEFT function - extracts leftmost n characters or up to a delimiter
1017pub struct LeftFunction;
1018
1019impl SqlFunction for LeftFunction {
1020    fn signature(&self) -> FunctionSignature {
1021        FunctionSignature {
1022            name: "LEFT",
1023            category: FunctionCategory::String,
1024            arg_count: ArgCount::Fixed(2),
1025            description: "Returns leftmost n characters from string",
1026            returns: "STRING",
1027            examples: vec![
1028                "SELECT LEFT(email, 5) FROM users",
1029                "SELECT LEFT('hello@world', INSTR('hello@world', '@') - 1)",
1030            ],
1031        }
1032    }
1033
1034    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1035        self.validate_args(args)?;
1036
1037        let string = match &args[0] {
1038            DataValue::String(s) => s.as_str(),
1039            DataValue::InternedString(s) => s.as_str(),
1040            DataValue::Null => return Ok(DataValue::Null),
1041            _ => return Err(anyhow!("LEFT expects a string as first argument")),
1042        };
1043
1044        let length = match &args[1] {
1045            DataValue::Integer(n) => *n as usize,
1046            DataValue::Float(f) => *f as usize,
1047            DataValue::Null => return Ok(DataValue::Null),
1048            _ => return Err(anyhow!("LEFT expects a number as second argument")),
1049        };
1050
1051        let result = if length >= string.len() {
1052            string.to_string()
1053        } else {
1054            string.chars().take(length).collect()
1055        };
1056
1057        Ok(DataValue::String(result))
1058    }
1059}
1060
1061/// RIGHT function - extracts rightmost n characters
1062pub struct RightFunction;
1063
1064impl SqlFunction for RightFunction {
1065    fn signature(&self) -> FunctionSignature {
1066        FunctionSignature {
1067            name: "RIGHT",
1068            category: FunctionCategory::String,
1069            arg_count: ArgCount::Fixed(2),
1070            description: "Returns rightmost n characters from string",
1071            returns: "STRING",
1072            examples: vec![
1073                "SELECT RIGHT(filename, 4) FROM files", // Get file extension
1074                "SELECT RIGHT(email, LENGTH(email) - INSTR(email, '@'))", // Get domain
1075            ],
1076        }
1077    }
1078
1079    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1080        self.validate_args(args)?;
1081
1082        let string = match &args[0] {
1083            DataValue::String(s) => s.as_str(),
1084            DataValue::InternedString(s) => s.as_str(),
1085            DataValue::Null => return Ok(DataValue::Null),
1086            _ => return Err(anyhow!("RIGHT expects a string as first argument")),
1087        };
1088
1089        let length = match &args[1] {
1090            DataValue::Integer(n) => *n as usize,
1091            DataValue::Float(f) => *f as usize,
1092            DataValue::Null => return Ok(DataValue::Null),
1093            _ => return Err(anyhow!("RIGHT expects a number as second argument")),
1094        };
1095
1096        let chars: Vec<char> = string.chars().collect();
1097        let start = if length >= chars.len() {
1098            0
1099        } else {
1100            chars.len() - length
1101        };
1102
1103        let result: String = chars[start..].iter().collect();
1104        Ok(DataValue::String(result))
1105    }
1106}
1107
1108/// SUBSTRING_BEFORE - returns substring before first/nth occurrence of delimiter
1109pub struct SubstringBeforeFunction;
1110
1111impl SqlFunction for SubstringBeforeFunction {
1112    fn signature(&self) -> FunctionSignature {
1113        FunctionSignature {
1114            name: "SUBSTRING_BEFORE",
1115            category: FunctionCategory::String,
1116            arg_count: ArgCount::Range(2, 3),
1117            description: "Returns substring before the first (or nth) occurrence of delimiter",
1118            returns: "STRING",
1119            examples: vec![
1120                "SELECT SUBSTRING_BEFORE(email, '@') FROM users", // Get username
1121                "SELECT SUBSTRING_BEFORE('a.b.c.d', '.', 2)",     // Get 'a.b' (before 2nd dot)
1122            ],
1123        }
1124    }
1125
1126    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1127        if args.len() < 2 || args.len() > 3 {
1128            return Err(anyhow!("SUBSTRING_BEFORE expects 2 or 3 arguments"));
1129        }
1130
1131        let string = match &args[0] {
1132            DataValue::String(s) => s.as_str(),
1133            DataValue::InternedString(s) => s.as_str(),
1134            DataValue::Null => return Ok(DataValue::Null),
1135            _ => {
1136                return Err(anyhow!(
1137                    "SUBSTRING_BEFORE expects a string as first argument"
1138                ))
1139            }
1140        };
1141
1142        let delimiter = match &args[1] {
1143            DataValue::String(s) => s.as_str(),
1144            DataValue::InternedString(s) => s.as_str(),
1145            DataValue::Null => return Ok(DataValue::Null),
1146            _ => return Err(anyhow!("SUBSTRING_BEFORE expects a string delimiter")),
1147        };
1148
1149        let occurrence = if args.len() == 3 {
1150            match &args[2] {
1151                DataValue::Integer(n) => *n as usize,
1152                DataValue::Float(f) => *f as usize,
1153                DataValue::Null => 1,
1154                _ => return Err(anyhow!("SUBSTRING_BEFORE expects a number for occurrence")),
1155            }
1156        } else {
1157            1
1158        };
1159
1160        if occurrence == 0 {
1161            return Ok(DataValue::String(String::new()));
1162        }
1163
1164        // Find the nth occurrence
1165        let mut count = 0;
1166        for (i, _) in string.match_indices(delimiter) {
1167            count += 1;
1168            if count == occurrence {
1169                return Ok(DataValue::String(string[..i].to_string()));
1170            }
1171        }
1172
1173        // If we didn't find enough occurrences, return empty string
1174        Ok(DataValue::String(String::new()))
1175    }
1176}
1177
1178/// SUBSTRING_AFTER - returns substring after first/nth occurrence of delimiter
1179pub struct SubstringAfterFunction;
1180
1181impl SqlFunction for SubstringAfterFunction {
1182    fn signature(&self) -> FunctionSignature {
1183        FunctionSignature {
1184            name: "SUBSTRING_AFTER",
1185            category: FunctionCategory::String,
1186            arg_count: ArgCount::Range(2, 3),
1187            description: "Returns substring after the first (or nth) occurrence of delimiter",
1188            returns: "STRING",
1189            examples: vec![
1190                "SELECT SUBSTRING_AFTER(email, '@') FROM users", // Get domain
1191                "SELECT SUBSTRING_AFTER('a.b.c.d', '.', 2)",     // Get 'c.d' (after 2nd dot)
1192            ],
1193        }
1194    }
1195
1196    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1197        if args.len() < 2 || args.len() > 3 {
1198            return Err(anyhow!("SUBSTRING_AFTER expects 2 or 3 arguments"));
1199        }
1200
1201        let string = match &args[0] {
1202            DataValue::String(s) => s.as_str(),
1203            DataValue::InternedString(s) => s.as_str(),
1204            DataValue::Null => return Ok(DataValue::Null),
1205            _ => {
1206                return Err(anyhow!(
1207                    "SUBSTRING_AFTER expects a string as first argument"
1208                ))
1209            }
1210        };
1211
1212        let delimiter = match &args[1] {
1213            DataValue::String(s) => s.as_str(),
1214            DataValue::InternedString(s) => s.as_str(),
1215            DataValue::Null => return Ok(DataValue::Null),
1216            _ => return Err(anyhow!("SUBSTRING_AFTER expects a string delimiter")),
1217        };
1218
1219        let occurrence = if args.len() == 3 {
1220            match &args[2] {
1221                DataValue::Integer(n) => *n as usize,
1222                DataValue::Float(f) => *f as usize,
1223                DataValue::Null => 1,
1224                _ => return Err(anyhow!("SUBSTRING_AFTER expects a number for occurrence")),
1225            }
1226        } else {
1227            1
1228        };
1229
1230        if occurrence == 0 {
1231            return Ok(DataValue::String(string.to_string()));
1232        }
1233
1234        // Find the nth occurrence
1235        let mut count = 0;
1236        for (i, _) in string.match_indices(delimiter) {
1237            count += 1;
1238            if count == occurrence {
1239                let start = i + delimiter.len();
1240                if start < string.len() {
1241                    return Ok(DataValue::String(string[start..].to_string()));
1242                } else {
1243                    return Ok(DataValue::String(String::new()));
1244                }
1245            }
1246        }
1247
1248        // If we didn't find enough occurrences, return empty string
1249        Ok(DataValue::String(String::new()))
1250    }
1251}
1252
1253/// SPLIT_PART - returns the nth part of a string split by delimiter (1-based)
1254pub struct SplitPartFunction;
1255
1256impl SqlFunction for SplitPartFunction {
1257    fn signature(&self) -> FunctionSignature {
1258        FunctionSignature {
1259            name: "SPLIT_PART",
1260            category: FunctionCategory::String,
1261            arg_count: ArgCount::Fixed(3),
1262            description: "Returns the nth part of a string split by delimiter (1-based index)",
1263            returns: "STRING",
1264            examples: vec![
1265                "SELECT SPLIT_PART('a.b.c.d', '.', 2)",        // Returns 'b'
1266                "SELECT SPLIT_PART(email, '@', 1) FROM users", // Get username
1267            ],
1268        }
1269    }
1270
1271    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1272        self.validate_args(args)?;
1273
1274        let string = match &args[0] {
1275            DataValue::String(s) => s.as_str(),
1276            DataValue::InternedString(s) => s.as_str(),
1277            DataValue::Null => return Ok(DataValue::Null),
1278            _ => return Err(anyhow!("SPLIT_PART expects a string as first argument")),
1279        };
1280
1281        let delimiter = match &args[1] {
1282            DataValue::String(s) => s.as_str(),
1283            DataValue::InternedString(s) => s.as_str(),
1284            DataValue::Null => return Ok(DataValue::Null),
1285            _ => return Err(anyhow!("SPLIT_PART expects a string delimiter")),
1286        };
1287
1288        let part_num = match &args[2] {
1289            DataValue::Integer(n) => *n as usize,
1290            DataValue::Float(f) => *f as usize,
1291            DataValue::Null => return Ok(DataValue::Null),
1292            _ => return Err(anyhow!("SPLIT_PART expects a number for part index")),
1293        };
1294
1295        if part_num == 0 {
1296            return Err(anyhow!("SPLIT_PART part index must be >= 1"));
1297        }
1298
1299        let parts: Vec<&str> = string.split(delimiter).collect();
1300
1301        if part_num <= parts.len() {
1302            Ok(DataValue::String(parts[part_num - 1].to_string()))
1303        } else {
1304            Ok(DataValue::String(String::new()))
1305        }
1306    }
1307}
1308
1309/// CHR function - Convert ASCII code to character
1310pub struct ChrFunction;
1311
1312impl SqlFunction for ChrFunction {
1313    fn signature(&self) -> FunctionSignature {
1314        FunctionSignature {
1315            name: "CHR",
1316            category: FunctionCategory::String,
1317            arg_count: ArgCount::Fixed(1),
1318            description: "Convert ASCII code to character",
1319            returns: "STRING",
1320            examples: vec![
1321                "SELECT CHR(65)", // Returns 'A'
1322                "SELECT CHR(97)", // Returns 'a'
1323                "SELECT CHR(48)", // Returns '0'
1324            ],
1325        }
1326    }
1327
1328    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1329        if args.len() != 1 {
1330            return Err(anyhow!("CHR expects exactly 1 argument"));
1331        }
1332
1333        let ascii_code = match &args[0] {
1334            DataValue::Integer(n) => *n,
1335            DataValue::Float(f) => *f as i64,
1336            DataValue::String(s) => s
1337                .parse::<i64>()
1338                .map_err(|_| anyhow!("Invalid number for CHR: {}", s))?,
1339            DataValue::InternedString(s) => s
1340                .parse::<i64>()
1341                .map_err(|_| anyhow!("Invalid number for CHR: {}", s))?,
1342            DataValue::Null => return Ok(DataValue::Null),
1343            _ => return Err(anyhow!("CHR expects a numeric argument")),
1344        };
1345
1346        // ASCII printable range is 32-126, but we'll allow 0-255
1347        if ascii_code < 0 || ascii_code > 255 {
1348            return Err(anyhow!(
1349                "CHR argument must be between 0 and 255, got {}",
1350                ascii_code
1351            ));
1352        }
1353
1354        let ch = ascii_code as u8 as char;
1355        Ok(DataValue::String(ch.to_string()))
1356    }
1357}
1358
1359/// LOREM_IPSUM function - generates Lorem Ipsum placeholder text
1360pub struct LoremIpsumFunction;
1361
1362impl SqlFunction for LoremIpsumFunction {
1363    fn signature(&self) -> FunctionSignature {
1364        FunctionSignature {
1365            name: "LOREM_IPSUM",
1366            category: FunctionCategory::String,
1367            arg_count: ArgCount::Range(1, 3),
1368            description: "Generate Lorem Ipsum placeholder text with specified number of words",
1369            returns: "STRING",
1370            examples: vec![
1371                "SELECT LOREM_IPSUM(10)",        // 10 random Lorem Ipsum words
1372                "SELECT LOREM_IPSUM(50)",        // 50 words
1373                "SELECT LOREM_IPSUM(20, 1)",     // 20 words, starting with 'Lorem ipsum...'
1374                "SELECT LOREM_IPSUM(15, 0, id)", // 15 words, use id as seed for variation
1375            ],
1376        }
1377    }
1378
1379    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1380        self.validate_args(args)?;
1381
1382        let num_words = match &args[0] {
1383            DataValue::Integer(n) if *n > 0 => *n as usize,
1384            DataValue::Float(f) if *f > 0.0 => *f as usize,
1385            DataValue::Null => return Ok(DataValue::Null),
1386            _ => return Err(anyhow!("LOREM_IPSUM requires a positive number of words")),
1387        };
1388
1389        // Check if we should start with traditional "Lorem ipsum..." opening
1390        let start_traditional = if args.len() > 1 {
1391            match &args[1] {
1392                DataValue::Integer(n) => *n != 0,
1393                DataValue::Boolean(b) => *b,
1394                _ => false,
1395            }
1396        } else {
1397            false
1398        };
1399
1400        // Get optional seed for reproducible but varied results
1401        let seed_value = if args.len() > 2 {
1402            match &args[2] {
1403                DataValue::Integer(n) => *n as u64,
1404                DataValue::Float(f) => *f as u64,
1405                DataValue::String(s) => {
1406                    // Hash the string to get a numeric seed
1407                    let mut hash = 0u64;
1408                    for byte in s.bytes() {
1409                        hash = hash.wrapping_mul(31).wrapping_add(byte as u64);
1410                    }
1411                    hash
1412                }
1413                DataValue::Null => 0,
1414                _ => 0,
1415            }
1416        } else {
1417            0
1418        };
1419
1420        // Lorem Ipsum word bank - traditional Latin placeholder text words
1421        const LOREM_WORDS: &[&str] = &[
1422            "lorem",
1423            "ipsum",
1424            "dolor",
1425            "sit",
1426            "amet",
1427            "consectetur",
1428            "adipiscing",
1429            "elit",
1430            "sed",
1431            "do",
1432            "eiusmod",
1433            "tempor",
1434            "incididunt",
1435            "ut",
1436            "labore",
1437            "et",
1438            "dolore",
1439            "magna",
1440            "aliqua",
1441            "enim",
1442            "ad",
1443            "minim",
1444            "veniam",
1445            "quis",
1446            "nostrud",
1447            "exercitation",
1448            "ullamco",
1449            "laboris",
1450            "nisi",
1451            "aliquip",
1452            "ex",
1453            "ea",
1454            "commodo",
1455            "consequat",
1456            "duis",
1457            "aute",
1458            "irure",
1459            "in",
1460            "reprehenderit",
1461            "voluptate",
1462            "velit",
1463            "esse",
1464            "cillum",
1465            "fugiat",
1466            "nulla",
1467            "pariatur",
1468            "excepteur",
1469            "sint",
1470            "occaecat",
1471            "cupidatat",
1472            "non",
1473            "proident",
1474            "sunt",
1475            "culpa",
1476            "qui",
1477            "officia",
1478            "deserunt",
1479            "mollit",
1480            "anim",
1481            "id",
1482            "est",
1483            "laborum",
1484            "perspiciatis",
1485            "unde",
1486            "omnis",
1487            "iste",
1488            "natus",
1489            "error",
1490            "voluptatem",
1491            "accusantium",
1492            "doloremque",
1493            "laudantium",
1494            "totam",
1495            "rem",
1496            "aperiam",
1497            "eaque",
1498            "ipsa",
1499            "quae",
1500            "ab",
1501            "illo",
1502            "inventore",
1503            "veritatis",
1504            "quasi",
1505            "architecto",
1506            "beatae",
1507            "vitae",
1508            "dicta",
1509            "explicabo",
1510            "nemo",
1511            "enim",
1512            "ipsam",
1513            "quia",
1514            "voluptas",
1515            "aspernatur",
1516            "aut",
1517            "odit",
1518            "fugit",
1519            "consequuntur",
1520            "magni",
1521            "dolores",
1522            "eos",
1523            "ratione",
1524            "sequi",
1525            "nesciunt",
1526            "neque",
1527            "porro",
1528            "quisquam",
1529            "dolorem",
1530            "adipisci",
1531            "numquam",
1532            "eius",
1533            "modi",
1534            "tempora",
1535            "incidunt",
1536            "magnam",
1537            "quaerat",
1538            "etiam",
1539            "minus",
1540            "soluta",
1541            "nobis",
1542            "eligendi",
1543            "optio",
1544            "cumque",
1545            "nihil",
1546            "impedit",
1547            "quo",
1548            "possimus",
1549            "suscipit",
1550            "laboriosam",
1551            "aliquid",
1552            "fuga",
1553            "distinctio",
1554            "libero",
1555            "tempore",
1556            "cum",
1557            "assumenda",
1558            "est",
1559            "omnis",
1560            "dolor",
1561            "repellendus",
1562            "temporibus",
1563            "autem",
1564            "quibusdam",
1565            "officiis",
1566            "debitis",
1567            "rerum",
1568            "necessitatibus",
1569            "saepe",
1570            "eveniet",
1571            "voluptates",
1572            "repudiandae",
1573            "molestiae",
1574            "recusandae",
1575            "itaque",
1576            "earum",
1577            "hic",
1578            "tenetur",
1579            "sapiente",
1580            "delectus",
1581            "reiciendis",
1582            "voluptatibus",
1583            "maiores",
1584            "alias",
1585            "consequatur",
1586            "perferendis",
1587            "doloribus",
1588            "asperiores",
1589            "repellat",
1590            "iusto",
1591            "odio",
1592            "dignissimos",
1593            "ducimus",
1594            "blanditiis",
1595            "praesentium",
1596            "voluptatum",
1597            "deleniti",
1598            "atque",
1599            "corrupti",
1600            "quos",
1601            "quas",
1602            "molestias",
1603            "excepturi",
1604            "occaecati",
1605            "provident",
1606            "similique",
1607            "mollitia",
1608            "animi",
1609            "illum",
1610            "dolorum",
1611            "fuga",
1612            "harum",
1613            "quidem",
1614            "rerum",
1615            "facilis",
1616            "expedita",
1617            "distinctio",
1618            "nam",
1619            "libero",
1620            "tempore",
1621            "cum",
1622            "soluta",
1623            "nobis",
1624            "eligendi",
1625            "optio",
1626            "cumque",
1627            "nihil",
1628            "impedit",
1629            "minus",
1630            "quod",
1631            "maxime",
1632            "placeat",
1633            "facere",
1634            "possimus",
1635            "omnis",
1636            "voluptas",
1637            "assumenda",
1638        ];
1639
1640        let mut result = Vec::with_capacity(num_words);
1641
1642        if start_traditional && num_words > 0 {
1643            // Start with traditional "Lorem ipsum dolor sit amet..."
1644            let traditional_start = ["lorem", "ipsum", "dolor", "sit", "amet"];
1645            let take_count = num_words.min(traditional_start.len());
1646            for i in 0..take_count {
1647                result.push(traditional_start[i]);
1648            }
1649
1650            // Fill remaining with random words
1651            let seed = if seed_value != 0 {
1652                seed_value
1653            } else {
1654                use std::time::{SystemTime, UNIX_EPOCH};
1655                SystemTime::now()
1656                    .duration_since(UNIX_EPOCH)
1657                    .unwrap_or_default()
1658                    .as_nanos() as u64
1659            };
1660
1661            let mut rng = seed.wrapping_mul(num_words as u64); // Combine seed with word count
1662            for i in take_count..num_words {
1663                // Simple pseudo-random selection
1664                rng = (rng.wrapping_mul(1664525).wrapping_add(1013904223)) ^ (i as u64);
1665                let idx = (rng as usize) % LOREM_WORDS.len();
1666                result.push(LOREM_WORDS[idx]);
1667            }
1668        } else {
1669            // Generate random Lorem words
1670            let seed = if seed_value != 0 {
1671                seed_value
1672            } else {
1673                use std::time::{SystemTime, UNIX_EPOCH};
1674                SystemTime::now()
1675                    .duration_since(UNIX_EPOCH)
1676                    .unwrap_or_default()
1677                    .as_nanos() as u64
1678            };
1679
1680            let mut rng = seed.wrapping_mul(num_words as u64).wrapping_add(12345); // Combine seed with word count
1681            for i in 0..num_words {
1682                // Simple pseudo-random selection with better entropy
1683                rng = (rng.wrapping_mul(1664525).wrapping_add(1013904223)) ^ (i as u64);
1684                let idx = (rng as usize) % LOREM_WORDS.len();
1685                result.push(LOREM_WORDS[idx]);
1686            }
1687        }
1688
1689        // Capitalize first word and add periods for readability
1690        let mut text = String::new();
1691        for (i, word) in result.iter().enumerate() {
1692            if i == 0 {
1693                // Capitalize first word
1694                text.push_str(&word.chars().next().unwrap().to_uppercase().to_string());
1695                text.push_str(&word[1..]);
1696            } else {
1697                text.push(' ');
1698                // Occasionally start a new sentence (roughly every 10-15 words)
1699                if i > 0 && ((i * 7) % 13 == 0) && i < num_words - 1 {
1700                    text.pop(); // Remove the space
1701                    text.push_str(". ");
1702                    // Capitalize next word
1703                    text.push_str(&word.chars().next().unwrap().to_uppercase().to_string());
1704                    text.push_str(&word[1..]);
1705                } else {
1706                    text.push_str(word);
1707                }
1708            }
1709        }
1710
1711        // Add final period if we generated text
1712        if !text.is_empty() {
1713            text.push('.');
1714        }
1715
1716        Ok(DataValue::String(text))
1717    }
1718}
1719
1720/// Register all string method functions
1721pub fn register_string_methods(registry: &mut super::FunctionRegistry) {
1722    use std::sync::Arc;
1723
1724    // Register new string functions (non-method versions)
1725    registry.register(Box::new(MidFunction));
1726    registry.register(Box::new(UpperFunction));
1727    registry.register(Box::new(LowerFunction));
1728    registry.register(Box::new(TrimFunction));
1729    registry.register(Box::new(TextJoinFunction));
1730    registry.register(Box::new(EditDistanceFunction));
1731    registry.register(Box::new(FrequencyFunction));
1732
1733    // Register new convenient string extraction functions
1734    registry.register(Box::new(LeftFunction));
1735    registry.register(Box::new(RightFunction));
1736    registry.register(Box::new(SubstringBeforeFunction));
1737    registry.register(Box::new(SubstringAfterFunction));
1738    registry.register(Box::new(SplitPartFunction));
1739
1740    // Register ToUpper
1741    let to_upper = Arc::new(ToUpperMethod);
1742    registry.register(Box::new(ToUpperMethod));
1743    registry.register_method(to_upper);
1744
1745    // Register ToLower
1746    let to_lower = Arc::new(ToLowerMethod);
1747    registry.register(Box::new(ToLowerMethod));
1748    registry.register_method(to_lower);
1749
1750    // Register Trim
1751    let trim = Arc::new(TrimMethod);
1752    registry.register(Box::new(TrimMethod));
1753    registry.register_method(trim);
1754
1755    // Register TrimStart
1756    let trim_start = Arc::new(TrimStartMethod);
1757    registry.register(Box::new(TrimStartMethod));
1758    registry.register_method(trim_start);
1759
1760    // Register TrimEnd
1761    let trim_end = Arc::new(TrimEndMethod);
1762    registry.register(Box::new(TrimEndMethod));
1763    registry.register_method(trim_end);
1764
1765    // Register Length
1766    let length = Arc::new(LengthMethod);
1767    registry.register(Box::new(LengthMethod));
1768    registry.register_method(length);
1769
1770    // Register Contains
1771    let contains = Arc::new(ContainsMethod);
1772    registry.register(Box::new(ContainsMethod));
1773    registry.register_method(contains);
1774
1775    // Register StartsWith
1776    let starts_with = Arc::new(StartsWithMethod);
1777    registry.register(Box::new(StartsWithMethod));
1778    registry.register_method(starts_with);
1779
1780    // Register EndsWith
1781    let ends_with = Arc::new(EndsWithMethod);
1782    registry.register(Box::new(EndsWithMethod));
1783    registry.register_method(ends_with);
1784
1785    // Register Substring
1786    let substring = Arc::new(SubstringMethod);
1787    registry.register(Box::new(SubstringMethod));
1788    registry.register_method(substring);
1789
1790    // Register Replace
1791    let replace = Arc::new(ReplaceMethod);
1792    registry.register(Box::new(ReplaceMethod));
1793    registry.register_method(replace);
1794
1795    // Register IndexOf/INSTR
1796    let indexof = Arc::new(IndexOfMethod);
1797    registry.register(Box::new(IndexOfMethod));
1798    registry.register_method(indexof.clone());
1799    // Also register as INSTR for SQL compatibility
1800    registry.register(Box::new(InstrFunction));
1801
1802    // Register CHR function
1803    registry.register(Box::new(ChrFunction));
1804
1805    // Register LOREM_IPSUM function
1806    registry.register(Box::new(LoremIpsumFunction));
1807}