1#![allow(
8 clippy::unnecessary_literal_bound,
9 clippy::too_many_lines,
10 clippy::cast_possible_truncation,
11 clippy::cast_possible_wrap,
12 clippy::cast_sign_loss,
13 clippy::fn_params_excessive_bools,
14 clippy::items_after_statements,
15 clippy::match_same_arms,
16 clippy::single_match_else,
17 clippy::manual_let_else,
18 clippy::comparison_chain,
19 clippy::suboptimal_flops,
20 clippy::unnecessary_wraps,
21 clippy::useless_let_if_seq,
22 clippy::redundant_closure_for_method_calls,
23 clippy::manual_ignore_case_cmp
24)]
25
26use std::fmt::Write as _;
27
28use fsqlite_error::{FrankenError, Result};
29use fsqlite_types::SqliteValue;
30
31use crate::agg_builtins::register_aggregate_builtins;
32use crate::datetime::register_datetime_builtins;
33use crate::math::register_math_builtins;
34use crate::{FunctionRegistry, ScalarFunction};
35
36thread_local! {
40 static LAST_INSERT_ROWID: std::cell::Cell<i64> = const { std::cell::Cell::new(0) };
41 static LAST_CHANGES: std::cell::Cell<i64> = const { std::cell::Cell::new(0) };
42}
43
44pub fn set_last_insert_rowid(rowid: i64) {
46 LAST_INSERT_ROWID.set(rowid);
47}
48
49pub fn get_last_insert_rowid() -> i64 {
51 LAST_INSERT_ROWID.get()
52}
53
54pub fn set_last_changes(count: i64) {
56 LAST_CHANGES.set(count);
57}
58
59pub fn get_last_changes() -> i64 {
61 LAST_CHANGES.get()
62}
63
64fn null_propagate(args: &[SqliteValue]) -> Option<SqliteValue> {
68 if args.iter().any(SqliteValue::is_null) {
69 Some(SqliteValue::Null)
70 } else {
71 None
72 }
73}
74
75fn coerce_numeric(v: &SqliteValue) -> SqliteValue {
77 match v {
78 SqliteValue::Integer(_) | SqliteValue::Float(_) => v.clone(),
79 SqliteValue::Text(s) => {
80 if let Ok(i) = s.parse::<i64>() {
81 SqliteValue::Integer(i)
82 } else if let Ok(f) = s.parse::<f64>() {
83 SqliteValue::Float(f)
84 } else {
85 SqliteValue::Integer(0)
86 }
87 }
88 SqliteValue::Null => SqliteValue::Null,
89 SqliteValue::Blob(_) => SqliteValue::Integer(0),
90 }
91}
92
93pub struct AbsFunc;
96
97impl ScalarFunction for AbsFunc {
98 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
99 if args[0].is_null() {
100 return Ok(SqliteValue::Null);
101 }
102 match coerce_numeric(&args[0]) {
103 SqliteValue::Integer(i) => {
104 if i == i64::MIN {
105 return Err(FrankenError::IntegerOverflow);
106 }
107 Ok(SqliteValue::Integer(i.abs()))
108 }
109 SqliteValue::Float(f) => Ok(SqliteValue::Float(f.abs())),
110 other => Ok(other),
111 }
112 }
113
114 fn num_args(&self) -> i32 {
115 1
116 }
117
118 fn name(&self) -> &str {
119 "abs"
120 }
121}
122
123pub struct CharFunc;
126
127impl ScalarFunction for CharFunc {
128 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
129 let mut result = String::new();
130 for arg in args {
131 if arg.is_null() {
133 continue;
134 }
135 #[allow(clippy::cast_sign_loss)]
136 let cp = arg.to_integer() as u32;
137 if let Some(c) = char::from_u32(cp) {
138 result.push(c);
139 }
140 }
141 Ok(SqliteValue::Text(result))
142 }
143
144 fn is_deterministic(&self) -> bool {
145 true
146 }
147
148 fn num_args(&self) -> i32 {
149 -1 }
151
152 fn name(&self) -> &str {
153 "char"
154 }
155}
156
157pub struct CoalesceFunc;
160
161impl ScalarFunction for CoalesceFunc {
162 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
163 for arg in args {
167 if !arg.is_null() {
168 return Ok(arg.clone());
169 }
170 }
171 Ok(SqliteValue::Null)
172 }
173
174 fn num_args(&self) -> i32 {
175 -1
176 }
177
178 fn name(&self) -> &str {
179 "coalesce"
180 }
181}
182
183pub struct ConcatFunc;
186
187impl ScalarFunction for ConcatFunc {
188 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
189 let mut result = String::new();
190 for arg in args {
191 if !arg.is_null() {
193 result.push_str(&arg.to_text());
194 }
195 }
196 Ok(SqliteValue::Text(result))
197 }
198
199 fn num_args(&self) -> i32 {
200 -1
201 }
202
203 fn name(&self) -> &str {
204 "concat"
205 }
206}
207
208pub struct ConcatWsFunc;
211
212impl ScalarFunction for ConcatWsFunc {
213 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
214 if args.is_empty() {
215 return Ok(SqliteValue::Text(String::new()));
216 }
217 let sep = if args[0].is_null() {
218 String::new()
219 } else {
220 args[0].to_text()
221 };
222 let mut parts = Vec::new();
223 for arg in &args[1..] {
224 if !arg.is_null() {
226 parts.push(arg.to_text());
227 }
228 }
229 Ok(SqliteValue::Text(parts.join(&sep)))
230 }
231
232 fn num_args(&self) -> i32 {
233 -1
234 }
235
236 fn name(&self) -> &str {
237 "concat_ws"
238 }
239}
240
241pub struct HexFunc;
244
245impl ScalarFunction for HexFunc {
246 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
247 if args[0].is_null() {
248 return Ok(SqliteValue::Null);
249 }
250 let bytes = match &args[0] {
251 SqliteValue::Blob(b) => b.clone(),
252 other => other.to_text().into_bytes(),
254 };
255 let mut hex = String::with_capacity(bytes.len() * 2);
256 for b in &bytes {
257 let _ = write!(hex, "{b:02X}");
258 }
259 Ok(SqliteValue::Text(hex))
260 }
261
262 fn num_args(&self) -> i32 {
263 1
264 }
265
266 fn name(&self) -> &str {
267 "hex"
268 }
269}
270
271pub struct IfnullFunc;
274
275impl ScalarFunction for IfnullFunc {
276 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
277 if args[0].is_null() {
278 Ok(args[1].clone())
279 } else {
280 Ok(args[0].clone())
281 }
282 }
283
284 fn num_args(&self) -> i32 {
285 2
286 }
287
288 fn name(&self) -> &str {
289 "ifnull"
290 }
291}
292
293pub struct IifFunc;
296
297impl ScalarFunction for IifFunc {
298 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
299 let cond = &args[0];
301 let is_true = match cond {
302 SqliteValue::Null => false,
303 SqliteValue::Integer(i) => *i != 0,
304 SqliteValue::Float(f) => *f != 0.0,
305 SqliteValue::Text(s) => s.parse::<f64>().is_ok_and(|f| f != 0.0),
306 SqliteValue::Blob(_) => false,
307 };
308 if is_true {
309 Ok(args[1].clone())
310 } else if args.len() > 2 {
311 Ok(args[2].clone())
312 } else {
313 Ok(SqliteValue::Null)
315 }
316 }
317
318 fn num_args(&self) -> i32 {
319 -1 }
321
322 fn name(&self) -> &str {
323 "iif"
324 }
325}
326
327pub struct InstrFunc;
330
331impl ScalarFunction for InstrFunc {
332 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
333 if let Some(null) = null_propagate(args) {
334 return Ok(null);
335 }
336 match (&args[0], &args[1]) {
337 (SqliteValue::Blob(haystack), SqliteValue::Blob(needle)) => {
338 let pos = find_bytes(haystack, needle).map_or(0, |p| p + 1);
340 Ok(SqliteValue::Integer(i64::try_from(pos).unwrap_or(0)))
341 }
342 _ => {
343 let haystack = args[0].to_text();
345 let needle = args[1].to_text();
346 let pos = haystack
347 .find(&needle)
348 .map_or(0, |byte_pos| haystack[..byte_pos].chars().count() + 1);
349 Ok(SqliteValue::Integer(i64::try_from(pos).unwrap_or(0)))
350 }
351 }
352 }
353
354 fn num_args(&self) -> i32 {
355 2
356 }
357
358 fn name(&self) -> &str {
359 "instr"
360 }
361}
362
363fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
364 if needle.is_empty() {
365 return Some(0);
366 }
367 haystack.windows(needle.len()).position(|w| w == needle)
368}
369
370pub struct LengthFunc;
373
374impl ScalarFunction for LengthFunc {
375 #[allow(clippy::cast_possible_wrap)]
376 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
377 if args[0].is_null() {
378 return Ok(SqliteValue::Null);
379 }
380 let len = match &args[0] {
381 SqliteValue::Text(s) => s.chars().count(),
382 SqliteValue::Blob(b) => b.len(),
383 other => other.to_text().chars().count(),
385 };
386 Ok(SqliteValue::Integer(len as i64))
387 }
388
389 fn num_args(&self) -> i32 {
390 1
391 }
392
393 fn name(&self) -> &str {
394 "length"
395 }
396}
397
398pub struct OctetLengthFunc;
401
402impl ScalarFunction for OctetLengthFunc {
403 #[allow(clippy::cast_possible_wrap)]
404 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
405 if args[0].is_null() {
406 return Ok(SqliteValue::Null);
407 }
408 let len = match &args[0] {
409 SqliteValue::Text(s) => s.len(),
410 SqliteValue::Blob(b) => b.len(),
411 other => other.to_text().len(),
412 };
413 Ok(SqliteValue::Integer(len as i64))
414 }
415
416 fn num_args(&self) -> i32 {
417 1
418 }
419
420 fn name(&self) -> &str {
421 "octet_length"
422 }
423}
424
425pub struct LowerFunc;
428
429impl ScalarFunction for LowerFunc {
430 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
431 if args[0].is_null() {
432 return Ok(SqliteValue::Null);
433 }
434 Ok(SqliteValue::Text(args[0].to_text().to_ascii_lowercase()))
435 }
436
437 fn num_args(&self) -> i32 {
438 1
439 }
440
441 fn name(&self) -> &str {
442 "lower"
443 }
444}
445
446pub struct UpperFunc;
447
448impl ScalarFunction for UpperFunc {
449 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
450 if args[0].is_null() {
451 return Ok(SqliteValue::Null);
452 }
453 Ok(SqliteValue::Text(args[0].to_text().to_ascii_uppercase()))
454 }
455
456 fn num_args(&self) -> i32 {
457 1
458 }
459
460 fn name(&self) -> &str {
461 "upper"
462 }
463}
464
465pub struct TrimFunc;
468pub struct LtrimFunc;
469pub struct RtrimFunc;
470
471fn trim_chars(s: &str, chars: &str) -> String {
472 let char_set: Vec<char> = chars.chars().collect();
473 s.trim_matches(|c: char| char_set.contains(&c)).to_owned()
474}
475
476fn ltrim_chars(s: &str, chars: &str) -> String {
477 let char_set: Vec<char> = chars.chars().collect();
478 s.trim_start_matches(|c: char| char_set.contains(&c))
479 .to_owned()
480}
481
482fn rtrim_chars(s: &str, chars: &str) -> String {
483 let char_set: Vec<char> = chars.chars().collect();
484 s.trim_end_matches(|c: char| char_set.contains(&c))
485 .to_owned()
486}
487
488impl ScalarFunction for TrimFunc {
489 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
490 if args[0].is_null() {
491 return Ok(SqliteValue::Null);
492 }
493 let s = args[0].to_text();
494 let chars = if args.len() > 1 && !args[1].is_null() {
495 args[1].to_text()
496 } else {
497 " ".to_owned()
498 };
499 Ok(SqliteValue::Text(trim_chars(&s, &chars)))
500 }
501
502 fn num_args(&self) -> i32 {
503 -1 }
505
506 fn name(&self) -> &str {
507 "trim"
508 }
509}
510
511impl ScalarFunction for LtrimFunc {
512 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
513 if args[0].is_null() {
514 return Ok(SqliteValue::Null);
515 }
516 let s = args[0].to_text();
517 let chars = if args.len() > 1 && !args[1].is_null() {
518 args[1].to_text()
519 } else {
520 " ".to_owned()
521 };
522 Ok(SqliteValue::Text(ltrim_chars(&s, &chars)))
523 }
524
525 fn num_args(&self) -> i32 {
526 -1
527 }
528
529 fn name(&self) -> &str {
530 "ltrim"
531 }
532}
533
534impl ScalarFunction for RtrimFunc {
535 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
536 if args[0].is_null() {
537 return Ok(SqliteValue::Null);
538 }
539 let s = args[0].to_text();
540 let chars = if args.len() > 1 && !args[1].is_null() {
541 args[1].to_text()
542 } else {
543 " ".to_owned()
544 };
545 Ok(SqliteValue::Text(rtrim_chars(&s, &chars)))
546 }
547
548 fn num_args(&self) -> i32 {
549 -1
550 }
551
552 fn name(&self) -> &str {
553 "rtrim"
554 }
555}
556
557pub struct NullifFunc;
560
561impl ScalarFunction for NullifFunc {
562 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
563 if args[0] == args[1] {
564 Ok(SqliteValue::Null)
565 } else {
566 Ok(args[0].clone())
567 }
568 }
569
570 fn num_args(&self) -> i32 {
571 2
572 }
573
574 fn name(&self) -> &str {
575 "nullif"
576 }
577}
578
579pub struct TypeofFunc;
582
583impl ScalarFunction for TypeofFunc {
584 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
585 let type_name = match &args[0] {
586 SqliteValue::Null => "null",
587 SqliteValue::Integer(_) => "integer",
588 SqliteValue::Float(_) => "real",
589 SqliteValue::Text(_) => "text",
590 SqliteValue::Blob(_) => "blob",
591 };
592 Ok(SqliteValue::Text(type_name.to_owned()))
593 }
594
595 fn num_args(&self) -> i32 {
596 1
597 }
598
599 fn name(&self) -> &str {
600 "typeof"
601 }
602}
603
604pub struct SubtypeFunc;
607
608impl ScalarFunction for SubtypeFunc {
609 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
610 Ok(SqliteValue::Integer(0))
613 }
614
615 fn num_args(&self) -> i32 {
616 1
617 }
618
619 fn name(&self) -> &str {
620 "subtype"
621 }
622}
623
624pub struct ReplaceFunc;
627
628impl ScalarFunction for ReplaceFunc {
629 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
630 if let Some(null) = null_propagate(args) {
631 return Ok(null);
632 }
633 let x = args[0].to_text();
634 let y = args[1].to_text();
635 let z = args[2].to_text();
636 if y.is_empty() {
637 return Ok(SqliteValue::Text(x));
638 }
639 Ok(SqliteValue::Text(x.replace(&y, &z)))
640 }
641
642 fn num_args(&self) -> i32 {
643 3
644 }
645
646 fn name(&self) -> &str {
647 "replace"
648 }
649}
650
651pub struct RoundFunc;
654
655impl ScalarFunction for RoundFunc {
656 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
657 if args[0].is_null() {
658 return Ok(SqliteValue::Null);
659 }
660 let x = args[0].to_float();
661 let n = if args.len() > 1 && !args[1].is_null() {
663 args[1].to_integer().clamp(0, 30)
664 } else {
665 0
666 };
667 if !(-4_503_599_627_370_496.0..=4_503_599_627_370_496.0).contains(&x) {
669 return Ok(SqliteValue::Float(x));
670 }
671 #[allow(clippy::cast_possible_truncation)]
674 let factor = 10.0_f64.powi(n as i32);
675 let rounded = if x >= 0.0 {
676 (x * factor + 0.5).floor() / factor
677 } else {
678 (x * factor - 0.5).ceil() / factor
679 };
680 Ok(SqliteValue::Float(rounded))
681 }
682
683 fn num_args(&self) -> i32 {
684 -1 }
686
687 fn name(&self) -> &str {
688 "round"
689 }
690}
691
692pub struct SignFunc;
695
696impl ScalarFunction for SignFunc {
697 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
698 if args[0].is_null() {
699 return Ok(SqliteValue::Null);
700 }
701 match &args[0] {
702 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(i.signum())),
703 SqliteValue::Float(f) => {
704 if *f > 0.0 {
705 Ok(SqliteValue::Integer(1))
706 } else if *f < 0.0 {
707 Ok(SqliteValue::Integer(-1))
708 } else {
709 Ok(SqliteValue::Integer(0))
710 }
711 }
712 SqliteValue::Text(s) => {
713 if let Ok(i) = s.parse::<i64>() {
715 Ok(SqliteValue::Integer(i.signum()))
716 } else if let Ok(f) = s.parse::<f64>() {
717 if f > 0.0 {
718 Ok(SqliteValue::Integer(1))
719 } else if f < 0.0 {
720 Ok(SqliteValue::Integer(-1))
721 } else {
722 Ok(SqliteValue::Integer(0))
723 }
724 } else {
725 Ok(SqliteValue::Null)
726 }
727 }
728 SqliteValue::Blob(_) => Ok(SqliteValue::Null),
729 SqliteValue::Null => Ok(SqliteValue::Null),
730 }
731 }
732
733 fn num_args(&self) -> i32 {
734 1
735 }
736
737 fn name(&self) -> &str {
738 "sign"
739 }
740}
741
742pub struct RandomFunc;
745
746impl ScalarFunction for RandomFunc {
747 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
748 let val = simple_random_i64();
751 Ok(SqliteValue::Integer(val))
752 }
753
754 fn is_deterministic(&self) -> bool {
755 false
756 }
757
758 fn num_args(&self) -> i32 {
759 0
760 }
761
762 fn name(&self) -> &str {
763 "random"
764 }
765}
766
767fn simple_random_i64() -> i64 {
769 use std::sync::atomic::{AtomicU64, Ordering};
774
775 static STATE: AtomicU64 = AtomicU64::new(0xD1B5_4A32_D192_ED03);
776 let mut x = STATE.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed);
777 x ^= x >> 30;
778 x = x.wrapping_mul(0xBF58_476D_1CE4_E5B9);
779 x ^= x >> 27;
780 x = x.wrapping_mul(0x94D0_49BB_1331_11EB);
781 x ^= x >> 31;
782 x as i64
783}
784
785pub struct RandomblobFunc;
788
789impl ScalarFunction for RandomblobFunc {
790 #[allow(clippy::cast_sign_loss)]
791 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
792 if args[0].is_null() {
793 return Ok(SqliteValue::Null);
794 }
795 let n = args[0].to_integer().max(0) as usize;
796 let mut buf = vec![0u8; n];
797 let mut i = 0;
798 while i < n {
799 let rnd = simple_random_i64().to_ne_bytes();
800 let to_copy = (n - i).min(8);
801 buf[i..i + to_copy].copy_from_slice(&rnd[..to_copy]);
802 i += to_copy;
803 }
804 Ok(SqliteValue::Blob(buf))
805 }
806
807 fn is_deterministic(&self) -> bool {
808 false
809 }
810
811 fn num_args(&self) -> i32 {
812 1
813 }
814
815 fn name(&self) -> &str {
816 "randomblob"
817 }
818}
819
820pub struct ZeroblobFunc;
823
824impl ScalarFunction for ZeroblobFunc {
825 #[allow(clippy::cast_sign_loss)]
826 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
827 if args[0].is_null() {
828 return Ok(SqliteValue::Null);
829 }
830 let n = args[0].to_integer().max(0) as usize;
831 Ok(SqliteValue::Blob(vec![0u8; n]))
832 }
833
834 fn num_args(&self) -> i32 {
835 1
836 }
837
838 fn name(&self) -> &str {
839 "zeroblob"
840 }
841}
842
843pub struct QuoteFunc;
846
847impl ScalarFunction for QuoteFunc {
848 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
849 let result = match &args[0] {
850 SqliteValue::Null => "NULL".to_owned(),
851 SqliteValue::Integer(i) => i.to_string(),
852 SqliteValue::Float(f) => format!("{f}"),
853 SqliteValue::Text(s) => {
854 let escaped = s.replace('\'', "''");
855 format!("'{escaped}'")
856 }
857 SqliteValue::Blob(b) => {
858 let mut hex = String::with_capacity(3 + b.len() * 2);
859 hex.push_str("X'");
860 for byte in b {
861 let _ = write!(hex, "{byte:02X}");
862 }
863 hex.push('\'');
864 hex
865 }
866 };
867 Ok(SqliteValue::Text(result))
868 }
869
870 fn num_args(&self) -> i32 {
871 1
872 }
873
874 fn name(&self) -> &str {
875 "quote"
876 }
877}
878
879pub struct UnhexFunc;
882
883impl ScalarFunction for UnhexFunc {
884 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
885 if args[0].is_null() {
886 return Ok(SqliteValue::Null);
887 }
888 let input = args[0].to_text();
889 let ignore_chars: Vec<char> = if args.len() > 1 && !args[1].is_null() {
890 args[1].to_text().chars().collect()
891 } else {
892 Vec::new()
893 };
894
895 let filtered: String = input
897 .chars()
898 .filter(|c| !ignore_chars.contains(c))
899 .collect();
900
901 if filtered.len() % 2 != 0 {
903 return Ok(SqliteValue::Null);
904 }
905
906 let mut bytes = Vec::with_capacity(filtered.len() / 2);
907 let chars: Vec<char> = filtered.chars().collect();
908 let mut i = 0;
909 while i < chars.len() {
910 let hi = match hex_digit(chars[i]) {
911 Some(v) => v,
912 None => return Ok(SqliteValue::Null),
913 };
914 let lo = match hex_digit(chars[i + 1]) {
915 Some(v) => v,
916 None => return Ok(SqliteValue::Null),
917 };
918 bytes.push(hi << 4 | lo);
919 i += 2;
920 }
921 Ok(SqliteValue::Blob(bytes))
922 }
923
924 fn num_args(&self) -> i32 {
925 -1 }
927
928 fn name(&self) -> &str {
929 "unhex"
930 }
931}
932
933fn hex_digit(c: char) -> Option<u8> {
934 match c {
935 '0'..='9' => Some(c as u8 - b'0'),
936 'a'..='f' => Some(c as u8 - b'a' + 10),
937 'A'..='F' => Some(c as u8 - b'A' + 10),
938 _ => None,
939 }
940}
941
942pub struct UnicodeFunc;
945
946impl ScalarFunction for UnicodeFunc {
947 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
948 if args[0].is_null() {
949 return Ok(SqliteValue::Null);
950 }
951 let s = args[0].to_text();
952 match s.chars().next() {
953 Some(c) => Ok(SqliteValue::Integer(i64::from(c as u32))),
954 None => Ok(SqliteValue::Null),
955 }
956 }
957
958 fn num_args(&self) -> i32 {
959 1
960 }
961
962 fn name(&self) -> &str {
963 "unicode"
964 }
965}
966
967pub struct SubstrFunc;
970
971impl ScalarFunction for SubstrFunc {
972 #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
973 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
974 if args[0].is_null() {
975 return Ok(SqliteValue::Null);
976 }
977 let is_blob = matches!(&args[0], SqliteValue::Blob(_));
978 if is_blob {
979 return self.invoke_blob(args);
980 }
981
982 let s = args[0].to_text();
983 let chars: Vec<char> = s.chars().collect();
984 let char_count = chars.len() as i64;
985 let start = args[1].to_integer();
986 let has_length = args.len() > 2 && !args[2].is_null();
987 let length = if has_length {
988 args[2].to_integer()
989 } else {
990 char_count + 1
991 };
992
993 if length < 0 {
999 let abs_len = length.unsigned_abs();
1001 let end_pos = if start > 0 {
1002 (start - 1).min(char_count)
1003 } else if start == 0 {
1004 0
1005 } else {
1006 (char_count + start + 1).max(0).min(char_count)
1007 };
1008 let start_pos = (end_pos - abs_len as i64).max(0);
1009 let result: String = chars[start_pos as usize..end_pos as usize].iter().collect();
1010 return Ok(SqliteValue::Text(result));
1011 }
1012
1013 let (begin, len) = if start > 0 {
1014 ((start - 1).max(0) as usize, length as usize)
1015 } else if start == 0 {
1016 (0, (length - 1).max(0) as usize)
1018 } else {
1019 let effective = char_count + start; if effective < 0 {
1022 let skip = effective.unsigned_abs() as usize;
1023 (0, length as usize - skip.min(length as usize))
1024 } else {
1025 (effective as usize, length as usize)
1026 }
1027 };
1028
1029 let result: String = chars.iter().skip(begin).take(len).collect();
1030 Ok(SqliteValue::Text(result))
1031 }
1032
1033 fn num_args(&self) -> i32 {
1034 -1 }
1036
1037 fn name(&self) -> &str {
1038 "substr"
1039 }
1040}
1041
1042impl SubstrFunc {
1043 #[allow(clippy::unused_self)]
1044 fn invoke_blob(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1045 let blob = match &args[0] {
1046 SqliteValue::Blob(b) => b,
1047 _ => return Ok(SqliteValue::Null),
1048 };
1049 let blob_len = blob.len() as i64;
1050 let start = args[1].to_integer();
1051 let has_length = args.len() > 2 && !args[2].is_null();
1052 let length = if has_length {
1053 args[2].to_integer()
1054 } else {
1055 blob_len + 1
1056 };
1057
1058 if length < 0 {
1059 let abs_len = length.unsigned_abs();
1060 let end_pos = if start > 0 {
1061 (start - 1).min(blob_len)
1062 } else if start == 0 {
1063 0
1064 } else {
1065 (blob_len + start + 1).max(0).min(blob_len)
1066 };
1067 let start_pos = (end_pos - abs_len as i64).max(0);
1068 return Ok(SqliteValue::Blob(
1069 blob[start_pos as usize..end_pos as usize].to_vec(),
1070 ));
1071 }
1072
1073 let (begin, len) = if start > 0 {
1074 ((start - 1).max(0) as usize, length as usize)
1075 } else if start == 0 {
1076 (0, (length - 1).max(0) as usize)
1077 } else {
1078 let effective = blob_len + start;
1079 if effective < 0 {
1080 let skip = effective.unsigned_abs() as usize;
1081 (0, length as usize - skip.min(length as usize))
1082 } else {
1083 (effective as usize, length as usize)
1084 }
1085 };
1086
1087 let end = (begin + len).min(blob.len());
1088 Ok(SqliteValue::Blob(blob[begin..end].to_vec()))
1089 }
1090}
1091
1092pub struct SoundexFunc;
1095
1096impl ScalarFunction for SoundexFunc {
1097 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1098 if args[0].is_null() {
1099 return Ok(SqliteValue::Text("?000".to_owned()));
1101 }
1102 let s = args[0].to_text();
1103 Ok(SqliteValue::Text(soundex(&s)))
1104 }
1105
1106 fn num_args(&self) -> i32 {
1107 1
1108 }
1109
1110 fn name(&self) -> &str {
1111 "soundex"
1112 }
1113}
1114
1115fn soundex(s: &str) -> String {
1116 let mut chars = s.chars().filter(|c| c.is_ascii_alphabetic());
1117 let first = match chars.next() {
1118 Some(c) => c.to_ascii_uppercase(),
1119 None => return "?000".to_owned(),
1120 };
1121
1122 let code = |c: char| -> Option<char> {
1123 match c.to_ascii_uppercase() {
1124 'B' | 'F' | 'P' | 'V' => Some('1'),
1125 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => Some('2'),
1126 'D' | 'T' => Some('3'),
1127 'L' => Some('4'),
1128 'M' | 'N' => Some('5'),
1129 'R' => Some('6'),
1130 _ => None, }
1132 };
1133
1134 let mut result = String::with_capacity(4);
1135 result.push(first);
1136 let mut last_code = code(first);
1137
1138 for c in chars {
1139 if result.len() >= 4 {
1140 break;
1141 }
1142 let current = code(c);
1143 if let Some(digit) = current {
1144 if current != last_code {
1145 result.push(digit);
1146 }
1147 }
1148 last_code = current;
1149 }
1150
1151 while result.len() < 4 {
1152 result.push('0');
1153 }
1154 result
1155}
1156
1157pub struct ScalarMaxFunc;
1160
1161impl ScalarFunction for ScalarMaxFunc {
1162 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1163 if let Some(null) = null_propagate(args) {
1165 return Ok(null);
1166 }
1167 let mut max = &args[0];
1168 for arg in &args[1..] {
1169 if arg.partial_cmp(max) == Some(std::cmp::Ordering::Greater) {
1170 max = arg;
1171 }
1172 }
1173 Ok(max.clone())
1174 }
1175
1176 fn num_args(&self) -> i32 {
1177 -1
1178 }
1179
1180 fn name(&self) -> &str {
1181 "max"
1182 }
1183}
1184
1185pub struct ScalarMinFunc;
1188
1189impl ScalarFunction for ScalarMinFunc {
1190 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1191 if let Some(null) = null_propagate(args) {
1193 return Ok(null);
1194 }
1195 let mut min = &args[0];
1196 for arg in &args[1..] {
1197 if arg.partial_cmp(min) == Some(std::cmp::Ordering::Less) {
1198 min = arg;
1199 }
1200 }
1201 Ok(min.clone())
1202 }
1203
1204 fn num_args(&self) -> i32 {
1205 -1
1206 }
1207
1208 fn name(&self) -> &str {
1209 "min"
1210 }
1211}
1212
1213pub struct LikelihoodFunc;
1216
1217impl ScalarFunction for LikelihoodFunc {
1218 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1219 Ok(args[0].clone())
1221 }
1222
1223 fn num_args(&self) -> i32 {
1224 2
1225 }
1226
1227 fn name(&self) -> &str {
1228 "likelihood"
1229 }
1230}
1231
1232pub struct LikelyFunc;
1233
1234impl ScalarFunction for LikelyFunc {
1235 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1236 Ok(args[0].clone())
1237 }
1238
1239 fn num_args(&self) -> i32 {
1240 1
1241 }
1242
1243 fn name(&self) -> &str {
1244 "likely"
1245 }
1246}
1247
1248pub struct UnlikelyFunc;
1249
1250impl ScalarFunction for UnlikelyFunc {
1251 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1252 Ok(args[0].clone())
1253 }
1254
1255 fn num_args(&self) -> i32 {
1256 1
1257 }
1258
1259 fn name(&self) -> &str {
1260 "unlikely"
1261 }
1262}
1263
1264pub struct SqliteVersionFunc;
1267
1268impl ScalarFunction for SqliteVersionFunc {
1269 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1270 Ok(SqliteValue::Text("3.52.0".to_owned()))
1271 }
1272
1273 fn num_args(&self) -> i32 {
1274 0
1275 }
1276
1277 fn name(&self) -> &str {
1278 "sqlite_version"
1279 }
1280}
1281
1282pub struct SqliteSourceIdFunc;
1285
1286impl ScalarFunction for SqliteSourceIdFunc {
1287 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1288 Ok(SqliteValue::Text(
1289 "FrankenSQLite 0.1.0 (compatible with SQLite 3.52.0)".to_owned(),
1290 ))
1291 }
1292
1293 fn num_args(&self) -> i32 {
1294 0
1295 }
1296
1297 fn name(&self) -> &str {
1298 "sqlite_source_id"
1299 }
1300}
1301
1302pub struct SqliteCompileoptionUsedFunc;
1305
1306impl ScalarFunction for SqliteCompileoptionUsedFunc {
1307 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1308 if args[0].is_null() {
1309 return Ok(SqliteValue::Null);
1310 }
1311 let opt = args[0].to_text().to_ascii_uppercase();
1312 let known = matches!(
1314 opt.as_str(),
1315 "THREADSAFE" | "ENABLE_FTS5" | "ENABLE_JSON1" | "ENABLE_RTREE"
1316 );
1317 Ok(SqliteValue::Integer(i64::from(known)))
1318 }
1319
1320 fn num_args(&self) -> i32 {
1321 1
1322 }
1323
1324 fn name(&self) -> &str {
1325 "sqlite_compileoption_used"
1326 }
1327}
1328
1329pub struct SqliteCompileoptionGetFunc;
1332
1333impl ScalarFunction for SqliteCompileoptionGetFunc {
1334 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1335 let n = args[0].to_integer();
1336 let options = [
1337 "THREADSAFE=1",
1338 "ENABLE_FTS5",
1339 "ENABLE_JSON1",
1340 "ENABLE_RTREE",
1341 ];
1342 #[allow(clippy::cast_sign_loss)]
1343 match options.get(n as usize) {
1344 Some(opt) => Ok(SqliteValue::Text((*opt).to_owned())),
1345 None => Ok(SqliteValue::Null),
1346 }
1347 }
1348
1349 fn num_args(&self) -> i32 {
1350 1
1351 }
1352
1353 fn name(&self) -> &str {
1354 "sqlite_compileoption_get"
1355 }
1356}
1357
1358pub struct LikeFunc;
1361
1362impl ScalarFunction for LikeFunc {
1363 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1364 if let Some(null) = null_propagate(args) {
1365 return Ok(null);
1366 }
1367 let pattern = args[0].to_text();
1368 let string = args[1].to_text();
1369 let escape = if args.len() > 2 && !args[2].is_null() {
1370 args[2].to_text().chars().next()
1371 } else {
1372 None
1373 };
1374 let matched = like_match(&pattern, &string, escape);
1375 Ok(SqliteValue::Integer(i64::from(matched)))
1376 }
1377
1378 fn num_args(&self) -> i32 {
1379 -1 }
1381
1382 fn name(&self) -> &str {
1383 "like"
1384 }
1385}
1386
1387fn like_match(pattern: &str, string: &str, escape: Option<char>) -> bool {
1389 let pat: Vec<char> = pattern.chars().collect();
1390 let txt: Vec<char> = string.chars().collect();
1391 like_match_inner(&pat, &txt, 0, 0, escape)
1392}
1393
1394fn like_match_inner(
1395 pat: &[char],
1396 txt: &[char],
1397 mut pi: usize,
1398 mut ti: usize,
1399 escape: Option<char>,
1400) -> bool {
1401 while pi < pat.len() {
1402 let pc = pat[pi];
1403
1404 if Some(pc) == escape {
1405 pi += 1;
1407 if pi >= pat.len() {
1408 return false;
1409 }
1410 if ti >= txt.len() {
1411 return false;
1412 }
1413 if !ascii_iequal(pat[pi], txt[ti]) {
1414 return false;
1415 }
1416 pi += 1;
1417 ti += 1;
1418 continue;
1419 }
1420
1421 match pc {
1422 '%' => {
1423 while pi < pat.len() && pat[pi] == '%' {
1425 pi += 1;
1426 }
1427 if pi >= pat.len() {
1428 return true; }
1430 for start in ti..=txt.len() {
1432 if like_match_inner(pat, txt, pi, start, escape) {
1433 return true;
1434 }
1435 }
1436 return false;
1437 }
1438 '_' => {
1439 if ti >= txt.len() {
1440 return false;
1441 }
1442 pi += 1;
1443 ti += 1;
1444 }
1445 _ => {
1446 if ti >= txt.len() {
1447 return false;
1448 }
1449 if !ascii_iequal(pc, txt[ti]) {
1450 return false;
1451 }
1452 pi += 1;
1453 ti += 1;
1454 }
1455 }
1456 }
1457 ti >= txt.len()
1458}
1459
1460fn ascii_iequal(a: char, b: char) -> bool {
1461 a.to_ascii_lowercase() == b.to_ascii_lowercase()
1462}
1463
1464pub struct GlobFunc;
1467
1468impl ScalarFunction for GlobFunc {
1469 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1470 if let Some(null) = null_propagate(args) {
1471 return Ok(null);
1472 }
1473 let pattern = args[0].to_text();
1474 let string = args[1].to_text();
1475 let matched = glob_match(&pattern, &string);
1476 Ok(SqliteValue::Integer(i64::from(matched)))
1477 }
1478
1479 fn num_args(&self) -> i32 {
1480 2
1481 }
1482
1483 fn name(&self) -> &str {
1484 "glob"
1485 }
1486}
1487
1488fn glob_match(pattern: &str, string: &str) -> bool {
1490 let pat: Vec<char> = pattern.chars().collect();
1491 let txt: Vec<char> = string.chars().collect();
1492 glob_match_inner(&pat, &txt, 0, 0)
1493}
1494
1495fn glob_match_inner(pat: &[char], txt: &[char], mut pi: usize, mut ti: usize) -> bool {
1496 while pi < pat.len() {
1497 match pat[pi] {
1498 '*' => {
1499 while pi < pat.len() && pat[pi] == '*' {
1500 pi += 1;
1501 }
1502 if pi >= pat.len() {
1503 return true;
1504 }
1505 for start in ti..=txt.len() {
1506 if glob_match_inner(pat, txt, pi, start) {
1507 return true;
1508 }
1509 }
1510 return false;
1511 }
1512 '?' => {
1513 if ti >= txt.len() {
1514 return false;
1515 }
1516 pi += 1;
1517 ti += 1;
1518 }
1519 '[' => {
1520 if ti >= txt.len() {
1521 return false;
1522 }
1523 pi += 1;
1524 let negate = pi < pat.len() && pat[pi] == '^';
1525 if negate {
1526 pi += 1;
1527 }
1528 let mut found = false;
1529 let mut first = true;
1530 while pi < pat.len() && (first || pat[pi] != ']') {
1531 first = false;
1532 if pi + 2 < pat.len() && pat[pi + 1] == '-' {
1533 let lo = pat[pi];
1534 let hi = pat[pi + 2];
1535 if txt[ti] >= lo && txt[ti] <= hi {
1536 found = true;
1537 }
1538 pi += 3;
1539 } else {
1540 if txt[ti] == pat[pi] {
1541 found = true;
1542 }
1543 pi += 1;
1544 }
1545 }
1546 if pi < pat.len() && pat[pi] == ']' {
1547 pi += 1;
1548 }
1549 if found == negate {
1550 return false;
1551 }
1552 ti += 1;
1553 }
1554 c => {
1555 if ti >= txt.len() || txt[ti] != c {
1556 return false;
1557 }
1558 pi += 1;
1559 ti += 1;
1560 }
1561 }
1562 }
1563 ti >= txt.len()
1564}
1565
1566pub struct UnistrFunc;
1569
1570impl ScalarFunction for UnistrFunc {
1571 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1572 if args[0].is_null() {
1573 return Ok(SqliteValue::Null);
1574 }
1575 let input = args[0].to_text();
1576 let mut result = String::new();
1577 let chars: Vec<char> = input.chars().collect();
1578 let mut i = 0;
1579 while i < chars.len() {
1580 if chars[i] == '\\' && i + 1 < chars.len() {
1581 if chars[i + 1] == 'u' && i + 5 < chars.len() {
1582 let hex: String = chars[i + 2..i + 6].iter().collect();
1584 if let Ok(cp) = u32::from_str_radix(&hex, 16) {
1585 if let Some(c) = char::from_u32(cp) {
1586 result.push(c);
1587 i += 6;
1588 continue;
1589 }
1590 }
1591 } else if chars[i + 1] == 'U' && i + 9 < chars.len() {
1592 let hex: String = chars[i + 2..i + 10].iter().collect();
1594 if let Ok(cp) = u32::from_str_radix(&hex, 16) {
1595 if let Some(c) = char::from_u32(cp) {
1596 result.push(c);
1597 i += 10;
1598 continue;
1599 }
1600 }
1601 }
1602 }
1603 result.push(chars[i]);
1604 i += 1;
1605 }
1606 Ok(SqliteValue::Text(result))
1607 }
1608
1609 fn num_args(&self) -> i32 {
1610 1
1611 }
1612
1613 fn name(&self) -> &str {
1614 "unistr"
1615 }
1616}
1617
1618pub struct ChangesFunc;
1624
1625impl ScalarFunction for ChangesFunc {
1626 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1627 Ok(SqliteValue::Integer(LAST_CHANGES.get()))
1628 }
1629
1630 fn is_deterministic(&self) -> bool {
1631 false
1632 }
1633
1634 fn num_args(&self) -> i32 {
1635 0
1636 }
1637
1638 fn name(&self) -> &str {
1639 "changes"
1640 }
1641}
1642
1643pub struct TotalChangesFunc;
1644
1645impl ScalarFunction for TotalChangesFunc {
1646 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1647 Ok(SqliteValue::Integer(LAST_CHANGES.get()))
1648 }
1649
1650 fn is_deterministic(&self) -> bool {
1651 false
1652 }
1653
1654 fn num_args(&self) -> i32 {
1655 0
1656 }
1657
1658 fn name(&self) -> &str {
1659 "total_changes"
1660 }
1661}
1662
1663pub struct LastInsertRowidFunc;
1664
1665impl ScalarFunction for LastInsertRowidFunc {
1666 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1667 Ok(SqliteValue::Integer(LAST_INSERT_ROWID.get()))
1668 }
1669
1670 fn is_deterministic(&self) -> bool {
1671 false
1672 }
1673
1674 fn num_args(&self) -> i32 {
1675 0
1676 }
1677
1678 fn name(&self) -> &str {
1679 "last_insert_rowid"
1680 }
1681}
1682
1683#[allow(clippy::too_many_lines)]
1687pub fn register_builtins(registry: &mut FunctionRegistry) {
1688 registry.register_scalar(AbsFunc);
1690 registry.register_scalar(SignFunc);
1691 registry.register_scalar(RoundFunc);
1692 registry.register_scalar(RandomFunc);
1693 registry.register_scalar(RandomblobFunc);
1694 registry.register_scalar(ZeroblobFunc);
1695
1696 registry.register_scalar(LowerFunc);
1698 registry.register_scalar(UpperFunc);
1699 registry.register_scalar(LengthFunc);
1700 registry.register_scalar(OctetLengthFunc);
1701 registry.register_scalar(TrimFunc);
1702 registry.register_scalar(LtrimFunc);
1703 registry.register_scalar(RtrimFunc);
1704 registry.register_scalar(ReplaceFunc);
1705 registry.register_scalar(SubstrFunc);
1706 registry.register_scalar(InstrFunc);
1707 registry.register_scalar(CharFunc);
1708 registry.register_scalar(UnicodeFunc);
1709 registry.register_scalar(UnistrFunc);
1710 registry.register_scalar(HexFunc);
1711 registry.register_scalar(UnhexFunc);
1712 registry.register_scalar(QuoteFunc);
1713 registry.register_scalar(SoundexFunc);
1714
1715 registry.register_scalar(TypeofFunc);
1717 registry.register_scalar(SubtypeFunc);
1718
1719 registry.register_scalar(CoalesceFunc);
1721 registry.register_scalar(IfnullFunc);
1722 registry.register_scalar(NullifFunc);
1723 registry.register_scalar(IifFunc);
1724
1725 registry.register_scalar(ConcatFunc);
1727 registry.register_scalar(ConcatWsFunc);
1728 registry.register_scalar(ScalarMaxFunc);
1729 registry.register_scalar(ScalarMinFunc);
1730
1731 registry.register_scalar(LikelihoodFunc);
1733 registry.register_scalar(LikelyFunc);
1734 registry.register_scalar(UnlikelyFunc);
1735
1736 registry.register_scalar(LikeFunc);
1738 registry.register_scalar(GlobFunc);
1739
1740 registry.register_scalar(SqliteVersionFunc);
1742 registry.register_scalar(SqliteSourceIdFunc);
1743 registry.register_scalar(SqliteCompileoptionUsedFunc);
1744 registry.register_scalar(SqliteCompileoptionGetFunc);
1745
1746 registry.register_scalar(ChangesFunc);
1748 registry.register_scalar(TotalChangesFunc);
1749 registry.register_scalar(LastInsertRowidFunc);
1750
1751 struct IfFunc;
1754 impl ScalarFunction for IfFunc {
1755 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1756 IifFunc.invoke(args)
1757 }
1758
1759 fn num_args(&self) -> i32 {
1760 -1
1761 }
1762
1763 fn name(&self) -> &str {
1764 "if"
1765 }
1766 }
1767 registry.register_scalar(IfFunc);
1768
1769 struct SubstringFunc;
1771 impl ScalarFunction for SubstringFunc {
1772 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1773 SubstrFunc.invoke(args)
1774 }
1775
1776 fn num_args(&self) -> i32 {
1777 -1
1778 }
1779
1780 fn name(&self) -> &str {
1781 "substring"
1782 }
1783 }
1784 registry.register_scalar(SubstringFunc);
1785
1786 struct PrintfFunc;
1789 impl ScalarFunction for PrintfFunc {
1790 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1791 FormatFunc.invoke(args)
1792 }
1793
1794 fn num_args(&self) -> i32 {
1795 -1
1796 }
1797
1798 fn name(&self) -> &str {
1799 "printf"
1800 }
1801 }
1802 registry.register_scalar(FormatFunc);
1803 registry.register_scalar(PrintfFunc);
1804
1805 register_math_builtins(registry);
1807
1808 register_datetime_builtins(registry);
1810
1811 register_aggregate_builtins(registry);
1813}
1814
1815pub struct FormatFunc;
1818
1819impl ScalarFunction for FormatFunc {
1820 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1821 if args.is_empty() || args[0].is_null() {
1822 return Ok(SqliteValue::Null);
1823 }
1824 let fmt_str = args[0].to_text();
1825 let params = &args[1..];
1826 let result = sqlite_format(&fmt_str, params)?;
1827 Ok(SqliteValue::Text(result))
1828 }
1829
1830 fn num_args(&self) -> i32 {
1831 -1
1832 }
1833
1834 fn name(&self) -> &str {
1835 "format"
1836 }
1837}
1838
1839fn sqlite_format(fmt: &str, params: &[SqliteValue]) -> Result<String> {
1842 let mut result = String::new();
1843 let chars: Vec<char> = fmt.chars().collect();
1844 let mut i = 0;
1845 let mut param_idx = 0;
1846
1847 while i < chars.len() {
1848 if chars[i] != '%' {
1849 result.push(chars[i]);
1850 i += 1;
1851 continue;
1852 }
1853 i += 1;
1854 if i >= chars.len() {
1855 break;
1856 }
1857
1858 let mut left_align = false;
1860 let mut show_sign = false;
1861 let mut space_sign = false;
1862 let mut zero_pad = false;
1863 loop {
1864 if i >= chars.len() {
1865 break;
1866 }
1867 match chars[i] {
1868 '-' => left_align = true,
1869 '+' => show_sign = true,
1870 ' ' => space_sign = true,
1871 '0' => zero_pad = true,
1872 _ => break,
1873 }
1874 i += 1;
1875 }
1876
1877 let mut width = 0usize;
1879 while i < chars.len() && chars[i].is_ascii_digit() {
1880 width = width * 10 + (chars[i] as usize - '0' as usize);
1881 i += 1;
1882 }
1883
1884 let mut precision = None;
1886 if i < chars.len() && chars[i] == '.' {
1887 i += 1;
1888 let mut prec = 0usize;
1889 while i < chars.len() && chars[i].is_ascii_digit() {
1890 prec = prec * 10 + (chars[i] as usize - '0' as usize);
1891 i += 1;
1892 }
1893 precision = Some(prec);
1894 }
1895
1896 if i >= chars.len() {
1897 break;
1898 }
1899
1900 let spec = chars[i];
1901 i += 1;
1902
1903 match spec {
1904 '%' => result.push('%'),
1905 'n' => {} 'd' | 'i' => {
1907 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
1908 param_idx += 1;
1909 let formatted =
1910 format_integer(val, width, left_align, show_sign, space_sign, zero_pad);
1911 result.push_str(&formatted);
1912 }
1913 'f' => {
1914 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
1915 param_idx += 1;
1916 let prec = precision.unwrap_or(6);
1917 let formatted = format_float_f(
1918 val, prec, width, left_align, show_sign, space_sign, zero_pad,
1919 );
1920 result.push_str(&formatted);
1921 }
1922 'e' | 'E' => {
1923 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
1924 param_idx += 1;
1925 let prec = precision.unwrap_or(6);
1926 let formatted = if spec == 'e' {
1927 format!("{val:.prec$e}")
1928 } else {
1929 format!("{val:.prec$E}")
1930 };
1931 result.push_str(&pad_string(&formatted, width, left_align));
1932 }
1933 'g' | 'G' => {
1934 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
1935 param_idx += 1;
1936 let _prec = precision.unwrap_or(6);
1937 let formatted = format!("{val}");
1939 result.push_str(&pad_string(&formatted, width, left_align));
1940 }
1941 's' | 'z' => {
1942 let val = params
1943 .get(param_idx)
1944 .map(SqliteValue::to_text)
1945 .unwrap_or_default();
1946 param_idx += 1;
1947 let truncated = if let Some(prec) = precision {
1948 val.chars().take(prec).collect::<String>()
1949 } else {
1950 val
1951 };
1952 result.push_str(&pad_string(&truncated, width, left_align));
1953 }
1954 'q' => {
1955 let val = params
1957 .get(param_idx)
1958 .map(SqliteValue::to_text)
1959 .unwrap_or_default();
1960 param_idx += 1;
1961 let escaped = val.replace('\'', "''");
1962 result.push_str(&escaped);
1963 }
1964 'Q' => {
1965 let param = params.get(param_idx);
1967 param_idx += 1;
1968 match param {
1969 Some(SqliteValue::Null) | None => result.push_str("NULL"),
1970 Some(v) => {
1971 let val = v.to_text();
1972 let escaped = val.replace('\'', "''");
1973 result.push('\'');
1974 result.push_str(&escaped);
1975 result.push('\'');
1976 }
1977 }
1978 }
1979 'w' => {
1980 let val = params
1982 .get(param_idx)
1983 .map(SqliteValue::to_text)
1984 .unwrap_or_default();
1985 param_idx += 1;
1986 let escaped = val.replace('"', "\"\"");
1987 result.push('"');
1988 result.push_str(&escaped);
1989 result.push('"');
1990 }
1991 'c' => {
1992 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
1993 param_idx += 1;
1994 #[allow(clippy::cast_sign_loss)]
1995 if let Some(c) = char::from_u32(val as u32) {
1996 result.push(c);
1997 }
1998 }
1999 _ => {
2000 result.push('%');
2002 result.push(spec);
2003 }
2004 }
2005 let _ = (left_align, show_sign, space_sign, zero_pad);
2007 }
2008 Ok(result)
2009}
2010
2011fn format_integer(
2012 val: i64,
2013 width: usize,
2014 left_align: bool,
2015 show_sign: bool,
2016 space_sign: bool,
2017 zero_pad: bool,
2018) -> String {
2019 let sign = if val < 0 {
2020 "-".to_owned()
2021 } else if show_sign {
2022 "+".to_owned()
2023 } else if space_sign {
2024 " ".to_owned()
2025 } else {
2026 String::new()
2027 };
2028 let digits = format!("{}", val.abs());
2029 let body = format!("{sign}{digits}");
2030 if body.len() >= width {
2031 return body;
2032 }
2033 let pad = width - body.len();
2034 if left_align {
2035 format!("{body}{}", " ".repeat(pad))
2036 } else if zero_pad {
2037 format!("{sign}{}{digits}", "0".repeat(pad))
2038 } else {
2039 format!("{}{body}", " ".repeat(pad))
2040 }
2041}
2042
2043fn format_float_f(
2044 val: f64,
2045 prec: usize,
2046 width: usize,
2047 left_align: bool,
2048 show_sign: bool,
2049 space_sign: bool,
2050 zero_pad: bool,
2051) -> String {
2052 let sign = if val < 0.0 {
2053 "-".to_owned()
2054 } else if show_sign {
2055 "+".to_owned()
2056 } else if space_sign {
2057 " ".to_owned()
2058 } else {
2059 String::new()
2060 };
2061 let digits = format!("{:.prec$}", val.abs());
2062 let body = format!("{sign}{digits}");
2063 if body.len() >= width {
2064 return body;
2065 }
2066 let pad = width - body.len();
2067 if left_align {
2068 format!("{body}{}", " ".repeat(pad))
2069 } else if zero_pad {
2070 format!("{sign}{}{digits}", "0".repeat(pad))
2071 } else {
2072 format!("{}{body}", " ".repeat(pad))
2073 }
2074}
2075
2076fn pad_string(s: &str, width: usize, left_align: bool) -> String {
2077 if s.len() >= width {
2078 return s.to_owned();
2079 }
2080 let pad = width - s.len();
2081 if left_align {
2082 format!("{s}{}", " ".repeat(pad))
2083 } else {
2084 format!("{}{s}", " ".repeat(pad))
2085 }
2086}
2087
2088#[cfg(test)]
2091#[allow(clippy::too_many_lines)]
2092mod tests {
2093 use super::*;
2094
2095 fn invoke1(f: &dyn ScalarFunction, v: SqliteValue) -> Result<SqliteValue> {
2096 f.invoke(&[v])
2097 }
2098
2099 fn invoke2(f: &dyn ScalarFunction, a: SqliteValue, b: SqliteValue) -> Result<SqliteValue> {
2100 f.invoke(&[a, b])
2101 }
2102
2103 #[test]
2106 fn test_abs_positive() {
2107 assert_eq!(
2108 invoke1(&AbsFunc, SqliteValue::Integer(42)).unwrap(),
2109 SqliteValue::Integer(42)
2110 );
2111 }
2112
2113 #[test]
2114 fn test_abs_negative() {
2115 assert_eq!(
2116 invoke1(&AbsFunc, SqliteValue::Integer(-42)).unwrap(),
2117 SqliteValue::Integer(42)
2118 );
2119 }
2120
2121 #[test]
2122 fn test_abs_null() {
2123 assert_eq!(
2124 invoke1(&AbsFunc, SqliteValue::Null).unwrap(),
2125 SqliteValue::Null
2126 );
2127 }
2128
2129 #[test]
2130 fn test_abs_min_i64_overflow() {
2131 let err = invoke1(&AbsFunc, SqliteValue::Integer(i64::MIN)).unwrap_err();
2132 assert!(matches!(err, FrankenError::IntegerOverflow));
2133 }
2134
2135 #[test]
2136 fn test_abs_string_coercion() {
2137 assert_eq!(
2138 invoke1(&AbsFunc, SqliteValue::Text("-7.5".to_owned())).unwrap(),
2139 SqliteValue::Float(7.5)
2140 );
2141 }
2142
2143 #[test]
2144 #[allow(clippy::approx_constant)]
2145 fn test_abs_float() {
2146 assert_eq!(
2147 invoke1(&AbsFunc, SqliteValue::Float(-3.14)).unwrap(),
2148 SqliteValue::Float(3.14)
2149 );
2150 }
2151
2152 #[test]
2155 fn test_char_basic() {
2156 let f = CharFunc;
2157 let result = f
2158 .invoke(&[
2159 SqliteValue::Integer(72),
2160 SqliteValue::Integer(101),
2161 SqliteValue::Integer(108),
2162 SqliteValue::Integer(108),
2163 SqliteValue::Integer(111),
2164 ])
2165 .unwrap();
2166 assert_eq!(result, SqliteValue::Text("Hello".to_owned()));
2167 }
2168
2169 #[test]
2170 fn test_char_null_skipped() {
2171 let f = CharFunc;
2172 let result = f
2173 .invoke(&[
2174 SqliteValue::Integer(65),
2175 SqliteValue::Null,
2176 SqliteValue::Integer(66),
2177 ])
2178 .unwrap();
2179 assert_eq!(result, SqliteValue::Text("AB".to_owned()));
2180 }
2181
2182 #[test]
2185 fn test_coalesce_first_non_null() {
2186 let f = CoalesceFunc;
2187 let result = f
2188 .invoke(&[
2189 SqliteValue::Null,
2190 SqliteValue::Null,
2191 SqliteValue::Integer(3),
2192 SqliteValue::Integer(4),
2193 ])
2194 .unwrap();
2195 assert_eq!(result, SqliteValue::Integer(3));
2196 }
2197
2198 #[test]
2201 fn test_concat_null_as_empty() {
2202 let f = ConcatFunc;
2203 let result = f
2204 .invoke(&[
2205 SqliteValue::Null,
2206 SqliteValue::Text("hello".to_owned()),
2207 SqliteValue::Null,
2208 ])
2209 .unwrap();
2210 assert_eq!(result, SqliteValue::Text("hello".to_owned()));
2211 }
2212
2213 #[test]
2216 fn test_concat_ws_null_skipped() {
2217 let f = ConcatWsFunc;
2218 let result = f
2219 .invoke(&[
2220 SqliteValue::Text(",".to_owned()),
2221 SqliteValue::Text("a".to_owned()),
2222 SqliteValue::Null,
2223 SqliteValue::Text("b".to_owned()),
2224 ])
2225 .unwrap();
2226 assert_eq!(result, SqliteValue::Text("a,b".to_owned()));
2227 }
2228
2229 #[test]
2232 fn test_hex_blob() {
2233 let result = invoke1(&HexFunc, SqliteValue::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF])).unwrap();
2234 assert_eq!(result, SqliteValue::Text("DEADBEEF".to_owned()));
2235 }
2236
2237 #[test]
2238 fn test_hex_number_via_text() {
2239 let result = invoke1(&HexFunc, SqliteValue::Integer(42)).unwrap();
2241 assert_eq!(result, SqliteValue::Text("3432".to_owned()));
2242 }
2243
2244 #[test]
2247 fn test_iif_true() {
2248 let f = IifFunc;
2249 let result = f
2250 .invoke(&[
2251 SqliteValue::Integer(1),
2252 SqliteValue::Text("yes".to_owned()),
2253 SqliteValue::Text("no".to_owned()),
2254 ])
2255 .unwrap();
2256 assert_eq!(result, SqliteValue::Text("yes".to_owned()));
2257 }
2258
2259 #[test]
2260 fn test_iif_false() {
2261 let f = IifFunc;
2262 let result = f
2263 .invoke(&[
2264 SqliteValue::Integer(0),
2265 SqliteValue::Text("yes".to_owned()),
2266 SqliteValue::Text("no".to_owned()),
2267 ])
2268 .unwrap();
2269 assert_eq!(result, SqliteValue::Text("no".to_owned()));
2270 }
2271
2272 #[test]
2275 fn test_ifnull_non_null() {
2276 assert_eq!(
2277 invoke2(
2278 &IfnullFunc,
2279 SqliteValue::Integer(5),
2280 SqliteValue::Integer(10)
2281 )
2282 .unwrap(),
2283 SqliteValue::Integer(5)
2284 );
2285 }
2286
2287 #[test]
2288 fn test_ifnull_null() {
2289 assert_eq!(
2290 invoke2(&IfnullFunc, SqliteValue::Null, SqliteValue::Integer(10)).unwrap(),
2291 SqliteValue::Integer(10)
2292 );
2293 }
2294
2295 #[test]
2298 fn test_instr_found() {
2299 assert_eq!(
2300 invoke2(
2301 &InstrFunc,
2302 SqliteValue::Text("hello world".to_owned()),
2303 SqliteValue::Text("world".to_owned())
2304 )
2305 .unwrap(),
2306 SqliteValue::Integer(7)
2307 );
2308 }
2309
2310 #[test]
2311 fn test_instr_not_found() {
2312 assert_eq!(
2313 invoke2(
2314 &InstrFunc,
2315 SqliteValue::Text("hello".to_owned()),
2316 SqliteValue::Text("xyz".to_owned())
2317 )
2318 .unwrap(),
2319 SqliteValue::Integer(0)
2320 );
2321 }
2322
2323 #[test]
2326 fn test_length_text_chars() {
2327 assert_eq!(
2329 invoke1(&LengthFunc, SqliteValue::Text("café".to_owned())).unwrap(),
2330 SqliteValue::Integer(4)
2331 );
2332 }
2333
2334 #[test]
2335 fn test_length_blob_bytes() {
2336 assert_eq!(
2337 invoke1(&LengthFunc, SqliteValue::Blob(vec![1, 2])).unwrap(),
2338 SqliteValue::Integer(2)
2339 );
2340 }
2341
2342 #[test]
2345 fn test_octet_length_multibyte() {
2346 assert_eq!(
2348 invoke1(&OctetLengthFunc, SqliteValue::Text("café".to_owned())).unwrap(),
2349 SqliteValue::Integer(5)
2350 );
2351 }
2352
2353 #[test]
2356 fn test_lower_ascii() {
2357 assert_eq!(
2358 invoke1(&LowerFunc, SqliteValue::Text("HELLO".to_owned())).unwrap(),
2359 SqliteValue::Text("hello".to_owned())
2360 );
2361 }
2362
2363 #[test]
2364 fn test_upper_ascii() {
2365 assert_eq!(
2366 invoke1(&UpperFunc, SqliteValue::Text("hello".to_owned())).unwrap(),
2367 SqliteValue::Text("HELLO".to_owned())
2368 );
2369 }
2370
2371 #[test]
2374 fn test_trim_default() {
2375 let f = TrimFunc;
2376 assert_eq!(
2377 f.invoke(&[SqliteValue::Text(" hello ".to_owned())])
2378 .unwrap(),
2379 SqliteValue::Text("hello".to_owned())
2380 );
2381 }
2382
2383 #[test]
2384 fn test_ltrim_default() {
2385 let f = LtrimFunc;
2386 assert_eq!(
2387 f.invoke(&[SqliteValue::Text(" hello".to_owned())])
2388 .unwrap(),
2389 SqliteValue::Text("hello".to_owned())
2390 );
2391 }
2392
2393 #[test]
2394 fn test_ltrim_custom() {
2395 let f = LtrimFunc;
2396 assert_eq!(
2397 f.invoke(&[
2398 SqliteValue::Text("xxhello".to_owned()),
2399 SqliteValue::Text("x".to_owned()),
2400 ])
2401 .unwrap(),
2402 SqliteValue::Text("hello".to_owned())
2403 );
2404 }
2405
2406 #[test]
2409 fn test_nullif_equal() {
2410 assert_eq!(
2411 invoke2(
2412 &NullifFunc,
2413 SqliteValue::Integer(5),
2414 SqliteValue::Integer(5)
2415 )
2416 .unwrap(),
2417 SqliteValue::Null
2418 );
2419 }
2420
2421 #[test]
2422 fn test_nullif_different() {
2423 assert_eq!(
2424 invoke2(
2425 &NullifFunc,
2426 SqliteValue::Integer(5),
2427 SqliteValue::Integer(3)
2428 )
2429 .unwrap(),
2430 SqliteValue::Integer(5)
2431 );
2432 }
2433
2434 #[test]
2437 fn test_typeof_each() {
2438 assert_eq!(
2439 invoke1(&TypeofFunc, SqliteValue::Null).unwrap(),
2440 SqliteValue::Text("null".to_owned())
2441 );
2442 assert_eq!(
2443 invoke1(&TypeofFunc, SqliteValue::Integer(1)).unwrap(),
2444 SqliteValue::Text("integer".to_owned())
2445 );
2446 assert_eq!(
2447 invoke1(&TypeofFunc, SqliteValue::Float(1.0)).unwrap(),
2448 SqliteValue::Text("real".to_owned())
2449 );
2450 assert_eq!(
2451 invoke1(&TypeofFunc, SqliteValue::Text("x".to_owned())).unwrap(),
2452 SqliteValue::Text("text".to_owned())
2453 );
2454 assert_eq!(
2455 invoke1(&TypeofFunc, SqliteValue::Blob(vec![0])).unwrap(),
2456 SqliteValue::Text("blob".to_owned())
2457 );
2458 }
2459
2460 #[test]
2463 fn test_subtype_null_returns_zero() {
2464 assert_eq!(
2465 invoke1(&SubtypeFunc, SqliteValue::Null).unwrap(),
2466 SqliteValue::Integer(0)
2467 );
2468 }
2469
2470 #[test]
2473 fn test_replace_basic() {
2474 let f = ReplaceFunc;
2475 assert_eq!(
2476 f.invoke(&[
2477 SqliteValue::Text("hello world".to_owned()),
2478 SqliteValue::Text("world".to_owned()),
2479 SqliteValue::Text("earth".to_owned()),
2480 ])
2481 .unwrap(),
2482 SqliteValue::Text("hello earth".to_owned())
2483 );
2484 }
2485
2486 #[test]
2487 fn test_replace_empty_y() {
2488 let f = ReplaceFunc;
2489 assert_eq!(
2490 f.invoke(&[
2491 SqliteValue::Text("hello".to_owned()),
2492 SqliteValue::Text(String::new()),
2493 SqliteValue::Text("x".to_owned()),
2494 ])
2495 .unwrap(),
2496 SqliteValue::Text("hello".to_owned())
2497 );
2498 }
2499
2500 #[test]
2503 #[allow(clippy::float_cmp)]
2504 fn test_round_half_away() {
2505 assert_eq!(
2507 RoundFunc.invoke(&[SqliteValue::Float(2.5)]).unwrap(),
2508 SqliteValue::Float(3.0)
2509 );
2510 assert_eq!(
2511 RoundFunc.invoke(&[SqliteValue::Float(-2.5)]).unwrap(),
2512 SqliteValue::Float(-3.0)
2513 );
2514 }
2515
2516 #[test]
2517 #[allow(clippy::float_cmp, clippy::approx_constant)]
2518 fn test_round_precision() {
2519 assert_eq!(
2520 RoundFunc
2521 .invoke(&[SqliteValue::Float(3.14159), SqliteValue::Integer(2)])
2522 .unwrap(),
2523 SqliteValue::Float(3.14)
2524 );
2525 }
2526
2527 #[test]
2528 #[allow(clippy::float_cmp)]
2529 fn test_round_extreme_n_clamped() {
2530 assert_eq!(
2532 RoundFunc
2533 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(400)])
2534 .unwrap(),
2535 RoundFunc
2536 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(30)])
2537 .unwrap(),
2538 );
2539 assert_eq!(
2541 RoundFunc
2542 .invoke(&[SqliteValue::Float(2.5), SqliteValue::Integer(-5)])
2543 .unwrap(),
2544 SqliteValue::Float(3.0)
2545 );
2546 let result = RoundFunc
2548 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(i64::MAX)])
2549 .unwrap();
2550 if let SqliteValue::Float(v) = result {
2551 assert!(!v.is_nan(), "round must never return NaN");
2552 }
2553 }
2554
2555 #[test]
2556 #[allow(clippy::float_cmp)]
2557 fn test_round_large_value_no_fractional() {
2558 let big = 9_007_199_254_740_993.0_f64;
2560 assert_eq!(
2561 RoundFunc.invoke(&[SqliteValue::Float(big)]).unwrap(),
2562 SqliteValue::Float(big)
2563 );
2564 assert_eq!(
2565 RoundFunc.invoke(&[SqliteValue::Float(-big)]).unwrap(),
2566 SqliteValue::Float(-big)
2567 );
2568 }
2569
2570 #[test]
2573 fn test_sign_positive() {
2574 assert_eq!(
2575 invoke1(&SignFunc, SqliteValue::Integer(42)).unwrap(),
2576 SqliteValue::Integer(1)
2577 );
2578 }
2579
2580 #[test]
2581 fn test_sign_negative() {
2582 assert_eq!(
2583 invoke1(&SignFunc, SqliteValue::Integer(-42)).unwrap(),
2584 SqliteValue::Integer(-1)
2585 );
2586 }
2587
2588 #[test]
2589 fn test_sign_zero() {
2590 assert_eq!(
2591 invoke1(&SignFunc, SqliteValue::Integer(0)).unwrap(),
2592 SqliteValue::Integer(0)
2593 );
2594 }
2595
2596 #[test]
2597 fn test_sign_null() {
2598 assert_eq!(
2599 invoke1(&SignFunc, SqliteValue::Null).unwrap(),
2600 SqliteValue::Null
2601 );
2602 }
2603
2604 #[test]
2605 fn test_sign_non_numeric() {
2606 assert_eq!(
2607 invoke1(&SignFunc, SqliteValue::Text("abc".to_owned())).unwrap(),
2608 SqliteValue::Null
2609 );
2610 }
2611
2612 #[test]
2615 fn test_scalar_max_null() {
2616 let f = ScalarMaxFunc;
2617 let result = f
2618 .invoke(&[
2619 SqliteValue::Integer(1),
2620 SqliteValue::Null,
2621 SqliteValue::Integer(3),
2622 ])
2623 .unwrap();
2624 assert_eq!(result, SqliteValue::Null);
2625 }
2626
2627 #[test]
2628 fn test_scalar_max_values() {
2629 let f = ScalarMaxFunc;
2630 let result = f
2631 .invoke(&[
2632 SqliteValue::Integer(3),
2633 SqliteValue::Integer(1),
2634 SqliteValue::Integer(2),
2635 ])
2636 .unwrap();
2637 assert_eq!(result, SqliteValue::Integer(3));
2638 }
2639
2640 #[test]
2641 fn test_scalar_min_null() {
2642 let f = ScalarMinFunc;
2643 let result = f
2644 .invoke(&[
2645 SqliteValue::Integer(1),
2646 SqliteValue::Null,
2647 SqliteValue::Integer(3),
2648 ])
2649 .unwrap();
2650 assert_eq!(result, SqliteValue::Null);
2651 }
2652
2653 #[test]
2656 fn test_quote_text() {
2657 assert_eq!(
2658 invoke1(&QuoteFunc, SqliteValue::Text("it's".to_owned())).unwrap(),
2659 SqliteValue::Text("'it''s'".to_owned())
2660 );
2661 }
2662
2663 #[test]
2664 fn test_quote_null() {
2665 assert_eq!(
2666 invoke1(&QuoteFunc, SqliteValue::Null).unwrap(),
2667 SqliteValue::Text("NULL".to_owned())
2668 );
2669 }
2670
2671 #[test]
2672 fn test_quote_blob() {
2673 assert_eq!(
2674 invoke1(&QuoteFunc, SqliteValue::Blob(vec![0xAB])).unwrap(),
2675 SqliteValue::Text("X'AB'".to_owned())
2676 );
2677 }
2678
2679 #[test]
2682 fn test_random_range() {
2683 let f = RandomFunc;
2684 let result = f.invoke(&[]).unwrap();
2685 assert!(matches!(result, SqliteValue::Integer(_)));
2686 }
2687
2688 #[test]
2691 fn test_randomblob_length() {
2692 let result = invoke1(&RandomblobFunc, SqliteValue::Integer(16)).unwrap();
2693 match result {
2694 SqliteValue::Blob(b) => assert_eq!(b.len(), 16),
2695 other => unreachable!("expected blob, got {other:?}"),
2696 }
2697 }
2698
2699 #[test]
2702 fn test_zeroblob_length() {
2703 let result = invoke1(&ZeroblobFunc, SqliteValue::Integer(100)).unwrap();
2704 match result {
2705 SqliteValue::Blob(b) => {
2706 assert_eq!(b.len(), 100);
2707 assert!(b.iter().all(|&x| x == 0));
2708 }
2709 other => unreachable!("expected blob, got {other:?}"),
2710 }
2711 }
2712
2713 #[test]
2716 fn test_unhex_valid() {
2717 let result = invoke1(&UnhexFunc, SqliteValue::Text("48656C6C6F".to_owned())).unwrap();
2718 assert_eq!(result, SqliteValue::Blob(b"Hello".to_vec()));
2719 }
2720
2721 #[test]
2722 fn test_unhex_invalid() {
2723 let result = invoke1(&UnhexFunc, SqliteValue::Text("ZZZZ".to_owned())).unwrap();
2724 assert_eq!(result, SqliteValue::Null);
2725 }
2726
2727 #[test]
2728 fn test_unhex_ignore_chars() {
2729 let f = UnhexFunc;
2730 let result = f
2731 .invoke(&[
2732 SqliteValue::Text("48-65-6C".to_owned()),
2733 SqliteValue::Text("-".to_owned()),
2734 ])
2735 .unwrap();
2736 assert_eq!(result, SqliteValue::Blob(b"Hel".to_vec()));
2737 }
2738
2739 #[test]
2742 fn test_unicode_first_char() {
2743 assert_eq!(
2744 invoke1(&UnicodeFunc, SqliteValue::Text("A".to_owned())).unwrap(),
2745 SqliteValue::Integer(65)
2746 );
2747 }
2748
2749 #[test]
2752 fn test_soundex_basic() {
2753 assert_eq!(
2754 invoke1(&SoundexFunc, SqliteValue::Text("Robert".to_owned())).unwrap(),
2755 SqliteValue::Text("R163".to_owned())
2756 );
2757 }
2758
2759 #[test]
2762 fn test_substr_basic() {
2763 let f = SubstrFunc;
2764 assert_eq!(
2765 f.invoke(&[
2766 SqliteValue::Text("hello".to_owned()),
2767 SqliteValue::Integer(2),
2768 SqliteValue::Integer(3),
2769 ])
2770 .unwrap(),
2771 SqliteValue::Text("ell".to_owned())
2772 );
2773 }
2774
2775 #[test]
2776 fn test_substr_start_zero_quirk() {
2777 let f = SubstrFunc;
2779 let result = f
2780 .invoke(&[
2781 SqliteValue::Text("hello".to_owned()),
2782 SqliteValue::Integer(0),
2783 SqliteValue::Integer(3),
2784 ])
2785 .unwrap();
2786 assert_eq!(result, SqliteValue::Text("he".to_owned()));
2787 }
2788
2789 #[test]
2790 fn test_substr_negative_start() {
2791 let f = SubstrFunc;
2793 let result = f
2794 .invoke(&[
2795 SqliteValue::Text("hello".to_owned()),
2796 SqliteValue::Integer(-2),
2797 ])
2798 .unwrap();
2799 assert_eq!(result, SqliteValue::Text("lo".to_owned()));
2800 }
2801
2802 #[test]
2805 fn test_like_case_insensitive() {
2806 assert_eq!(
2807 invoke2(
2808 &LikeFunc,
2809 SqliteValue::Text("ABC".to_owned()),
2810 SqliteValue::Text("abc".to_owned())
2811 )
2812 .unwrap(),
2813 SqliteValue::Integer(1)
2814 );
2815 }
2816
2817 #[test]
2818 fn test_like_escape() {
2819 let f = LikeFunc;
2820 let result = f
2821 .invoke(&[
2822 SqliteValue::Text("10\\%".to_owned()),
2823 SqliteValue::Text("10%".to_owned()),
2824 SqliteValue::Text("\\".to_owned()),
2825 ])
2826 .unwrap();
2827 assert_eq!(result, SqliteValue::Integer(1));
2828 }
2829
2830 #[test]
2831 fn test_like_percent() {
2832 assert_eq!(
2833 invoke2(
2834 &LikeFunc,
2835 SqliteValue::Text("%ell%".to_owned()),
2836 SqliteValue::Text("Hello".to_owned())
2837 )
2838 .unwrap(),
2839 SqliteValue::Integer(1)
2840 );
2841 }
2842
2843 #[test]
2846 fn test_glob_star() {
2847 assert_eq!(
2848 invoke2(
2849 &GlobFunc,
2850 SqliteValue::Text("*.txt".to_owned()),
2851 SqliteValue::Text("file.txt".to_owned())
2852 )
2853 .unwrap(),
2854 SqliteValue::Integer(1)
2855 );
2856 }
2857
2858 #[test]
2859 fn test_glob_case_sensitive() {
2860 assert_eq!(
2861 invoke2(
2862 &GlobFunc,
2863 SqliteValue::Text("ABC".to_owned()),
2864 SqliteValue::Text("abc".to_owned())
2865 )
2866 .unwrap(),
2867 SqliteValue::Integer(0)
2868 );
2869 }
2870
2871 #[test]
2874 fn test_format_specifiers() {
2875 let f = FormatFunc;
2876 let result = f
2877 .invoke(&[
2878 SqliteValue::Text("%d %s".to_owned()),
2879 SqliteValue::Integer(42),
2880 SqliteValue::Text("hello".to_owned()),
2881 ])
2882 .unwrap();
2883 assert_eq!(result, SqliteValue::Text("42 hello".to_owned()));
2884 }
2885
2886 #[test]
2887 fn test_format_n_noop() {
2888 let f = FormatFunc;
2889 let result = f
2891 .invoke(&[SqliteValue::Text("before%nafter".to_owned())])
2892 .unwrap();
2893 assert_eq!(result, SqliteValue::Text("beforeafter".to_owned()));
2894 }
2895
2896 #[test]
2899 fn test_sqlite_version_format() {
2900 let result = SqliteVersionFunc.invoke(&[]).unwrap();
2901 match result {
2902 SqliteValue::Text(v) => {
2903 assert_eq!(v.split('.').count(), 3, "version must be N.N.N format");
2904 }
2905 other => unreachable!("expected text, got {other:?}"),
2906 }
2907 }
2908
2909 #[test]
2912 fn test_register_builtins_all_present() {
2913 let mut registry = FunctionRegistry::new();
2914 register_builtins(&mut registry);
2915
2916 assert!(registry.find_scalar("abs", 1).is_some());
2918 assert!(registry.find_scalar("typeof", 1).is_some());
2919 assert!(registry.find_scalar("length", 1).is_some());
2920 assert!(registry.find_scalar("lower", 1).is_some());
2921 assert!(registry.find_scalar("upper", 1).is_some());
2922 assert!(registry.find_scalar("hex", 1).is_some());
2923 assert!(registry.find_scalar("coalesce", 3).is_some());
2924 assert!(registry.find_scalar("concat", 2).is_some());
2925 assert!(registry.find_scalar("like", 2).is_some());
2926 assert!(registry.find_scalar("glob", 2).is_some());
2927 assert!(registry.find_scalar("round", 1).is_some());
2928 assert!(registry.find_scalar("substr", 2).is_some());
2929 assert!(registry.find_scalar("substring", 3).is_some());
2930 assert!(registry.find_scalar("sqlite_version", 0).is_some());
2931 assert!(registry.find_scalar("iif", 3).is_some());
2932 assert!(registry.find_scalar("if", 3).is_some());
2933 assert!(registry.find_scalar("format", 1).is_some());
2934 assert!(registry.find_scalar("printf", 1).is_some());
2935 assert!(registry.find_scalar("max", 2).is_some());
2936 assert!(registry.find_scalar("min", 2).is_some());
2937 assert!(registry.find_scalar("sign", 1).is_some());
2938 assert!(registry.find_scalar("random", 0).is_some());
2939
2940 assert!(registry.find_scalar("concat_ws", 3).is_some());
2942 assert!(registry.find_scalar("octet_length", 1).is_some());
2943 assert!(registry.find_scalar("unhex", 1).is_some());
2944 assert!(registry.find_scalar("timediff", 2).is_some());
2945 assert!(registry.find_scalar("unistr", 1).is_some());
2946
2947 assert!(registry.find_aggregate("median", 1).is_some());
2949 assert!(registry.find_aggregate("percentile", 2).is_some());
2950 assert!(registry.find_aggregate("percentile_cont", 2).is_some());
2951 assert!(registry.find_aggregate("percentile_disc", 2).is_some());
2952
2953 assert!(registry.find_scalar("load_extension", 1).is_none());
2955 assert!(registry.find_scalar("load_extension", 2).is_none());
2956 }
2957
2958 #[test]
2959 fn test_e2e_registry_invoke_through_lookup() {
2960 let mut registry = FunctionRegistry::new();
2961 register_builtins(&mut registry);
2962
2963 let abs = registry.find_scalar("ABS", 1).unwrap();
2965 assert_eq!(
2966 abs.invoke(&[SqliteValue::Integer(-42)]).unwrap(),
2967 SqliteValue::Integer(42)
2968 );
2969
2970 let typeof_fn = registry.find_scalar("typeof", 1).unwrap();
2972 assert_eq!(
2973 typeof_fn
2974 .invoke(&[SqliteValue::Text("hello".to_owned())])
2975 .unwrap(),
2976 SqliteValue::Text("text".to_owned())
2977 );
2978
2979 let coalesce = registry.find_scalar("COALESCE", 4).unwrap();
2981 assert_eq!(
2982 coalesce
2983 .invoke(&[
2984 SqliteValue::Null,
2985 SqliteValue::Null,
2986 SqliteValue::Integer(42),
2987 SqliteValue::Integer(99),
2988 ])
2989 .unwrap(),
2990 SqliteValue::Integer(42)
2991 );
2992 }
2993
2994 #[test]
2997 fn test_nondeterministic_functions_flagged() {
2998 assert!(!RandomFunc.is_deterministic());
3001 assert!(!RandomblobFunc.is_deterministic());
3002 assert!(!ChangesFunc.is_deterministic());
3003 assert!(!TotalChangesFunc.is_deterministic());
3004 assert!(!LastInsertRowidFunc.is_deterministic());
3005 }
3006
3007 #[test]
3008 fn test_deterministic_functions_flagged() {
3009 assert!(AbsFunc.is_deterministic());
3011 assert!(LengthFunc.is_deterministic());
3012 assert!(TypeofFunc.is_deterministic());
3013 assert!(UpperFunc.is_deterministic());
3014 assert!(LowerFunc.is_deterministic());
3015 assert!(HexFunc.is_deterministic());
3016 assert!(CoalesceFunc.is_deterministic());
3017 assert!(IifFunc.is_deterministic());
3018 }
3019
3020 #[test]
3021 fn test_random_produces_different_values() {
3022 let a = RandomFunc.invoke(&[]).unwrap();
3025 let b = RandomFunc.invoke(&[]).unwrap();
3026 assert_ne!(a.as_integer(), b.as_integer());
3029 }
3030
3031 #[test]
3032 fn test_registry_nondeterministic_lookup() {
3033 let mut registry = FunctionRegistry::default();
3034 register_builtins(&mut registry);
3035
3036 let random = registry.find_scalar("random", 0).unwrap();
3038 assert!(!random.is_deterministic());
3039
3040 let changes = registry.find_scalar("changes", 0).unwrap();
3041 assert!(!changes.is_deterministic());
3042
3043 let lir = registry.find_scalar("last_insert_rowid", 0).unwrap();
3044 assert!(!lir.is_deterministic());
3045
3046 let abs = registry.find_scalar("abs", 1).unwrap();
3048 assert!(abs.is_deterministic());
3049 }
3050}