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 _;
27use std::sync::Arc;
28
29use fsqlite_error::{FrankenError, Result};
30use fsqlite_types::SqliteValue;
31use fsqlite_types::value::format_sqlite_float;
32
33use crate::agg_builtins::register_aggregate_builtins;
34use crate::datetime::register_datetime_builtins;
35use crate::math::register_math_builtins;
36use crate::{FunctionRegistry, ScalarFunction};
37
38thread_local! {
42 static LAST_INSERT_ROWID: std::cell::Cell<i64> = const { std::cell::Cell::new(0) };
43 static LAST_CHANGES: std::cell::Cell<i64> = const { std::cell::Cell::new(0) };
44 static TOTAL_CHANGES: std::cell::Cell<i64> = const { std::cell::Cell::new(0) };
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct ChangeTrackingState {
50 pub last_insert_rowid: i64,
51 pub last_changes: i64,
52 pub total_changes: i64,
53}
54
55pub fn set_change_tracking_state(state: ChangeTrackingState) {
57 LAST_INSERT_ROWID.set(state.last_insert_rowid);
58 LAST_CHANGES.set(state.last_changes);
59 TOTAL_CHANGES.set(state.total_changes);
60}
61
62#[must_use]
64pub fn get_change_tracking_state() -> ChangeTrackingState {
65 ChangeTrackingState {
66 last_insert_rowid: LAST_INSERT_ROWID.get(),
67 last_changes: LAST_CHANGES.get(),
68 total_changes: TOTAL_CHANGES.get(),
69 }
70}
71
72pub fn set_last_insert_rowid(rowid: i64) {
74 LAST_INSERT_ROWID.set(rowid);
75}
76
77pub fn get_last_insert_rowid() -> i64 {
79 LAST_INSERT_ROWID.get()
80}
81
82pub fn set_last_changes(count: i64) {
86 LAST_CHANGES.set(count);
87 TOTAL_CHANGES.set(TOTAL_CHANGES.get().saturating_add(count));
88}
89
90pub fn get_last_changes() -> i64 {
92 LAST_CHANGES.get()
93}
94
95pub fn get_total_changes() -> i64 {
97 TOTAL_CHANGES.get()
98}
99
100pub fn reset_total_changes() {
102 TOTAL_CHANGES.set(0);
103}
104
105fn null_propagate(args: &[SqliteValue]) -> Option<SqliteValue> {
109 if args.iter().any(SqliteValue::is_null) {
110 Some(SqliteValue::Null)
111 } else {
112 None
113 }
114}
115
116pub struct AbsFunc;
119
120impl ScalarFunction for AbsFunc {
121 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
122 if args[0].is_null() {
123 return Ok(SqliteValue::Null);
124 }
125 match &args[0] {
126 SqliteValue::Integer(i) => {
127 if *i == i64::MIN {
128 return Err(FrankenError::IntegerOverflow);
129 }
130 Ok(SqliteValue::Integer(i.abs()))
131 }
132 other => {
133 let f = other.to_float();
134 Ok(SqliteValue::Float(if f < 0.0 { -f } else { f }))
137 }
138 }
139 }
140
141 fn num_args(&self) -> i32 {
142 1
143 }
144
145 fn name(&self) -> &str {
146 "abs"
147 }
148}
149
150pub struct CharFunc;
153
154impl ScalarFunction for CharFunc {
155 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
156 let mut result = String::new();
157 for arg in args {
158 #[allow(clippy::cast_sign_loss)]
160 let cp = arg.to_integer() as u32;
161 if let Some(c) = char::from_u32(cp) {
162 result.push(c);
163 }
164 }
165 Ok(SqliteValue::Text(Arc::from(result)))
166 }
167
168 fn is_deterministic(&self) -> bool {
169 true
170 }
171
172 fn num_args(&self) -> i32 {
173 -1 }
175
176 fn name(&self) -> &str {
177 "char"
178 }
179}
180
181pub struct CoalesceFunc;
184
185impl ScalarFunction for CoalesceFunc {
186 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
187 for arg in args {
191 if !arg.is_null() {
192 return Ok(arg.clone());
193 }
194 }
195 Ok(SqliteValue::Null)
196 }
197
198 fn num_args(&self) -> i32 {
199 -1
200 }
201
202 fn name(&self) -> &str {
203 "coalesce"
204 }
205}
206
207pub struct ConcatFunc;
210
211impl ScalarFunction for ConcatFunc {
212 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
213 let mut result = String::new();
214 for arg in args {
215 if !arg.is_null() {
217 result.push_str(&arg.to_text());
218 }
219 }
220 Ok(SqliteValue::Text(Arc::from(result)))
221 }
222
223 fn num_args(&self) -> i32 {
224 -1
225 }
226
227 fn name(&self) -> &str {
228 "concat"
229 }
230}
231
232pub struct ConcatWsFunc;
235
236impl ScalarFunction for ConcatWsFunc {
237 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
238 if args.is_empty() {
239 return Ok(SqliteValue::Text(Arc::from("")));
240 }
241 if args[0].is_null() {
243 return Ok(SqliteValue::Null);
244 }
245 let sep = args[0].to_text();
246 let mut parts = Vec::new();
247 for arg in &args[1..] {
248 if !arg.is_null() {
250 parts.push(arg.to_text());
251 }
252 }
253 Ok(SqliteValue::Text(Arc::from(parts.join(&sep))))
254 }
255
256 fn num_args(&self) -> i32 {
257 -1
258 }
259
260 fn name(&self) -> &str {
261 "concat_ws"
262 }
263}
264
265pub struct HexFunc;
268
269impl ScalarFunction for HexFunc {
270 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
271 if args[0].is_null() {
275 return Ok(SqliteValue::Text(Arc::from("")));
276 }
277 let bytes = match &args[0] {
278 SqliteValue::Blob(b) => b.to_vec(),
279 other => other.to_text().into_bytes(),
281 };
282 let mut hex = String::with_capacity(bytes.len() * 2);
283 for b in &bytes {
284 let _ = write!(hex, "{b:02X}");
285 }
286 Ok(SqliteValue::Text(Arc::from(hex)))
287 }
288
289 fn num_args(&self) -> i32 {
290 1
291 }
292
293 fn name(&self) -> &str {
294 "hex"
295 }
296}
297
298pub struct IfnullFunc;
301
302impl ScalarFunction for IfnullFunc {
303 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
304 if args[0].is_null() {
305 Ok(args[1].clone())
306 } else {
307 Ok(args[0].clone())
308 }
309 }
310
311 fn num_args(&self) -> i32 {
312 2
313 }
314
315 fn name(&self) -> &str {
316 "ifnull"
317 }
318}
319
320pub struct IifFunc;
323
324impl ScalarFunction for IifFunc {
325 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
326 let cond = &args[0];
327 let is_true = match cond {
330 SqliteValue::Null => false,
331 SqliteValue::Integer(n) => *n != 0,
332 SqliteValue::Float(f) => *f != 0.0,
333 SqliteValue::Text(_) | SqliteValue::Blob(_) => {
334 let i = cond.to_integer();
335 if i != 0 { true } else { cond.to_float() != 0.0 }
336 }
337 };
338 if is_true {
339 Ok(args[1].clone())
340 } else {
341 Ok(args[2].clone())
342 }
343 }
344
345 fn num_args(&self) -> i32 {
346 3
347 }
348
349 fn name(&self) -> &str {
350 "iif"
351 }
352}
353
354pub struct InstrFunc;
357
358impl ScalarFunction for InstrFunc {
359 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
360 if let Some(null) = null_propagate(args) {
361 return Ok(null);
362 }
363 match (&args[0], &args[1]) {
364 (SqliteValue::Blob(haystack), SqliteValue::Blob(needle)) => {
365 if needle.is_empty() {
367 return Ok(SqliteValue::Integer(1));
368 }
369 if haystack.is_empty() {
370 return Ok(SqliteValue::Integer(0));
371 }
372 let pos = find_bytes(haystack, needle).map_or(0, |p| p + 1);
373 Ok(SqliteValue::Integer(i64::try_from(pos).unwrap_or(0)))
374 }
375 _ => {
376 let haystack = args[0].to_text();
379 let needle = args[1].to_text();
380 if needle.is_empty() {
381 return Ok(SqliteValue::Integer(1));
382 }
383 if haystack.is_empty() {
384 return Ok(SqliteValue::Integer(0));
385 }
386 let pos = haystack
387 .find(&needle)
388 .map_or(0, |byte_pos| haystack[..byte_pos].chars().count() + 1);
389 Ok(SqliteValue::Integer(i64::try_from(pos).unwrap_or(0)))
390 }
391 }
392 }
393
394 fn num_args(&self) -> i32 {
395 2
396 }
397
398 fn name(&self) -> &str {
399 "instr"
400 }
401}
402
403fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
404 if needle.is_empty() {
405 return Some(0);
406 }
407 haystack.windows(needle.len()).position(|w| w == needle)
408}
409
410pub struct LengthFunc;
413
414impl ScalarFunction for LengthFunc {
415 #[allow(clippy::cast_possible_wrap)]
416 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
417 if args[0].is_null() {
418 return Ok(SqliteValue::Null);
419 }
420 let len = match &args[0] {
421 SqliteValue::Text(s) => s.chars().count(),
422 SqliteValue::Blob(b) => b.len(),
423 other => other.to_text().chars().count(),
425 };
426 Ok(SqliteValue::Integer(len as i64))
427 }
428
429 fn num_args(&self) -> i32 {
430 1
431 }
432
433 fn name(&self) -> &str {
434 "length"
435 }
436}
437
438pub struct OctetLengthFunc;
441
442impl ScalarFunction for OctetLengthFunc {
443 #[allow(clippy::cast_possible_wrap)]
444 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
445 if args[0].is_null() {
446 return Ok(SqliteValue::Null);
447 }
448 let len = match &args[0] {
449 SqliteValue::Text(s) => s.len(),
450 SqliteValue::Blob(b) => b.len(),
451 other => other.to_text().len(),
452 };
453 Ok(SqliteValue::Integer(len as i64))
454 }
455
456 fn num_args(&self) -> i32 {
457 1
458 }
459
460 fn name(&self) -> &str {
461 "octet_length"
462 }
463}
464
465pub struct LowerFunc;
468
469impl ScalarFunction for LowerFunc {
470 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
471 if args[0].is_null() {
472 return Ok(SqliteValue::Null);
473 }
474 Ok(SqliteValue::Text(Arc::from(
475 args[0].to_text().to_ascii_lowercase().as_str(),
476 )))
477 }
478
479 fn num_args(&self) -> i32 {
480 1
481 }
482
483 fn name(&self) -> &str {
484 "lower"
485 }
486}
487
488pub struct UpperFunc;
489
490impl ScalarFunction for UpperFunc {
491 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
492 if args[0].is_null() {
493 return Ok(SqliteValue::Null);
494 }
495 Ok(SqliteValue::Text(Arc::from(
496 args[0].to_text().to_ascii_uppercase().as_str(),
497 )))
498 }
499
500 fn num_args(&self) -> i32 {
501 1
502 }
503
504 fn name(&self) -> &str {
505 "upper"
506 }
507}
508
509pub struct TrimFunc;
512pub struct LtrimFunc;
513pub struct RtrimFunc;
514
515fn trim_chars(s: &str, chars: &str) -> String {
516 let char_set: Vec<char> = chars.chars().collect();
517 s.trim_matches(|c: char| char_set.contains(&c)).to_owned()
518}
519
520fn ltrim_chars(s: &str, chars: &str) -> String {
521 let char_set: Vec<char> = chars.chars().collect();
522 s.trim_start_matches(|c: char| char_set.contains(&c))
523 .to_owned()
524}
525
526fn rtrim_chars(s: &str, chars: &str) -> String {
527 let char_set: Vec<char> = chars.chars().collect();
528 s.trim_end_matches(|c: char| char_set.contains(&c))
529 .to_owned()
530}
531
532impl ScalarFunction for TrimFunc {
533 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
534 if args[0].is_null() {
535 return Ok(SqliteValue::Null);
536 }
537 let s = args[0].to_text();
538 let chars = if args.len() > 1 && !args[1].is_null() {
539 args[1].to_text()
540 } else {
541 " ".to_owned()
542 };
543 Ok(SqliteValue::Text(Arc::from(
544 trim_chars(&s, &chars).as_str(),
545 )))
546 }
547
548 fn num_args(&self) -> i32 {
549 -1 }
551
552 fn name(&self) -> &str {
553 "trim"
554 }
555}
556
557impl ScalarFunction for LtrimFunc {
558 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
559 if args[0].is_null() {
560 return Ok(SqliteValue::Null);
561 }
562 let s = args[0].to_text();
563 let chars = if args.len() > 1 && !args[1].is_null() {
564 args[1].to_text()
565 } else {
566 " ".to_owned()
567 };
568 Ok(SqliteValue::Text(Arc::from(
569 ltrim_chars(&s, &chars).as_str(),
570 )))
571 }
572
573 fn num_args(&self) -> i32 {
574 -1
575 }
576
577 fn name(&self) -> &str {
578 "ltrim"
579 }
580}
581
582impl ScalarFunction for RtrimFunc {
583 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
584 if args[0].is_null() {
585 return Ok(SqliteValue::Null);
586 }
587 let s = args[0].to_text();
588 let chars = if args.len() > 1 && !args[1].is_null() {
589 args[1].to_text()
590 } else {
591 " ".to_owned()
592 };
593 Ok(SqliteValue::Text(Arc::from(
594 rtrim_chars(&s, &chars).as_str(),
595 )))
596 }
597
598 fn num_args(&self) -> i32 {
599 -1
600 }
601
602 fn name(&self) -> &str {
603 "rtrim"
604 }
605}
606
607pub struct NullifFunc;
610
611impl ScalarFunction for NullifFunc {
612 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
613 if args[0] == args[1] {
614 Ok(SqliteValue::Null)
615 } else {
616 Ok(args[0].clone())
617 }
618 }
619
620 fn num_args(&self) -> i32 {
621 2
622 }
623
624 fn name(&self) -> &str {
625 "nullif"
626 }
627}
628
629pub struct TypeofFunc;
632
633impl ScalarFunction for TypeofFunc {
634 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
635 let type_name = match &args[0] {
636 SqliteValue::Null => "null",
637 SqliteValue::Integer(_) => "integer",
638 SqliteValue::Float(_) => "real",
639 SqliteValue::Text(_) => "text",
640 SqliteValue::Blob(_) => "blob",
641 };
642 Ok(SqliteValue::Text(Arc::from(type_name)))
643 }
644
645 fn num_args(&self) -> i32 {
646 1
647 }
648
649 fn name(&self) -> &str {
650 "typeof"
651 }
652}
653
654pub struct SubtypeFunc;
657
658impl ScalarFunction for SubtypeFunc {
659 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
660 Ok(SqliteValue::Integer(0))
663 }
664
665 fn num_args(&self) -> i32 {
666 1
667 }
668
669 fn name(&self) -> &str {
670 "subtype"
671 }
672}
673
674pub struct ReplaceFunc;
677
678impl ScalarFunction for ReplaceFunc {
679 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
680 if let Some(null) = null_propagate(args) {
681 return Ok(null);
682 }
683 let x = args[0].to_text();
684 let y = args[1].to_text();
685 let z = args[2].to_text();
686 if y.is_empty() {
687 return Ok(SqliteValue::Text(Arc::from(x)));
688 }
689
690 if z.len() > y.len() {
692 let occurrences = x.matches(&y).count();
693 let final_len = x.len() + occurrences * (z.len() - y.len());
694 if final_len > 1_000_000_000 {
695 return Err(FrankenError::TooBig);
696 }
697 }
698
699 Ok(SqliteValue::Text(Arc::from(x.replace(&y, &z))))
700 }
701
702 fn num_args(&self) -> i32 {
703 3
704 }
705
706 fn name(&self) -> &str {
707 "replace"
708 }
709}
710
711pub struct RoundFunc;
714
715impl ScalarFunction for RoundFunc {
716 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
717 if args[0].is_null() {
718 return Ok(SqliteValue::Null);
719 }
720 let x = args[0].to_float();
721 let n = if args.len() > 1 && !args[1].is_null() {
723 args[1].to_integer().clamp(0, 30)
724 } else {
725 0
726 };
727 if !(-4_503_599_627_370_496.0..=4_503_599_627_370_496.0).contains(&x) {
729 return Ok(SqliteValue::Float(x));
730 }
731 #[allow(clippy::cast_possible_truncation)]
737 let rounded = {
738 let prec = (n as usize) + 15;
739 let full = format!("{x:.prec$}");
740 let dot = full.find('.').unwrap_or(full.len());
741 let rd_idx = dot + 1 + n as usize;
742 if rd_idx >= full.len() {
743 format!("{x:.prec$}", prec = n as usize)
744 .parse::<f64>()
745 .unwrap_or(x)
746 } else {
747 let rd = full.as_bytes()[rd_idx] - b'0';
748 if rd != 5 || !full[rd_idx + 1..].bytes().all(|b| b == b'0') {
749 format!("{x:.prec$}", prec = n as usize)
751 .parse::<f64>()
752 .unwrap_or(x)
753 } else {
754 let mut trunc = full.as_bytes()[..rd_idx].to_vec();
757 if trunc.last() == Some(&b'.') {
759 trunc.pop();
760 }
761 let start = usize::from(trunc.first() == Some(&b'-'));
762 let mut carry = true;
763 for b in trunc[start..].iter_mut().rev() {
764 if *b == b'.' {
765 continue;
766 }
767 if carry {
768 if *b == b'9' {
769 *b = b'0';
770 } else {
771 *b += 1;
772 carry = false;
773 break;
774 }
775 }
776 }
777 if carry {
778 trunc.insert(start, b'1');
779 }
780 String::from_utf8(trunc)
781 .ok()
782 .and_then(|s| s.parse::<f64>().ok())
783 .unwrap_or(x)
784 }
785 }
786 };
787 Ok(SqliteValue::Float(rounded))
788 }
789
790 fn num_args(&self) -> i32 {
791 -1 }
793
794 fn name(&self) -> &str {
795 "round"
796 }
797}
798
799pub struct SignFunc;
802
803impl ScalarFunction for SignFunc {
804 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
805 if args[0].is_null() {
806 return Ok(SqliteValue::Null);
807 }
808 match &args[0] {
809 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(i.signum())),
810 SqliteValue::Float(f) => {
811 if f.is_nan() {
812 Ok(SqliteValue::Null)
813 } else if *f > 0.0 {
814 Ok(SqliteValue::Integer(1))
815 } else if *f < 0.0 {
816 Ok(SqliteValue::Integer(-1))
817 } else {
818 Ok(SqliteValue::Integer(0))
819 }
820 }
821 SqliteValue::Text(s) => {
822 let trimmed = s.trim();
824 if trimmed.is_empty() {
825 return Ok(SqliteValue::Null);
826 }
827
828 let stripped = trimmed.strip_prefix(['+', '-']).unwrap_or(trimmed);
834 if stripped.eq_ignore_ascii_case("nan")
835 || stripped.eq_ignore_ascii_case("inf")
836 || stripped.eq_ignore_ascii_case("infinity")
837 {
838 return Ok(SqliteValue::Null);
839 }
840
841 if let Ok(f) = trimmed.parse::<f64>() {
844 if f > 0.0 {
846 Ok(SqliteValue::Integer(1))
847 } else if f < 0.0 {
848 Ok(SqliteValue::Integer(-1))
849 } else {
850 Ok(SqliteValue::Integer(0))
851 }
852 } else if trimmed.parse::<i64>().is_ok() {
853 let i: i64 = trimmed.parse().unwrap();
855 Ok(SqliteValue::Integer(i.signum()))
856 } else {
857 Ok(SqliteValue::Null)
858 }
859 }
860 other => {
861 let f = other.to_float();
862 if f > 0.0 {
863 Ok(SqliteValue::Integer(1))
864 } else if f < 0.0 {
865 Ok(SqliteValue::Integer(-1))
866 } else {
867 Ok(SqliteValue::Integer(0))
868 }
869 }
870 }
871 }
872
873 fn num_args(&self) -> i32 {
874 1
875 }
876
877 fn name(&self) -> &str {
878 "sign"
879 }
880}
881
882pub struct RandomFunc;
885
886impl ScalarFunction for RandomFunc {
887 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
888 let val = simple_random_i64();
891 Ok(SqliteValue::Integer(val))
892 }
893
894 fn is_deterministic(&self) -> bool {
895 false
896 }
897
898 fn num_args(&self) -> i32 {
899 0
900 }
901
902 fn name(&self) -> &str {
903 "random"
904 }
905}
906
907fn simple_random_i64() -> i64 {
909 use std::sync::atomic::{AtomicU64, Ordering};
914
915 static STATE: AtomicU64 = AtomicU64::new(0xD1B5_4A32_D192_ED03);
916 let mut x = STATE.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed);
917 x ^= x >> 30;
918 x = x.wrapping_mul(0xBF58_476D_1CE4_E5B9);
919 x ^= x >> 27;
920 x = x.wrapping_mul(0x94D0_49BB_1331_11EB);
921 x ^= x >> 31;
922 x as i64
923}
924
925pub struct RandomblobFunc;
928
929impl ScalarFunction for RandomblobFunc {
930 #[allow(clippy::cast_sign_loss)]
931 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
932 if args[0].is_null() {
933 return Ok(SqliteValue::Null);
934 }
935 let n_i64 = args[0].to_integer().max(0);
936 if n_i64 > 1_000_000_000 {
937 return Err(FrankenError::TooBig);
938 }
939 let n = n_i64 as usize;
940 let mut buf = vec![0u8; n];
941 let mut i = 0;
942 while i < n {
943 let rnd = simple_random_i64().to_ne_bytes();
944 let to_copy = (n - i).min(8);
945 buf[i..i + to_copy].copy_from_slice(&rnd[..to_copy]);
946 i += to_copy;
947 }
948 Ok(SqliteValue::Blob(Arc::from(buf.as_slice())))
949 }
950
951 fn is_deterministic(&self) -> bool {
952 false
953 }
954
955 fn num_args(&self) -> i32 {
956 1
957 }
958
959 fn name(&self) -> &str {
960 "randomblob"
961 }
962}
963
964pub struct ZeroblobFunc;
967
968impl ScalarFunction for ZeroblobFunc {
969 #[allow(clippy::cast_sign_loss)]
970 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
971 if args[0].is_null() {
973 return Ok(SqliteValue::Blob(Arc::from([] as [u8; 0])));
974 }
975 let n_i64 = args[0].to_integer().max(0);
976 if n_i64 > 1_000_000_000 {
977 return Err(FrankenError::TooBig);
978 }
979 let n = n_i64 as usize;
980 Ok(SqliteValue::Blob(Arc::from(vec![0u8; n].as_slice())))
981 }
982
983 fn num_args(&self) -> i32 {
984 1
985 }
986
987 fn name(&self) -> &str {
988 "zeroblob"
989 }
990}
991
992pub struct QuoteFunc;
995
996impl ScalarFunction for QuoteFunc {
997 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
998 let result = match &args[0] {
999 SqliteValue::Null => "NULL".to_owned(),
1000 SqliteValue::Integer(i) => i.to_string(),
1001 SqliteValue::Float(f) => format_sqlite_float(*f),
1002 SqliteValue::Text(s) => {
1003 let escaped = s.replace('\'', "''");
1004 format!("'{escaped}'")
1005 }
1006 SqliteValue::Blob(b) => {
1007 let mut hex = String::with_capacity(3 + b.len() * 2);
1008 hex.push_str("X'");
1009 for byte in b.iter() {
1010 let _ = write!(hex, "{byte:02X}");
1011 }
1012 hex.push('\'');
1013 hex
1014 }
1015 };
1016 Ok(SqliteValue::Text(Arc::from(result)))
1017 }
1018
1019 fn num_args(&self) -> i32 {
1020 1
1021 }
1022
1023 fn name(&self) -> &str {
1024 "quote"
1025 }
1026}
1027
1028pub struct UnhexFunc;
1031
1032impl ScalarFunction for UnhexFunc {
1033 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1034 if args[0].is_null() {
1035 return Ok(SqliteValue::Null);
1036 }
1037 let input = args[0].to_text();
1038 let ignore_chars: Vec<char> = if args.len() > 1 && !args[1].is_null() {
1039 args[1].to_text().chars().collect()
1040 } else {
1041 Vec::new()
1042 };
1043
1044 let filtered: String = input
1046 .chars()
1047 .filter(|c| !ignore_chars.contains(c))
1048 .collect();
1049
1050 if filtered.len() % 2 != 0 {
1052 return Ok(SqliteValue::Null);
1053 }
1054
1055 let mut bytes = Vec::with_capacity(filtered.len() / 2);
1056 let chars: Vec<char> = filtered.chars().collect();
1057 let mut i = 0;
1058 while i < chars.len() {
1059 let hi = match hex_digit(chars[i]) {
1060 Some(v) => v,
1061 None => return Ok(SqliteValue::Null),
1062 };
1063 let lo = match hex_digit(chars[i + 1]) {
1064 Some(v) => v,
1065 None => return Ok(SqliteValue::Null),
1066 };
1067 bytes.push(hi << 4 | lo);
1068 i += 2;
1069 }
1070 Ok(SqliteValue::Blob(Arc::from(bytes.as_slice())))
1071 }
1072
1073 fn num_args(&self) -> i32 {
1074 -1 }
1076
1077 fn name(&self) -> &str {
1078 "unhex"
1079 }
1080}
1081
1082fn hex_digit(c: char) -> Option<u8> {
1083 match c {
1084 '0'..='9' => Some(c as u8 - b'0'),
1085 'a'..='f' => Some(c as u8 - b'a' + 10),
1086 'A'..='F' => Some(c as u8 - b'A' + 10),
1087 _ => None,
1088 }
1089}
1090
1091pub struct UnicodeFunc;
1094
1095impl ScalarFunction for UnicodeFunc {
1096 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1097 if args[0].is_null() {
1098 return Ok(SqliteValue::Null);
1099 }
1100 let s = args[0].to_text();
1101 match s.chars().next() {
1102 Some(c) => Ok(SqliteValue::Integer(i64::from(c as u32))),
1103 None => Ok(SqliteValue::Null),
1104 }
1105 }
1106
1107 fn num_args(&self) -> i32 {
1108 1
1109 }
1110
1111 fn name(&self) -> &str {
1112 "unicode"
1113 }
1114}
1115
1116pub struct SubstrFunc;
1119
1120impl ScalarFunction for SubstrFunc {
1121 #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
1122 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1123 if args[0].is_null() || args[1].is_null() {
1124 return Ok(SqliteValue::Null);
1125 }
1126 let is_blob = matches!(&args[0], SqliteValue::Blob(_));
1127 if is_blob {
1128 return self.invoke_blob(args);
1129 }
1130
1131 let s = args[0].to_text();
1132 let chars: Vec<char> = s.chars().collect();
1133 let len = chars.len() as i64;
1134 let has_length = args.len() > 2 && !args[2].is_null();
1135
1136 let mut p1 = args[1].to_integer();
1137 let mut p2 = if has_length {
1138 args[2].to_integer()
1139 } else {
1140 1_000_000_000
1141 };
1142
1143 let neg_p2 = p2 < 0;
1147 if neg_p2 {
1148 p2 = p2.saturating_neg();
1149 }
1150
1151 if p1 < 0 {
1153 p1 = p1.saturating_add(len);
1154 if p1 < 0 {
1155 p2 = p2.saturating_add(p1);
1156 p1 = 0;
1157 }
1158 } else if p1 > 0 {
1159 p1 -= 1;
1160 } else if p2 > 0 {
1161 p2 -= 1; }
1163
1164 if neg_p2 {
1166 p1 = p1.saturating_sub(p2);
1167 if p1 < 0 {
1168 p2 = p2.saturating_add(p1);
1169 p1 = 0;
1170 }
1171 }
1172
1173 if p1.saturating_add(p2) > len {
1174 p2 = len.saturating_sub(p1);
1175 }
1176 if p2 <= 0 {
1177 return Ok(SqliteValue::Text(Arc::from("")));
1178 }
1179
1180 let result: String = chars[p1 as usize..(p1 + p2) as usize].iter().collect();
1181 Ok(SqliteValue::Text(Arc::from(result)))
1182 }
1183
1184 fn num_args(&self) -> i32 {
1185 -1 }
1187
1188 fn name(&self) -> &str {
1189 "substr"
1190 }
1191}
1192
1193impl SubstrFunc {
1194 #[allow(
1195 clippy::unused_self,
1196 clippy::cast_sign_loss,
1197 clippy::cast_possible_wrap
1198 )]
1199 fn invoke_blob(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1200 let blob = match &args[0] {
1201 SqliteValue::Blob(b) => b,
1202 _ => return Ok(SqliteValue::Null),
1203 };
1204 let len = blob.len() as i64;
1205 let has_length = args.len() > 2 && !args[2].is_null();
1206
1207 let mut p1 = args[1].to_integer();
1208 let mut p2 = if has_length {
1209 args[2].to_integer()
1210 } else {
1211 1_000_000_000
1212 };
1213
1214 let neg_p2 = p2 < 0;
1215 if neg_p2 {
1216 p2 = p2.saturating_neg();
1217 }
1218
1219 if p1 < 0 {
1220 p1 = p1.saturating_add(len);
1221 if p1 < 0 {
1222 p2 = p2.saturating_add(p1);
1223 p1 = 0;
1224 }
1225 } else if p1 > 0 {
1226 p1 -= 1;
1227 } else if p2 > 0 {
1228 p2 -= 1;
1229 }
1230
1231 if neg_p2 {
1232 p1 = p1.saturating_sub(p2);
1233 if p1 < 0 {
1234 p2 = p2.saturating_add(p1);
1235 p1 = 0;
1236 }
1237 }
1238
1239 if p1.saturating_add(p2) > len {
1240 p2 = len.saturating_sub(p1);
1241 }
1242 if p2 <= 0 {
1243 return Ok(SqliteValue::Blob(Arc::from([] as [u8; 0])));
1244 }
1245
1246 Ok(SqliteValue::Blob(Arc::from(
1247 &blob[p1 as usize..(p1 + p2) as usize],
1248 )))
1249 }
1250}
1251
1252pub struct SoundexFunc;
1255
1256impl ScalarFunction for SoundexFunc {
1257 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1258 if args[0].is_null() {
1259 return Ok(SqliteValue::Text(Arc::from("?000")));
1261 }
1262 let s = args[0].to_text();
1263 Ok(SqliteValue::Text(Arc::from(soundex(&s))))
1264 }
1265
1266 fn num_args(&self) -> i32 {
1267 1
1268 }
1269
1270 fn name(&self) -> &str {
1271 "soundex"
1272 }
1273}
1274
1275fn soundex(s: &str) -> String {
1276 let mut chars = s.chars().filter(|c| c.is_ascii_alphabetic());
1277 let first = match chars.next() {
1278 Some(c) => c.to_ascii_uppercase(),
1279 None => return "?000".to_owned(),
1280 };
1281
1282 let code = |c: char| -> Option<char> {
1283 match c.to_ascii_uppercase() {
1284 'B' | 'F' | 'P' | 'V' => Some('1'),
1285 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => Some('2'),
1286 'D' | 'T' => Some('3'),
1287 'L' => Some('4'),
1288 'M' | 'N' => Some('5'),
1289 'R' => Some('6'),
1290 _ => None, }
1292 };
1293
1294 let mut result = String::with_capacity(4);
1295 result.push(first);
1296 let mut last_code = code(first);
1297
1298 for c in chars {
1299 if result.len() >= 4 {
1300 break;
1301 }
1302 let current = code(c);
1303 if let Some(digit) = current {
1304 if current != last_code {
1305 result.push(digit);
1306 }
1307 }
1308 last_code = current;
1309 }
1310
1311 while result.len() < 4 {
1312 result.push('0');
1313 }
1314 result
1315}
1316
1317pub struct ScalarMaxFunc;
1320
1321impl ScalarFunction for ScalarMaxFunc {
1322 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1323 if let Some(null) = null_propagate(args) {
1325 return Ok(null);
1326 }
1327 let mut max = &args[0];
1328 for arg in &args[1..] {
1329 if arg.partial_cmp(max) == Some(std::cmp::Ordering::Greater) {
1330 max = arg;
1331 }
1332 }
1333 Ok(max.clone())
1334 }
1335
1336 fn num_args(&self) -> i32 {
1337 -1
1338 }
1339
1340 fn name(&self) -> &str {
1341 "max"
1342 }
1343}
1344
1345pub struct ScalarMinFunc;
1348
1349impl ScalarFunction for ScalarMinFunc {
1350 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1351 if let Some(null) = null_propagate(args) {
1353 return Ok(null);
1354 }
1355 let mut min = &args[0];
1356 for arg in &args[1..] {
1357 if arg.partial_cmp(min) == Some(std::cmp::Ordering::Less) {
1358 min = arg;
1359 }
1360 }
1361 Ok(min.clone())
1362 }
1363
1364 fn num_args(&self) -> i32 {
1365 -1
1366 }
1367
1368 fn name(&self) -> &str {
1369 "min"
1370 }
1371}
1372
1373pub struct LikelihoodFunc;
1376
1377impl ScalarFunction for LikelihoodFunc {
1378 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1379 Ok(args[0].clone())
1381 }
1382
1383 fn num_args(&self) -> i32 {
1384 2
1385 }
1386
1387 fn name(&self) -> &str {
1388 "likelihood"
1389 }
1390}
1391
1392pub struct LikelyFunc;
1393
1394impl ScalarFunction for LikelyFunc {
1395 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1396 Ok(args[0].clone())
1397 }
1398
1399 fn num_args(&self) -> i32 {
1400 1
1401 }
1402
1403 fn name(&self) -> &str {
1404 "likely"
1405 }
1406}
1407
1408pub struct UnlikelyFunc;
1409
1410impl ScalarFunction for UnlikelyFunc {
1411 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1412 Ok(args[0].clone())
1413 }
1414
1415 fn num_args(&self) -> i32 {
1416 1
1417 }
1418
1419 fn name(&self) -> &str {
1420 "unlikely"
1421 }
1422}
1423
1424pub struct SqliteVersionFunc;
1427
1428impl ScalarFunction for SqliteVersionFunc {
1429 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1430 Ok(SqliteValue::Text(Arc::from("3.52.0")))
1431 }
1432
1433 fn num_args(&self) -> i32 {
1434 0
1435 }
1436
1437 fn name(&self) -> &str {
1438 "sqlite_version"
1439 }
1440}
1441
1442pub struct SqliteSourceIdFunc;
1445
1446impl ScalarFunction for SqliteSourceIdFunc {
1447 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1448 Ok(SqliteValue::Text(Arc::from(
1449 "FrankenSQLite 0.1.0 (compatible with SQLite 3.52.0)",
1450 )))
1451 }
1452
1453 fn num_args(&self) -> i32 {
1454 0
1455 }
1456
1457 fn name(&self) -> &str {
1458 "sqlite_source_id"
1459 }
1460}
1461
1462pub struct SqliteCompileoptionUsedFunc;
1465
1466impl ScalarFunction for SqliteCompileoptionUsedFunc {
1467 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1468 if args[0].is_null() {
1469 return Ok(SqliteValue::Null);
1470 }
1471 let opt = args[0].to_text().to_ascii_uppercase();
1472 let known = matches!(
1474 opt.as_str(),
1475 "THREADSAFE" | "ENABLE_FTS5" | "ENABLE_JSON1" | "ENABLE_RTREE"
1476 );
1477 Ok(SqliteValue::Integer(i64::from(known)))
1478 }
1479
1480 fn num_args(&self) -> i32 {
1481 1
1482 }
1483
1484 fn name(&self) -> &str {
1485 "sqlite_compileoption_used"
1486 }
1487}
1488
1489pub struct SqliteCompileoptionGetFunc;
1492
1493impl ScalarFunction for SqliteCompileoptionGetFunc {
1494 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1495 if args[0].is_null() {
1496 return Ok(SqliteValue::Null);
1497 }
1498 let n = args[0].to_integer();
1499 let options = [
1500 "THREADSAFE=1",
1501 "ENABLE_FTS5",
1502 "ENABLE_JSON1",
1503 "ENABLE_RTREE",
1504 ];
1505 #[allow(clippy::cast_sign_loss)]
1506 match options.get(n as usize) {
1507 Some(opt) => Ok(SqliteValue::Text(Arc::from(*opt))),
1508 None => Ok(SqliteValue::Null),
1509 }
1510 }
1511
1512 fn num_args(&self) -> i32 {
1513 1
1514 }
1515
1516 fn name(&self) -> &str {
1517 "sqlite_compileoption_get"
1518 }
1519}
1520
1521pub struct LikeFunc;
1524
1525impl ScalarFunction for LikeFunc {
1526 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1527 if let Some(null) = null_propagate(args) {
1528 return Ok(null);
1529 }
1530 let pattern = args[0].to_text();
1531 let string = args[1].to_text();
1532 let escape = if args.len() > 2 && !args[2].is_null() {
1533 Some(single_char_escape(args[2].to_text().as_str())?)
1534 } else {
1535 None
1536 };
1537 let matched = like_match(&pattern, &string, escape);
1538 Ok(SqliteValue::Integer(i64::from(matched)))
1539 }
1540
1541 fn num_args(&self) -> i32 {
1542 -1 }
1544
1545 fn name(&self) -> &str {
1546 "like"
1547 }
1548}
1549
1550fn single_char_escape(escape: &str) -> Result<char> {
1551 let mut chars = escape.chars();
1552 match (chars.next(), chars.next()) {
1553 (Some(ch), None) => Ok(ch),
1554 _ => Err(FrankenError::function_error(
1555 "ESCAPE expression must be a single character",
1556 )),
1557 }
1558}
1559
1560fn like_match(pattern: &str, string: &str, escape: Option<char>) -> bool {
1562 let pat: Vec<char> = pattern.chars().collect();
1563 let txt: Vec<char> = string.chars().collect();
1564 like_match_inner(&pat, &txt, 0, 0, escape)
1565}
1566
1567fn like_match_inner(
1568 pat: &[char],
1569 txt: &[char],
1570 mut pi: usize,
1571 mut ti: usize,
1572 escape: Option<char>,
1573) -> bool {
1574 while pi < pat.len() {
1575 let pc = pat[pi];
1576
1577 if Some(pc) == escape {
1578 pi += 1;
1580 if pi >= pat.len() {
1581 return false;
1582 }
1583 if ti >= txt.len() {
1584 return false;
1585 }
1586 if !ascii_iequal(pat[pi], txt[ti]) {
1587 return false;
1588 }
1589 pi += 1;
1590 ti += 1;
1591 continue;
1592 }
1593
1594 match pc {
1595 '%' => {
1596 while pi < pat.len() && pat[pi] == '%' {
1598 pi += 1;
1599 }
1600 if pi >= pat.len() {
1601 return true; }
1603 for start in ti..=txt.len() {
1605 if like_match_inner(pat, txt, pi, start, escape) {
1606 return true;
1607 }
1608 }
1609 return false;
1610 }
1611 '_' => {
1612 if ti >= txt.len() {
1613 return false;
1614 }
1615 pi += 1;
1616 ti += 1;
1617 }
1618 _ => {
1619 if ti >= txt.len() {
1620 return false;
1621 }
1622 if !ascii_iequal(pc, txt[ti]) {
1623 return false;
1624 }
1625 pi += 1;
1626 ti += 1;
1627 }
1628 }
1629 }
1630 ti >= txt.len()
1631}
1632
1633fn ascii_iequal(a: char, b: char) -> bool {
1634 a.to_ascii_lowercase() == b.to_ascii_lowercase()
1635}
1636
1637pub struct GlobFunc;
1640
1641impl ScalarFunction for GlobFunc {
1642 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1643 if let Some(null) = null_propagate(args) {
1644 return Ok(null);
1645 }
1646 let pattern = args[0].to_text();
1647 let string = args[1].to_text();
1648 let matched = glob_match(&pattern, &string);
1649 Ok(SqliteValue::Integer(i64::from(matched)))
1650 }
1651
1652 fn num_args(&self) -> i32 {
1653 2
1654 }
1655
1656 fn name(&self) -> &str {
1657 "glob"
1658 }
1659}
1660
1661fn glob_match(pattern: &str, string: &str) -> bool {
1663 let pat: Vec<char> = pattern.chars().collect();
1664 let txt: Vec<char> = string.chars().collect();
1665 glob_match_inner(&pat, &txt, 0, 0)
1666}
1667
1668fn glob_match_inner(pat: &[char], txt: &[char], mut pi: usize, mut ti: usize) -> bool {
1669 while pi < pat.len() {
1670 match pat[pi] {
1671 '*' => {
1672 while pi < pat.len() && pat[pi] == '*' {
1673 pi += 1;
1674 }
1675 if pi >= pat.len() {
1676 return true;
1677 }
1678 for start in ti..=txt.len() {
1679 if glob_match_inner(pat, txt, pi, start) {
1680 return true;
1681 }
1682 }
1683 return false;
1684 }
1685 '?' => {
1686 if ti >= txt.len() {
1687 return false;
1688 }
1689 pi += 1;
1690 ti += 1;
1691 }
1692 '[' => {
1693 if ti >= txt.len() {
1694 return false;
1695 }
1696 pi += 1;
1697 let negate = pi < pat.len() && pat[pi] == '^';
1698 if negate {
1699 pi += 1;
1700 }
1701 let mut found = false;
1702 let mut first = true;
1703 while pi < pat.len() && (first || pat[pi] != ']') {
1704 first = false;
1705 if pi + 2 < pat.len() && pat[pi + 1] == '-' {
1706 let lo = pat[pi];
1707 let hi = pat[pi + 2];
1708 if txt[ti] >= lo && txt[ti] <= hi {
1709 found = true;
1710 }
1711 pi += 3;
1712 } else {
1713 if txt[ti] == pat[pi] {
1714 found = true;
1715 }
1716 pi += 1;
1717 }
1718 }
1719 if pi < pat.len() && pat[pi] == ']' {
1720 pi += 1;
1721 }
1722 if found == negate {
1723 return false;
1724 }
1725 ti += 1;
1726 }
1727 c => {
1728 if ti >= txt.len() || txt[ti] != c {
1729 return false;
1730 }
1731 pi += 1;
1732 ti += 1;
1733 }
1734 }
1735 }
1736 ti >= txt.len()
1737}
1738
1739pub struct UnistrFunc;
1742
1743impl ScalarFunction for UnistrFunc {
1744 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1745 if args[0].is_null() {
1746 return Ok(SqliteValue::Null);
1747 }
1748 let input = args[0].to_text();
1749 let mut result = String::new();
1750 let chars: Vec<char> = input.chars().collect();
1751 let mut i = 0;
1752 while i < chars.len() {
1753 if chars[i] == '\\' && i + 1 < chars.len() {
1754 if chars[i + 1] == '\\' {
1756 result.push('\\');
1757 i += 2;
1758 continue;
1759 } else if chars[i + 1] == 'u' && i + 5 < chars.len() {
1760 let hex: String = chars[i + 2..i + 6].iter().collect();
1762 if let Ok(cp) = u32::from_str_radix(&hex, 16) {
1763 if let Some(c) = char::from_u32(cp) {
1764 result.push(c);
1765 i += 6;
1766 continue;
1767 }
1768 }
1769 } else if chars[i + 1] == 'U' && i + 9 < chars.len() {
1770 let hex: String = chars[i + 2..i + 10].iter().collect();
1772 if let Ok(cp) = u32::from_str_radix(&hex, 16) {
1773 if let Some(c) = char::from_u32(cp) {
1774 result.push(c);
1775 i += 10;
1776 continue;
1777 }
1778 }
1779 }
1780 }
1781 result.push(chars[i]);
1782 i += 1;
1783 }
1784 Ok(SqliteValue::Text(Arc::from(result)))
1785 }
1786
1787 fn num_args(&self) -> i32 {
1788 1
1789 }
1790
1791 fn name(&self) -> &str {
1792 "unistr"
1793 }
1794}
1795
1796pub struct ChangesFunc;
1802
1803impl ScalarFunction for ChangesFunc {
1804 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1805 Ok(SqliteValue::Integer(LAST_CHANGES.get()))
1806 }
1807
1808 fn is_deterministic(&self) -> bool {
1809 false
1810 }
1811
1812 fn num_args(&self) -> i32 {
1813 0
1814 }
1815
1816 fn name(&self) -> &str {
1817 "changes"
1818 }
1819}
1820
1821pub struct TotalChangesFunc;
1822
1823impl ScalarFunction for TotalChangesFunc {
1824 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1825 Ok(SqliteValue::Integer(TOTAL_CHANGES.get()))
1826 }
1827
1828 fn is_deterministic(&self) -> bool {
1829 false
1830 }
1831
1832 fn num_args(&self) -> i32 {
1833 0
1834 }
1835
1836 fn name(&self) -> &str {
1837 "total_changes"
1838 }
1839}
1840
1841pub struct LastInsertRowidFunc;
1842
1843impl ScalarFunction for LastInsertRowidFunc {
1844 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
1845 Ok(SqliteValue::Integer(LAST_INSERT_ROWID.get()))
1846 }
1847
1848 fn is_deterministic(&self) -> bool {
1849 false
1850 }
1851
1852 fn num_args(&self) -> i32 {
1853 0
1854 }
1855
1856 fn name(&self) -> &str {
1857 "last_insert_rowid"
1858 }
1859}
1860
1861#[allow(clippy::too_many_lines)]
1865pub fn register_builtins(registry: &mut FunctionRegistry) {
1866 registry.register_scalar(AbsFunc);
1868 registry.register_scalar(SignFunc);
1869 registry.register_scalar(RoundFunc);
1870 registry.register_scalar(RandomFunc);
1871 registry.register_scalar(RandomblobFunc);
1872 registry.register_scalar(ZeroblobFunc);
1873
1874 registry.register_scalar(LowerFunc);
1876 registry.register_scalar(UpperFunc);
1877 registry.register_scalar(LengthFunc);
1878 registry.register_scalar(OctetLengthFunc);
1879 registry.register_scalar(TrimFunc);
1880 registry.register_scalar(LtrimFunc);
1881 registry.register_scalar(RtrimFunc);
1882 registry.register_scalar(ReplaceFunc);
1883 registry.register_scalar(SubstrFunc);
1884 registry.register_scalar(InstrFunc);
1885 registry.register_scalar(CharFunc);
1886 registry.register_scalar(UnicodeFunc);
1887 registry.register_scalar(UnistrFunc);
1888 registry.register_scalar(HexFunc);
1889 registry.register_scalar(UnhexFunc);
1890 registry.register_scalar(QuoteFunc);
1891 registry.register_scalar(SoundexFunc);
1892
1893 registry.register_scalar(TypeofFunc);
1895 registry.register_scalar(SubtypeFunc);
1896
1897 registry.register_scalar(CoalesceFunc);
1899 registry.register_scalar(IfnullFunc);
1900 registry.register_scalar(NullifFunc);
1901 registry.register_scalar(IifFunc);
1902
1903 registry.register_scalar(ConcatFunc);
1905 registry.register_scalar(ConcatWsFunc);
1906 registry.register_scalar(ScalarMaxFunc);
1907 registry.register_scalar(ScalarMinFunc);
1908
1909 registry.register_scalar(LikelihoodFunc);
1911 registry.register_scalar(LikelyFunc);
1912 registry.register_scalar(UnlikelyFunc);
1913
1914 registry.register_scalar(LikeFunc);
1916 registry.register_scalar(GlobFunc);
1917
1918 registry.register_scalar(SqliteVersionFunc);
1920 registry.register_scalar(SqliteSourceIdFunc);
1921 registry.register_scalar(SqliteCompileoptionUsedFunc);
1922 registry.register_scalar(SqliteCompileoptionGetFunc);
1923
1924 registry.register_scalar(ChangesFunc);
1926 registry.register_scalar(TotalChangesFunc);
1927 registry.register_scalar(LastInsertRowidFunc);
1928
1929 struct IfFunc;
1932 impl ScalarFunction for IfFunc {
1933 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1934 IifFunc.invoke(args)
1935 }
1936
1937 fn num_args(&self) -> i32 {
1938 3
1939 }
1940
1941 fn name(&self) -> &str {
1942 "if"
1943 }
1944 }
1945 registry.register_scalar(IfFunc);
1946
1947 struct SubstringFunc;
1949 impl ScalarFunction for SubstringFunc {
1950 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1951 SubstrFunc.invoke(args)
1952 }
1953
1954 fn num_args(&self) -> i32 {
1955 -1
1956 }
1957
1958 fn name(&self) -> &str {
1959 "substring"
1960 }
1961 }
1962 registry.register_scalar(SubstringFunc);
1963
1964 struct PrintfFunc;
1966 impl ScalarFunction for PrintfFunc {
1967 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1968 FormatFunc.invoke(args)
1969 }
1970
1971 fn num_args(&self) -> i32 {
1972 -1
1973 }
1974
1975 fn name(&self) -> &str {
1976 "printf"
1977 }
1978 }
1979 registry.register_scalar(FormatFunc);
1980 registry.register_scalar(PrintfFunc);
1981
1982 register_math_builtins(registry);
1984
1985 register_datetime_builtins(registry);
1987
1988 register_aggregate_builtins(registry);
1990}
1991
1992pub struct FormatFunc;
1995
1996impl ScalarFunction for FormatFunc {
1997 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1998 if args.is_empty() || args[0].is_null() {
1999 return Ok(SqliteValue::Null);
2000 }
2001 let fmt_str = args[0].to_text();
2002 let params = &args[1..];
2003 let result = sqlite_format(&fmt_str, params)?;
2004 Ok(SqliteValue::Text(Arc::from(result)))
2005 }
2006
2007 fn num_args(&self) -> i32 {
2008 -1
2009 }
2010
2011 fn name(&self) -> &str {
2012 "format"
2013 }
2014}
2015
2016fn sqlite_format(fmt: &str, params: &[SqliteValue]) -> Result<String> {
2019 let mut result = String::new();
2020 let chars: Vec<char> = fmt.chars().collect();
2021 let mut i = 0;
2022 let mut param_idx = 0;
2023
2024 while i < chars.len() {
2025 if chars[i] != '%' {
2026 result.push(chars[i]);
2027 i += 1;
2028 continue;
2029 }
2030 i += 1;
2031 if i >= chars.len() {
2032 break;
2033 }
2034
2035 let mut left_align = false;
2037 let mut show_sign = false;
2038 let mut space_sign = false;
2039 let mut zero_pad = false;
2040 loop {
2041 if i >= chars.len() {
2042 break;
2043 }
2044 match chars[i] {
2045 '-' => left_align = true,
2046 '+' => show_sign = true,
2047 ' ' => space_sign = true,
2048 '0' => zero_pad = true,
2049 _ => break,
2050 }
2051 i += 1;
2052 }
2053
2054 let mut width = 0usize;
2056 while i < chars.len() && chars[i].is_ascii_digit() {
2057 width = width
2058 .saturating_mul(10)
2059 .saturating_add(chars[i] as usize - '0' as usize)
2060 .min(100_000_000); i += 1;
2062 }
2063
2064 let mut precision = None;
2066 if i < chars.len() && chars[i] == '.' {
2067 i += 1;
2068 let mut prec = 0usize;
2069 while i < chars.len() && chars[i].is_ascii_digit() {
2070 prec = prec
2071 .saturating_mul(10)
2072 .saturating_add(chars[i] as usize - '0' as usize)
2073 .min(100_000_000); i += 1;
2075 }
2076 precision = Some(prec);
2077 }
2078
2079 if i >= chars.len() {
2080 break;
2081 }
2082
2083 let spec = chars[i];
2084 i += 1;
2085
2086 match spec {
2087 '%' => result.push('%'),
2088 'n' => {} 'd' | 'i' => {
2090 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
2091 param_idx += 1;
2092 let formatted =
2093 format_integer(val, width, left_align, show_sign, space_sign, zero_pad);
2094 result.push_str(&formatted);
2095 }
2096 'f' => {
2097 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
2098 param_idx += 1;
2099 let prec = precision.unwrap_or(6);
2100 let formatted = format_float_f(
2101 val, prec, width, left_align, show_sign, space_sign, zero_pad,
2102 );
2103 result.push_str(&formatted);
2104 }
2105 'e' | 'E' => {
2106 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
2107 param_idx += 1;
2108 let prec = precision.unwrap_or(6);
2109 let raw = if spec == 'e' {
2110 format!("{val:.prec$e}")
2111 } else {
2112 format!("{val:.prec$E}")
2113 };
2114 let formatted = normalize_exponent(&raw);
2116 result.push_str(&pad_string(&formatted, width, left_align));
2117 }
2118 'g' | 'G' => {
2119 let val = params.get(param_idx).map_or(0.0, SqliteValue::to_float);
2120 param_idx += 1;
2121 let prec = precision.unwrap_or(6);
2122 let sig = prec.max(1);
2123 let formatted = format_float_g(val, sig, spec == 'G');
2124 result.push_str(&pad_string(&formatted, width, left_align));
2125 }
2126 's' | 'z' => {
2127 let param = params.get(param_idx);
2128 param_idx += 1;
2129 let val = match param {
2130 Some(SqliteValue::Null) | None => String::new(),
2131 Some(v) => v.to_text(),
2132 };
2133 let truncated = if let Some(prec) = precision {
2134 val.chars().take(prec).collect::<String>()
2135 } else {
2136 val
2137 };
2138 result.push_str(&pad_string(&truncated, width, left_align));
2139 }
2140 'q' => {
2141 let param = params.get(param_idx);
2143 param_idx += 1;
2144 match param {
2145 Some(SqliteValue::Null) | None => result.push_str("(NULL)"),
2146 Some(v) => {
2147 let val = v.to_text();
2148 let escaped = val.replace('\'', "''");
2149 result.push_str(&escaped);
2150 }
2151 }
2152 }
2153 'Q' => {
2154 let param = params.get(param_idx);
2156 param_idx += 1;
2157 match param {
2158 Some(SqliteValue::Null) | None => result.push_str("NULL"),
2159 Some(v) => {
2160 let val = v.to_text();
2161 let escaped = val.replace('\'', "''");
2162 result.push('\'');
2163 result.push_str(&escaped);
2164 result.push('\'');
2165 }
2166 }
2167 }
2168 'w' => {
2169 let param = params.get(param_idx);
2172 param_idx += 1;
2173 if matches!(param, Some(SqliteValue::Null) | None) {
2174 result.push_str("(NULL)");
2175 } else {
2176 let val = param.map(SqliteValue::to_text).unwrap_or_default();
2177 let escaped = val.replace('"', "\"\"");
2178 result.push_str(&escaped);
2179 }
2180 }
2181 'x' | 'X' => {
2182 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
2183 param_idx += 1;
2184 #[allow(clippy::cast_sign_loss)]
2185 let formatted = if spec == 'x' {
2186 format!("{:x}", val as u64)
2187 } else {
2188 format!("{:X}", val as u64)
2189 };
2190 let padded = if zero_pad && width > formatted.len() {
2191 let pad = "0".repeat(width - formatted.len());
2192 format!("{pad}{formatted}")
2193 } else {
2194 pad_string(&formatted, width, left_align)
2195 };
2196 result.push_str(&padded);
2197 }
2198 'o' => {
2199 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
2200 param_idx += 1;
2201 #[allow(clippy::cast_sign_loss)]
2202 let formatted = format!("{:o}", val as u64);
2203 let padded = if zero_pad && width > formatted.len() {
2204 let pad = "0".repeat(width - formatted.len());
2205 format!("{pad}{formatted}")
2206 } else {
2207 pad_string(&formatted, width, left_align)
2208 };
2209 result.push_str(&padded);
2210 }
2211 'c' => {
2212 let val = params.get(param_idx).map_or(0, SqliteValue::to_integer);
2213 param_idx += 1;
2214 #[allow(clippy::cast_sign_loss)]
2215 if let Some(c) = char::from_u32(val as u32) {
2216 result.push(c);
2217 }
2218 }
2219 _ => {
2220 result.push('%');
2222 result.push(spec);
2223 }
2224 }
2225 let _ = (left_align, show_sign, space_sign, zero_pad);
2227 }
2228 Ok(result)
2229}
2230
2231fn format_integer(
2232 val: i64,
2233 width: usize,
2234 left_align: bool,
2235 show_sign: bool,
2236 space_sign: bool,
2237 zero_pad: bool,
2238) -> String {
2239 let sign = if val < 0 {
2240 "-".to_owned()
2241 } else if show_sign {
2242 "+".to_owned()
2243 } else if space_sign {
2244 " ".to_owned()
2245 } else {
2246 String::new()
2247 };
2248 let digits = format!("{}", val.unsigned_abs());
2249 let body = format!("{sign}{digits}");
2250 if body.len() >= width {
2251 return body;
2252 }
2253 let pad = width - body.len();
2254 if left_align {
2255 format!("{body}{}", " ".repeat(pad))
2256 } else if zero_pad {
2257 format!("{sign}{}{digits}", "0".repeat(pad))
2258 } else {
2259 format!("{}{body}", " ".repeat(pad))
2260 }
2261}
2262
2263fn format_float_f(
2264 val: f64,
2265 prec: usize,
2266 width: usize,
2267 left_align: bool,
2268 show_sign: bool,
2269 space_sign: bool,
2270 zero_pad: bool,
2271) -> String {
2272 let sign = if val < 0.0 {
2273 "-".to_owned()
2274 } else if show_sign {
2275 "+".to_owned()
2276 } else if space_sign {
2277 " ".to_owned()
2278 } else {
2279 String::new()
2280 };
2281 let digits = format!("{:.prec$}", val.abs());
2282 let body = format!("{sign}{digits}");
2283 if body.len() >= width {
2284 return body;
2285 }
2286 let pad = width - body.len();
2287 if left_align {
2288 format!("{body}{}", " ".repeat(pad))
2289 } else if zero_pad {
2290 format!("{sign}{}{digits}", "0".repeat(pad))
2291 } else {
2292 format!("{}{body}", " ".repeat(pad))
2293 }
2294}
2295
2296fn pad_string(s: &str, width: usize, left_align: bool) -> String {
2297 if s.len() >= width {
2298 return s.to_owned();
2299 }
2300 let pad = width - s.len();
2301 if left_align {
2302 format!("{s}{}", " ".repeat(pad))
2303 } else {
2304 format!("{}{s}", " ".repeat(pad))
2305 }
2306}
2307
2308fn normalize_exponent(s: &str) -> String {
2311 let (prefix, e_char, exp_part) = if let Some(pos) = s.find('e') {
2312 (&s[..pos], 'e', &s[pos + 1..])
2313 } else if let Some(pos) = s.find('E') {
2314 (&s[..pos], 'E', &s[pos + 1..])
2315 } else {
2316 return s.to_owned();
2317 };
2318 let (sign, digits) = if let Some(rest) = exp_part.strip_prefix('-') {
2319 ("-", rest)
2320 } else if let Some(rest) = exp_part.strip_prefix('+') {
2321 ("+", rest)
2322 } else {
2323 ("+", exp_part)
2324 };
2325 let padded = if digits.len() < 2 {
2326 format!("0{digits}")
2327 } else {
2328 digits.to_owned()
2329 };
2330 format!("{prefix}{e_char}{sign}{padded}")
2331}
2332
2333fn format_float_g(val: f64, sig: usize, upper: bool) -> String {
2335 if !val.is_finite() {
2336 return format!("{val}");
2337 }
2338 let e_str = format!("{val:.prec$e}", prec = sig.saturating_sub(1));
2339 let exp: i32 = e_str
2340 .rsplit_once('e')
2341 .and_then(|(_, e)| e.parse().ok())
2342 .unwrap_or(0);
2343 #[allow(clippy::cast_possible_wrap)]
2344 let formatted = if exp < -4 || exp >= sig as i32 {
2345 let s = format!("{val:.prec$e}", prec = sig.saturating_sub(1));
2346 let s = if upper { s.replace('e', "E") } else { s };
2347 let trimmed = if s.contains('.') {
2349 if let Some(e_pos) = s.find('e').or_else(|| s.find('E')) {
2350 let mantissa = s[..e_pos].trim_end_matches('0').trim_end_matches('.');
2351 format!("{mantissa}{}", &s[e_pos..])
2352 } else {
2353 s.trim_end_matches('0').trim_end_matches('.').to_owned()
2354 }
2355 } else {
2356 s
2357 };
2358 normalize_exponent(&trimmed)
2359 } else {
2360 let decimal_places = if exp >= 0 {
2361 sig.saturating_sub((exp + 1) as usize)
2362 } else {
2363 sig + exp.unsigned_abs() as usize - 1
2364 };
2365 let s = format!("{val:.decimal_places$}");
2366 s.trim_end_matches('0').trim_end_matches('.').to_owned()
2367 };
2368 formatted
2369}
2370
2371#[cfg(test)]
2372#[allow(clippy::too_many_lines)]
2373mod tests {
2374 use super::*;
2375
2376 fn invoke1(f: &dyn ScalarFunction, v: SqliteValue) -> Result<SqliteValue> {
2377 f.invoke(&[v])
2378 }
2379
2380 fn invoke2(f: &dyn ScalarFunction, a: SqliteValue, b: SqliteValue) -> Result<SqliteValue> {
2381 f.invoke(&[a, b])
2382 }
2383
2384 #[test]
2385 fn test_get_change_tracking_state_returns_thread_local_snapshot() {
2386 let original = get_change_tracking_state();
2387 let expected = ChangeTrackingState {
2388 last_insert_rowid: 17,
2389 last_changes: 23,
2390 total_changes: 42,
2391 };
2392
2393 set_change_tracking_state(expected);
2394 assert_eq!(get_change_tracking_state(), expected);
2395
2396 set_change_tracking_state(original);
2397 }
2398
2399 #[test]
2402 fn test_abs_positive() {
2403 assert_eq!(
2404 invoke1(&AbsFunc, SqliteValue::Integer(42)).unwrap(),
2405 SqliteValue::Integer(42)
2406 );
2407 }
2408
2409 #[test]
2410 fn test_abs_negative() {
2411 assert_eq!(
2412 invoke1(&AbsFunc, SqliteValue::Integer(-42)).unwrap(),
2413 SqliteValue::Integer(42)
2414 );
2415 }
2416
2417 #[test]
2418 fn test_abs_null() {
2419 assert_eq!(
2420 invoke1(&AbsFunc, SqliteValue::Null).unwrap(),
2421 SqliteValue::Null
2422 );
2423 }
2424
2425 #[test]
2426 fn test_abs_min_i64_overflow() {
2427 let err = invoke1(&AbsFunc, SqliteValue::Integer(i64::MIN)).unwrap_err();
2428 assert!(matches!(err, FrankenError::IntegerOverflow));
2429 }
2430
2431 #[test]
2432 fn test_abs_string_coercion() {
2433 assert_eq!(
2434 invoke1(&AbsFunc, SqliteValue::Text(Arc::from("-7.5"))).unwrap(),
2435 SqliteValue::Float(7.5)
2436 );
2437 }
2438
2439 #[test]
2440 fn test_abs_whitespace_padded_text() {
2441 assert_eq!(
2443 invoke1(&AbsFunc, SqliteValue::Text(Arc::from(" 42 "))).unwrap(),
2444 SqliteValue::Float(42.0)
2445 );
2446 assert_eq!(
2447 invoke1(&AbsFunc, SqliteValue::Text(Arc::from(" -7.5 "))).unwrap(),
2448 SqliteValue::Float(7.5)
2449 );
2450 assert_eq!(
2451 invoke1(&AbsFunc, SqliteValue::Text(Arc::from("abc"))).unwrap(),
2452 SqliteValue::Float(0.0)
2453 );
2454 }
2455
2456 #[test]
2457 #[allow(clippy::approx_constant)]
2458 fn test_abs_float() {
2459 assert_eq!(
2460 invoke1(&AbsFunc, SqliteValue::Float(-3.14)).unwrap(),
2461 SqliteValue::Float(3.14)
2462 );
2463 }
2464
2465 #[test]
2468 fn test_char_basic() {
2469 let f = CharFunc;
2470 let result = f
2471 .invoke(&[
2472 SqliteValue::Integer(72),
2473 SqliteValue::Integer(101),
2474 SqliteValue::Integer(108),
2475 SqliteValue::Integer(108),
2476 SqliteValue::Integer(111),
2477 ])
2478 .unwrap();
2479 assert_eq!(result, SqliteValue::Text(Arc::from("Hello")));
2480 }
2481
2482 #[test]
2483 fn test_char_null_skipped() {
2484 let f = CharFunc;
2485 let result = f
2487 .invoke(&[
2488 SqliteValue::Integer(65),
2489 SqliteValue::Null,
2490 SqliteValue::Integer(66),
2491 ])
2492 .unwrap();
2493 assert_eq!(result, SqliteValue::Text(Arc::from("A\0B")));
2494 }
2495
2496 #[test]
2499 fn test_coalesce_first_non_null() {
2500 let f = CoalesceFunc;
2501 let result = f
2502 .invoke(&[
2503 SqliteValue::Null,
2504 SqliteValue::Null,
2505 SqliteValue::Integer(3),
2506 SqliteValue::Integer(4),
2507 ])
2508 .unwrap();
2509 assert_eq!(result, SqliteValue::Integer(3));
2510 }
2511
2512 #[test]
2515 fn test_concat_null_as_empty() {
2516 let f = ConcatFunc;
2517 let result = f
2518 .invoke(&[
2519 SqliteValue::Null,
2520 SqliteValue::Text(Arc::from("hello")),
2521 SqliteValue::Null,
2522 ])
2523 .unwrap();
2524 assert_eq!(result, SqliteValue::Text(Arc::from("hello")));
2525 }
2526
2527 #[test]
2530 fn test_concat_ws_null_skipped() {
2531 let f = ConcatWsFunc;
2532 let result = f
2533 .invoke(&[
2534 SqliteValue::Text(Arc::from(",")),
2535 SqliteValue::Text(Arc::from("a")),
2536 SqliteValue::Null,
2537 SqliteValue::Text(Arc::from("b")),
2538 ])
2539 .unwrap();
2540 assert_eq!(result, SqliteValue::Text(Arc::from("a,b")));
2541 }
2542
2543 #[test]
2546 fn test_hex_blob() {
2547 let result = invoke1(
2548 &HexFunc,
2549 SqliteValue::Blob(Arc::from([0xDE, 0xAD, 0xBE, 0xEF].as_slice())),
2550 )
2551 .unwrap();
2552 assert_eq!(result, SqliteValue::Text(Arc::from("DEADBEEF")));
2553 }
2554
2555 #[test]
2556 fn test_hex_number_via_text() {
2557 let result = invoke1(&HexFunc, SqliteValue::Integer(42)).unwrap();
2559 assert_eq!(result, SqliteValue::Text(Arc::from("3432")));
2560 }
2561
2562 #[test]
2565 fn test_iif_true() {
2566 let f = IifFunc;
2567 let result = f
2568 .invoke(&[
2569 SqliteValue::Integer(1),
2570 SqliteValue::Text(Arc::from("yes")),
2571 SqliteValue::Text(Arc::from("no")),
2572 ])
2573 .unwrap();
2574 assert_eq!(result, SqliteValue::Text(Arc::from("yes")));
2575 }
2576
2577 #[test]
2578 fn test_iif_false() {
2579 let f = IifFunc;
2580 let result = f
2581 .invoke(&[
2582 SqliteValue::Integer(0),
2583 SqliteValue::Text(Arc::from("yes")),
2584 SqliteValue::Text(Arc::from("no")),
2585 ])
2586 .unwrap();
2587 assert_eq!(result, SqliteValue::Text(Arc::from("no")));
2588 }
2589
2590 #[test]
2591 fn test_iif_whitespace_padded_text_truthy() {
2592 let f = IifFunc;
2595 let result = f
2596 .invoke(&[
2597 SqliteValue::Text(Arc::from(" 5 ")),
2598 SqliteValue::Text(Arc::from("yes")),
2599 SqliteValue::Text(Arc::from("no")),
2600 ])
2601 .unwrap();
2602 assert_eq!(result, SqliteValue::Text(Arc::from("yes")));
2603 }
2604
2605 #[test]
2608 fn test_ifnull_non_null() {
2609 assert_eq!(
2610 invoke2(
2611 &IfnullFunc,
2612 SqliteValue::Integer(5),
2613 SqliteValue::Integer(10)
2614 )
2615 .unwrap(),
2616 SqliteValue::Integer(5)
2617 );
2618 }
2619
2620 #[test]
2621 fn test_ifnull_null() {
2622 assert_eq!(
2623 invoke2(&IfnullFunc, SqliteValue::Null, SqliteValue::Integer(10)).unwrap(),
2624 SqliteValue::Integer(10)
2625 );
2626 }
2627
2628 #[test]
2631 fn test_instr_found() {
2632 assert_eq!(
2633 invoke2(
2634 &InstrFunc,
2635 SqliteValue::Text(Arc::from("hello world")),
2636 SqliteValue::Text(Arc::from("world"))
2637 )
2638 .unwrap(),
2639 SqliteValue::Integer(7)
2640 );
2641 }
2642
2643 #[test]
2644 fn test_instr_not_found() {
2645 assert_eq!(
2646 invoke2(
2647 &InstrFunc,
2648 SqliteValue::Text(Arc::from("hello")),
2649 SqliteValue::Text(Arc::from("xyz"))
2650 )
2651 .unwrap(),
2652 SqliteValue::Integer(0)
2653 );
2654 }
2655
2656 #[test]
2657 fn test_instr_empty_needle_returns_one() {
2658 assert_eq!(
2660 invoke2(
2661 &InstrFunc,
2662 SqliteValue::Text(Arc::from("hello")),
2663 SqliteValue::Text(Arc::from(""))
2664 )
2665 .unwrap(),
2666 SqliteValue::Integer(1)
2667 );
2668 }
2669
2670 #[test]
2671 fn test_instr_empty_haystack_returns_zero() {
2672 assert_eq!(
2673 invoke2(
2674 &InstrFunc,
2675 SqliteValue::Text(Arc::from("")),
2676 SqliteValue::Text(Arc::from("x"))
2677 )
2678 .unwrap(),
2679 SqliteValue::Integer(0)
2680 );
2681 }
2682
2683 #[test]
2684 fn test_instr_blob_empty_needle_returns_one() {
2685 assert_eq!(
2687 invoke2(
2688 &InstrFunc,
2689 SqliteValue::Blob(Arc::from([1, 2, 3].as_slice())),
2690 SqliteValue::Blob(Arc::from([].as_slice()))
2691 )
2692 .unwrap(),
2693 SqliteValue::Integer(1)
2694 );
2695 }
2696
2697 #[test]
2700 fn test_length_text_chars() {
2701 assert_eq!(
2703 invoke1(&LengthFunc, SqliteValue::Text(Arc::from("café"))).unwrap(),
2704 SqliteValue::Integer(4)
2705 );
2706 }
2707
2708 #[test]
2709 fn test_length_blob_bytes() {
2710 assert_eq!(
2711 invoke1(&LengthFunc, SqliteValue::Blob(Arc::from([1, 2].as_slice()))).unwrap(),
2712 SqliteValue::Integer(2)
2713 );
2714 }
2715
2716 #[test]
2719 fn test_octet_length_multibyte() {
2720 assert_eq!(
2722 invoke1(&OctetLengthFunc, SqliteValue::Text(Arc::from("café"))).unwrap(),
2723 SqliteValue::Integer(5)
2724 );
2725 }
2726
2727 #[test]
2730 fn test_lower_ascii() {
2731 assert_eq!(
2732 invoke1(&LowerFunc, SqliteValue::Text(Arc::from("HELLO"))).unwrap(),
2733 SqliteValue::Text(Arc::from("hello"))
2734 );
2735 }
2736
2737 #[test]
2738 fn test_upper_ascii() {
2739 assert_eq!(
2740 invoke1(&UpperFunc, SqliteValue::Text(Arc::from("hello"))).unwrap(),
2741 SqliteValue::Text(Arc::from("HELLO"))
2742 );
2743 }
2744
2745 #[test]
2748 fn test_trim_default() {
2749 let f = TrimFunc;
2750 assert_eq!(
2751 f.invoke(&[SqliteValue::Text(Arc::from(" hello "))])
2752 .unwrap(),
2753 SqliteValue::Text(Arc::from("hello"))
2754 );
2755 }
2756
2757 #[test]
2758 fn test_ltrim_default() {
2759 let f = LtrimFunc;
2760 assert_eq!(
2761 f.invoke(&[SqliteValue::Text(Arc::from(" hello"))])
2762 .unwrap(),
2763 SqliteValue::Text(Arc::from("hello"))
2764 );
2765 }
2766
2767 #[test]
2768 fn test_ltrim_custom() {
2769 let f = LtrimFunc;
2770 assert_eq!(
2771 f.invoke(&[
2772 SqliteValue::Text(Arc::from("xxhello")),
2773 SqliteValue::Text(Arc::from("x")),
2774 ])
2775 .unwrap(),
2776 SqliteValue::Text(Arc::from("hello"))
2777 );
2778 }
2779
2780 #[test]
2783 fn test_nullif_equal() {
2784 assert_eq!(
2785 invoke2(
2786 &NullifFunc,
2787 SqliteValue::Integer(5),
2788 SqliteValue::Integer(5)
2789 )
2790 .unwrap(),
2791 SqliteValue::Null
2792 );
2793 }
2794
2795 #[test]
2796 fn test_nullif_different() {
2797 assert_eq!(
2798 invoke2(
2799 &NullifFunc,
2800 SqliteValue::Integer(5),
2801 SqliteValue::Integer(3)
2802 )
2803 .unwrap(),
2804 SqliteValue::Integer(5)
2805 );
2806 }
2807
2808 #[test]
2811 fn test_typeof_each() {
2812 assert_eq!(
2813 invoke1(&TypeofFunc, SqliteValue::Null).unwrap(),
2814 SqliteValue::Text(Arc::from("null"))
2815 );
2816 assert_eq!(
2817 invoke1(&TypeofFunc, SqliteValue::Integer(1)).unwrap(),
2818 SqliteValue::Text(Arc::from("integer"))
2819 );
2820 assert_eq!(
2821 invoke1(&TypeofFunc, SqliteValue::Float(1.0)).unwrap(),
2822 SqliteValue::Text(Arc::from("real"))
2823 );
2824 assert_eq!(
2825 invoke1(&TypeofFunc, SqliteValue::Text(Arc::from("x"))).unwrap(),
2826 SqliteValue::Text(Arc::from("text"))
2827 );
2828 assert_eq!(
2829 invoke1(&TypeofFunc, SqliteValue::Blob(Arc::from([0].as_slice()))).unwrap(),
2830 SqliteValue::Text(Arc::from("blob"))
2831 );
2832 }
2833
2834 #[test]
2837 fn test_subtype_null_returns_zero() {
2838 assert_eq!(
2839 invoke1(&SubtypeFunc, SqliteValue::Null).unwrap(),
2840 SqliteValue::Integer(0)
2841 );
2842 }
2843
2844 #[test]
2847 fn test_replace_basic() {
2848 let f = ReplaceFunc;
2849 assert_eq!(
2850 f.invoke(&[
2851 SqliteValue::Text(Arc::from("hello world")),
2852 SqliteValue::Text(Arc::from("world")),
2853 SqliteValue::Text(Arc::from("earth")),
2854 ])
2855 .unwrap(),
2856 SqliteValue::Text(Arc::from("hello earth"))
2857 );
2858 }
2859
2860 #[test]
2861 fn test_replace_empty_y() {
2862 let f = ReplaceFunc;
2863 assert_eq!(
2864 f.invoke(&[
2865 SqliteValue::Text(Arc::from("hello")),
2866 SqliteValue::Text(Arc::from("")),
2867 SqliteValue::Text(Arc::from("x")),
2868 ])
2869 .unwrap(),
2870 SqliteValue::Text(Arc::from("hello"))
2871 );
2872 }
2873
2874 #[test]
2877 #[allow(clippy::float_cmp)]
2878 fn test_round_half_away() {
2879 assert_eq!(
2881 RoundFunc.invoke(&[SqliteValue::Float(2.5)]).unwrap(),
2882 SqliteValue::Float(3.0)
2883 );
2884 assert_eq!(
2885 RoundFunc.invoke(&[SqliteValue::Float(-2.5)]).unwrap(),
2886 SqliteValue::Float(-3.0)
2887 );
2888 }
2889
2890 #[test]
2891 #[allow(clippy::float_cmp, clippy::approx_constant)]
2892 fn test_round_precision() {
2893 assert_eq!(
2894 RoundFunc
2895 .invoke(&[SqliteValue::Float(3.14159), SqliteValue::Integer(2)])
2896 .unwrap(),
2897 SqliteValue::Float(3.14)
2898 );
2899 }
2900
2901 #[test]
2902 #[allow(clippy::float_cmp)]
2903 fn test_round_extreme_n_clamped() {
2904 assert_eq!(
2906 RoundFunc
2907 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(400)])
2908 .unwrap(),
2909 RoundFunc
2910 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(30)])
2911 .unwrap(),
2912 );
2913 assert_eq!(
2915 RoundFunc
2916 .invoke(&[SqliteValue::Float(2.5), SqliteValue::Integer(-5)])
2917 .unwrap(),
2918 SqliteValue::Float(3.0)
2919 );
2920 let result = RoundFunc
2922 .invoke(&[SqliteValue::Float(1.5), SqliteValue::Integer(i64::MAX)])
2923 .unwrap();
2924 if let SqliteValue::Float(v) = result {
2925 assert!(!v.is_nan(), "round must never return NaN");
2926 }
2927 }
2928
2929 #[test]
2930 #[allow(clippy::float_cmp)]
2931 fn test_round_large_value_no_fractional() {
2932 let big = 9_007_199_254_740_993.0_f64;
2934 assert_eq!(
2935 RoundFunc.invoke(&[SqliteValue::Float(big)]).unwrap(),
2936 SqliteValue::Float(big)
2937 );
2938 assert_eq!(
2939 RoundFunc.invoke(&[SqliteValue::Float(-big)]).unwrap(),
2940 SqliteValue::Float(-big)
2941 );
2942 }
2943
2944 #[test]
2947 fn test_sign_positive() {
2948 assert_eq!(
2949 invoke1(&SignFunc, SqliteValue::Integer(42)).unwrap(),
2950 SqliteValue::Integer(1)
2951 );
2952 }
2953
2954 #[test]
2955 fn test_sign_negative() {
2956 assert_eq!(
2957 invoke1(&SignFunc, SqliteValue::Integer(-42)).unwrap(),
2958 SqliteValue::Integer(-1)
2959 );
2960 }
2961
2962 #[test]
2963 fn test_sign_zero() {
2964 assert_eq!(
2965 invoke1(&SignFunc, SqliteValue::Integer(0)).unwrap(),
2966 SqliteValue::Integer(0)
2967 );
2968 }
2969
2970 #[test]
2971 fn test_sign_null() {
2972 assert_eq!(
2973 invoke1(&SignFunc, SqliteValue::Null).unwrap(),
2974 SqliteValue::Null
2975 );
2976 }
2977
2978 #[test]
2979 fn test_sign_non_numeric() {
2980 assert_eq!(
2982 invoke1(&SignFunc, SqliteValue::Text(Arc::from("abc"))).unwrap(),
2983 SqliteValue::Null
2984 );
2985 }
2986
2987 #[test]
2988 fn test_sign_whitespace_padded_text() {
2989 assert_eq!(
2992 invoke1(&SignFunc, SqliteValue::Text(Arc::from(" 5 "))).unwrap(),
2993 SqliteValue::Integer(1)
2994 );
2995 assert_eq!(
2996 invoke1(&SignFunc, SqliteValue::Text(Arc::from(" -3.14 "))).unwrap(),
2997 SqliteValue::Integer(-1)
2998 );
2999 }
3000
3001 #[test]
3002 fn test_sign_nan_inf_text_returns_null() {
3003 for s in &[
3006 "NaN",
3007 "nan",
3008 "inf",
3009 "-inf",
3010 "Infinity",
3011 "-Infinity",
3012 "INF",
3013 "+nan",
3014 "+inf",
3015 ] {
3016 assert_eq!(
3017 invoke1(&SignFunc, SqliteValue::Text(Arc::from(*s))).unwrap(),
3018 SqliteValue::Null,
3019 "sign('{s}') should be NULL"
3020 );
3021 }
3022 }
3023
3024 #[test]
3025 fn test_sign_numeric_overflow_to_infinity() {
3026 assert_eq!(
3029 invoke1(&SignFunc, SqliteValue::Text(Arc::from("1e999"))).unwrap(),
3030 SqliteValue::Integer(1)
3031 );
3032 assert_eq!(
3033 invoke1(&SignFunc, SqliteValue::Text(Arc::from("-1e999"))).unwrap(),
3034 SqliteValue::Integer(-1)
3035 );
3036 assert_eq!(
3038 invoke1(&SignFunc, SqliteValue::Text(Arc::from("1e-999"))).unwrap(),
3039 SqliteValue::Integer(0)
3040 );
3041 }
3042
3043 #[test]
3044 fn test_sign_float_nan_returns_null() {
3045 assert_eq!(
3047 invoke1(&SignFunc, SqliteValue::Float(f64::NAN)).unwrap(),
3048 SqliteValue::Null
3049 );
3050 }
3051
3052 #[test]
3055 fn test_scalar_max_null() {
3056 let f = ScalarMaxFunc;
3057 let result = f
3058 .invoke(&[
3059 SqliteValue::Integer(1),
3060 SqliteValue::Null,
3061 SqliteValue::Integer(3),
3062 ])
3063 .unwrap();
3064 assert_eq!(result, SqliteValue::Null);
3065 }
3066
3067 #[test]
3068 fn test_scalar_max_values() {
3069 let f = ScalarMaxFunc;
3070 let result = f
3071 .invoke(&[
3072 SqliteValue::Integer(3),
3073 SqliteValue::Integer(1),
3074 SqliteValue::Integer(2),
3075 ])
3076 .unwrap();
3077 assert_eq!(result, SqliteValue::Integer(3));
3078 }
3079
3080 #[test]
3081 fn test_scalar_min_null() {
3082 let f = ScalarMinFunc;
3083 let result = f
3084 .invoke(&[
3085 SqliteValue::Integer(1),
3086 SqliteValue::Null,
3087 SqliteValue::Integer(3),
3088 ])
3089 .unwrap();
3090 assert_eq!(result, SqliteValue::Null);
3091 }
3092
3093 #[test]
3096 fn test_quote_text() {
3097 assert_eq!(
3098 invoke1(&QuoteFunc, SqliteValue::Text(Arc::from("it's"))).unwrap(),
3099 SqliteValue::Text(Arc::from("'it''s'"))
3100 );
3101 }
3102
3103 #[test]
3104 fn test_quote_null() {
3105 assert_eq!(
3106 invoke1(&QuoteFunc, SqliteValue::Null).unwrap(),
3107 SqliteValue::Text(Arc::from("NULL"))
3108 );
3109 }
3110
3111 #[test]
3112 fn test_quote_blob() {
3113 assert_eq!(
3114 invoke1(&QuoteFunc, SqliteValue::Blob(Arc::from([0xAB].as_slice()))).unwrap(),
3115 SqliteValue::Text(Arc::from("X'AB'"))
3116 );
3117 }
3118
3119 #[test]
3122 fn test_random_range() {
3123 let f = RandomFunc;
3124 let result = f.invoke(&[]).unwrap();
3125 assert!(matches!(result, SqliteValue::Integer(_)));
3126 }
3127
3128 #[test]
3131 fn test_randomblob_length() {
3132 let result = invoke1(&RandomblobFunc, SqliteValue::Integer(16)).unwrap();
3133 match result {
3134 SqliteValue::Blob(b) => assert_eq!(b.len(), 16),
3135 other => unreachable!("expected blob, got {other:?}"),
3136 }
3137 }
3138
3139 #[test]
3142 fn test_zeroblob_length() {
3143 let result = invoke1(&ZeroblobFunc, SqliteValue::Integer(100)).unwrap();
3144 match result {
3145 SqliteValue::Blob(b) => {
3146 assert_eq!(b.len(), 100);
3147 assert!(b.iter().all(|&x| x == 0));
3148 }
3149 other => unreachable!("expected blob, got {other:?}"),
3150 }
3151 }
3152
3153 #[test]
3156 fn test_unhex_valid() {
3157 let result = invoke1(&UnhexFunc, SqliteValue::Text(Arc::from("48656C6C6F"))).unwrap();
3158 assert_eq!(result, SqliteValue::Blob(Arc::from(b"Hello".as_slice())));
3159 }
3160
3161 #[test]
3162 fn test_unhex_invalid() {
3163 let result = invoke1(&UnhexFunc, SqliteValue::Text(Arc::from("ZZZZ"))).unwrap();
3164 assert_eq!(result, SqliteValue::Null);
3165 }
3166
3167 #[test]
3168 fn test_unhex_ignore_chars() {
3169 let f = UnhexFunc;
3170 let result = f
3171 .invoke(&[
3172 SqliteValue::Text(Arc::from("48-65-6C")),
3173 SqliteValue::Text(Arc::from("-")),
3174 ])
3175 .unwrap();
3176 assert_eq!(result, SqliteValue::Blob(Arc::from(b"Hel".as_slice())));
3177 }
3178
3179 #[test]
3182 fn test_unicode_first_char() {
3183 assert_eq!(
3184 invoke1(&UnicodeFunc, SqliteValue::Text(Arc::from("A"))).unwrap(),
3185 SqliteValue::Integer(65)
3186 );
3187 }
3188
3189 #[test]
3192 fn test_soundex_basic() {
3193 assert_eq!(
3194 invoke1(&SoundexFunc, SqliteValue::Text(Arc::from("Robert"))).unwrap(),
3195 SqliteValue::Text(Arc::from("R163"))
3196 );
3197 }
3198
3199 #[test]
3202 fn test_substr_basic() {
3203 let f = SubstrFunc;
3204 assert_eq!(
3205 f.invoke(&[
3206 SqliteValue::Text(Arc::from("hello")),
3207 SqliteValue::Integer(2),
3208 SqliteValue::Integer(3),
3209 ])
3210 .unwrap(),
3211 SqliteValue::Text(Arc::from("ell"))
3212 );
3213 }
3214
3215 #[test]
3216 fn test_substr_start_zero_quirk() {
3217 let f = SubstrFunc;
3219 let result = f
3220 .invoke(&[
3221 SqliteValue::Text(Arc::from("hello")),
3222 SqliteValue::Integer(0),
3223 SqliteValue::Integer(3),
3224 ])
3225 .unwrap();
3226 assert_eq!(result, SqliteValue::Text(Arc::from("he")));
3227 }
3228
3229 #[test]
3230 fn test_substr_negative_start() {
3231 let f = SubstrFunc;
3233 let result = f
3234 .invoke(&[
3235 SqliteValue::Text(Arc::from("hello")),
3236 SqliteValue::Integer(-2),
3237 ])
3238 .unwrap();
3239 assert_eq!(result, SqliteValue::Text(Arc::from("lo")));
3240 }
3241
3242 #[test]
3243 fn test_substr_negative_length() {
3244 let f = SubstrFunc;
3245 let t = |s: &str| SqliteValue::Text(Arc::from(s));
3246 let i = SqliteValue::Integer;
3247 assert_eq!(f.invoke(&[t("hello"), i(3), i(-2)]).unwrap(), t("he"));
3249 assert_eq!(f.invoke(&[t("hello"), i(3), i(-5)]).unwrap(), t("he"));
3251 assert_eq!(f.invoke(&[t("hello"), i(1), i(-1)]).unwrap(), t(""));
3253 }
3254
3255 #[test]
3256 fn test_substr_negative_start_negative_length() {
3257 let f = SubstrFunc;
3258 let t = |s: &str| SqliteValue::Text(Arc::from(s));
3259 let i = SqliteValue::Integer;
3260 assert_eq!(f.invoke(&[t("hello"), i(-2), i(-2)]).unwrap(), t("el"));
3262 }
3263
3264 #[test]
3265 fn test_substr_edge_cases() {
3266 let f = SubstrFunc;
3267 let t = |s: &str| SqliteValue::Text(Arc::from(s));
3268 let i = SqliteValue::Integer;
3269 assert_eq!(f.invoke(&[t("hello"), i(6), i(2)]).unwrap(), t(""));
3271 assert_eq!(f.invoke(&[t("hello"), i(-10), i(3)]).unwrap(), t(""));
3273 assert_eq!(f.invoke(&[t("hello"), i(-5), i(6)]).unwrap(), t("hello"));
3275 assert_eq!(f.invoke(&[t("hello"), i(0), i(1)]).unwrap(), t(""));
3277 assert_eq!(f.invoke(&[t("hello"), i(0), i(-1)]).unwrap(), t(""));
3279 assert_eq!(f.invoke(&[t(""), i(1), i(1)]).unwrap(), t(""));
3281 }
3282
3283 #[test]
3284 fn test_substr_blob_negative_length() {
3285 let f = SubstrFunc;
3286 let i = SqliteValue::Integer;
3287 let blob = SqliteValue::Blob(Arc::from([1, 2, 3, 4, 5].as_slice()));
3288 assert_eq!(
3290 f.invoke(&[blob, i(-2), i(-2)]).unwrap(),
3291 SqliteValue::Blob(Arc::from([2, 3].as_slice()))
3292 );
3293 }
3294
3295 #[test]
3298 fn test_like_case_insensitive() {
3299 assert_eq!(
3300 invoke2(
3301 &LikeFunc,
3302 SqliteValue::Text(Arc::from("ABC")),
3303 SqliteValue::Text(Arc::from("abc"))
3304 )
3305 .unwrap(),
3306 SqliteValue::Integer(1)
3307 );
3308 }
3309
3310 #[test]
3311 fn test_like_escape() {
3312 let f = LikeFunc;
3313 let result = f
3314 .invoke(&[
3315 SqliteValue::Text(Arc::from("10\\%")),
3316 SqliteValue::Text(Arc::from("10%")),
3317 SqliteValue::Text(Arc::from("\\")),
3318 ])
3319 .unwrap();
3320 assert_eq!(result, SqliteValue::Integer(1));
3321 }
3322
3323 #[test]
3324 fn test_like_escape_rejects_empty_string() {
3325 let err = LikeFunc
3326 .invoke(&[
3327 SqliteValue::Text(Arc::from("a")),
3328 SqliteValue::Text(Arc::from("a")),
3329 SqliteValue::Text(Arc::from("")),
3330 ])
3331 .unwrap_err();
3332 assert!(
3333 err.to_string()
3334 .contains("ESCAPE expression must be a single character")
3335 );
3336 }
3337
3338 #[test]
3339 fn test_like_escape_rejects_multi_character_string() {
3340 let err = LikeFunc
3341 .invoke(&[
3342 SqliteValue::Text(Arc::from("a")),
3343 SqliteValue::Text(Arc::from("a")),
3344 SqliteValue::Text(Arc::from("xx")),
3345 ])
3346 .unwrap_err();
3347 assert!(
3348 err.to_string()
3349 .contains("ESCAPE expression must be a single character")
3350 );
3351 }
3352
3353 #[test]
3354 fn test_like_percent() {
3355 assert_eq!(
3356 invoke2(
3357 &LikeFunc,
3358 SqliteValue::Text(Arc::from("%ell%")),
3359 SqliteValue::Text(Arc::from("Hello"))
3360 )
3361 .unwrap(),
3362 SqliteValue::Integer(1)
3363 );
3364 }
3365
3366 #[test]
3369 fn test_glob_star() {
3370 assert_eq!(
3371 invoke2(
3372 &GlobFunc,
3373 SqliteValue::Text(Arc::from("*.txt")),
3374 SqliteValue::Text(Arc::from("file.txt"))
3375 )
3376 .unwrap(),
3377 SqliteValue::Integer(1)
3378 );
3379 }
3380
3381 #[test]
3382 fn test_glob_case_sensitive() {
3383 assert_eq!(
3384 invoke2(
3385 &GlobFunc,
3386 SqliteValue::Text(Arc::from("ABC")),
3387 SqliteValue::Text(Arc::from("abc"))
3388 )
3389 .unwrap(),
3390 SqliteValue::Integer(0)
3391 );
3392 }
3393
3394 #[test]
3397 fn test_format_specifiers() {
3398 let f = FormatFunc;
3399 let result = f
3400 .invoke(&[
3401 SqliteValue::Text(Arc::from("%d %s")),
3402 SqliteValue::Integer(42),
3403 SqliteValue::Text(Arc::from("hello")),
3404 ])
3405 .unwrap();
3406 assert_eq!(result, SqliteValue::Text(Arc::from("42 hello")));
3407 }
3408
3409 #[test]
3410 fn test_format_n_noop() {
3411 let f = FormatFunc;
3412 let result = f
3414 .invoke(&[SqliteValue::Text(Arc::from("before%nafter"))])
3415 .unwrap();
3416 assert_eq!(result, SqliteValue::Text(Arc::from("beforeafter")));
3417 }
3418
3419 #[test]
3422 fn test_sqlite_version_format() {
3423 let result = SqliteVersionFunc.invoke(&[]).unwrap();
3424 match result {
3425 SqliteValue::Text(v) => {
3426 assert_eq!(v.split('.').count(), 3, "version must be N.N.N format");
3427 }
3428 other => unreachable!("expected text, got {other:?}"),
3429 }
3430 }
3431
3432 #[test]
3435 fn test_register_builtins_all_present() {
3436 let mut registry = FunctionRegistry::new();
3437 register_builtins(&mut registry);
3438
3439 assert!(registry.find_scalar("abs", 1).is_some());
3441 assert!(registry.find_scalar("typeof", 1).is_some());
3442 assert!(registry.find_scalar("length", 1).is_some());
3443 assert!(registry.find_scalar("lower", 1).is_some());
3444 assert!(registry.find_scalar("upper", 1).is_some());
3445 assert!(registry.find_scalar("hex", 1).is_some());
3446 assert!(registry.find_scalar("coalesce", 3).is_some());
3447 assert!(registry.find_scalar("concat", 2).is_some());
3448 assert!(registry.find_scalar("like", 2).is_some());
3449 assert!(registry.find_scalar("glob", 2).is_some());
3450 assert!(registry.find_scalar("round", 1).is_some());
3451 assert!(registry.find_scalar("substr", 2).is_some());
3452 assert!(registry.find_scalar("substring", 3).is_some());
3453 assert!(registry.find_scalar("sqlite_version", 0).is_some());
3454 assert!(registry.find_scalar("iif", 3).is_some());
3455 assert!(registry.find_scalar("if", 3).is_some());
3456 assert!(registry.find_scalar("format", 1).is_some());
3457 assert!(registry.find_scalar("printf", 1).is_some());
3458 assert!(registry.find_scalar("max", 2).is_some());
3459 assert!(registry.find_scalar("min", 2).is_some());
3460 assert!(registry.find_scalar("sign", 1).is_some());
3461 assert!(registry.find_scalar("random", 0).is_some());
3462
3463 assert!(registry.find_scalar("concat_ws", 3).is_some());
3465 assert!(registry.find_scalar("octet_length", 1).is_some());
3466 assert!(registry.find_scalar("unhex", 1).is_some());
3467 assert!(registry.find_scalar("timediff", 2).is_some());
3468 assert!(registry.find_scalar("unistr", 1).is_some());
3469
3470 assert!(registry.find_aggregate("median", 1).is_some());
3472 assert!(registry.find_aggregate("percentile", 2).is_some());
3473 assert!(registry.find_aggregate("percentile_cont", 2).is_some());
3474 assert!(registry.find_aggregate("percentile_disc", 2).is_some());
3475
3476 assert!(registry.find_scalar("load_extension", 1).is_none());
3478 assert!(registry.find_scalar("load_extension", 2).is_none());
3479 }
3480
3481 #[test]
3482 fn test_e2e_registry_invoke_through_lookup() {
3483 let mut registry = FunctionRegistry::new();
3484 register_builtins(&mut registry);
3485
3486 let abs = registry.find_scalar("ABS", 1).unwrap();
3488 assert_eq!(
3489 abs.invoke(&[SqliteValue::Integer(-42)]).unwrap(),
3490 SqliteValue::Integer(42)
3491 );
3492
3493 let typeof_fn = registry.find_scalar("typeof", 1).unwrap();
3495 assert_eq!(
3496 typeof_fn
3497 .invoke(&[SqliteValue::Text(Arc::from("hello"))])
3498 .unwrap(),
3499 SqliteValue::Text(Arc::from("text"))
3500 );
3501
3502 let coalesce = registry.find_scalar("COALESCE", 4).unwrap();
3504 assert_eq!(
3505 coalesce
3506 .invoke(&[
3507 SqliteValue::Null,
3508 SqliteValue::Null,
3509 SqliteValue::Integer(42),
3510 SqliteValue::Integer(99),
3511 ])
3512 .unwrap(),
3513 SqliteValue::Integer(42)
3514 );
3515 }
3516
3517 #[test]
3520 fn test_nondeterministic_functions_flagged() {
3521 assert!(!RandomFunc.is_deterministic());
3524 assert!(!RandomblobFunc.is_deterministic());
3525 assert!(!ChangesFunc.is_deterministic());
3526 assert!(!TotalChangesFunc.is_deterministic());
3527 assert!(!LastInsertRowidFunc.is_deterministic());
3528 }
3529
3530 #[test]
3531 fn test_deterministic_functions_flagged() {
3532 assert!(AbsFunc.is_deterministic());
3534 assert!(LengthFunc.is_deterministic());
3535 assert!(TypeofFunc.is_deterministic());
3536 assert!(UpperFunc.is_deterministic());
3537 assert!(LowerFunc.is_deterministic());
3538 assert!(HexFunc.is_deterministic());
3539 assert!(CoalesceFunc.is_deterministic());
3540 assert!(IifFunc.is_deterministic());
3541 }
3542
3543 #[test]
3544 fn test_random_produces_different_values() {
3545 let a = RandomFunc.invoke(&[]).unwrap();
3548 let b = RandomFunc.invoke(&[]).unwrap();
3549 assert_ne!(a.as_integer(), b.as_integer());
3552 }
3553
3554 #[test]
3555 fn test_registry_nondeterministic_lookup() {
3556 let mut registry = FunctionRegistry::default();
3557 register_builtins(&mut registry);
3558
3559 let random = registry.find_scalar("random", 0).unwrap();
3561 assert!(!random.is_deterministic());
3562
3563 let changes = registry.find_scalar("changes", 0).unwrap();
3564 assert!(!changes.is_deterministic());
3565
3566 let lir = registry.find_scalar("last_insert_rowid", 0).unwrap();
3567 assert!(!lir.is_deterministic());
3568
3569 let abs = registry.find_scalar("abs", 1).unwrap();
3571 assert!(abs.is_deterministic());
3572 }
3573}