1use std::cmp::Ordering;
16
17use fsqlite_error::{FrankenError, Result};
18use fsqlite_func::FunctionRegistry;
19use fsqlite_func::scalar::ScalarFunction;
20use fsqlite_func::vtab::{ColumnContext, IndexInfo, VirtualTable, VirtualTableCursor};
21use fsqlite_types::SqliteValue;
22use fsqlite_types::cx::Cx;
23use tracing::{debug, info};
24
25#[must_use]
26pub const fn extension_name() -> &'static str {
27 "misc"
28}
29
30pub struct GenerateSeriesTable;
39
40impl VirtualTable for GenerateSeriesTable {
41 type Cursor = GenerateSeriesCursor;
42
43 fn create(_cx: &Cx, _args: &[&str]) -> Result<Self> {
44 Ok(Self)
45 }
46
47 fn connect(_cx: &Cx, _args: &[&str]) -> Result<Self> {
48 Ok(Self)
49 }
50
51 fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
52 info.estimated_cost = 1.0;
55 info.estimated_rows = 1000;
56 Ok(())
57 }
58
59 fn open(&self) -> Result<Self::Cursor> {
60 Ok(GenerateSeriesCursor {
61 current: 0,
62 stop: 0,
63 step: 1,
64 done: true,
65 })
66 }
67}
68
69pub struct GenerateSeriesCursor {
71 current: i64,
72 stop: i64,
73 step: i64,
74 done: bool,
75}
76
77impl GenerateSeriesCursor {
78 #[allow(clippy::similar_names)]
80 pub fn init(&mut self, start: i64, stop: i64, step: i64) -> Result<()> {
81 if step == 0 {
82 return Err(FrankenError::internal(
83 "generate_series: step cannot be zero",
84 ));
85 }
86 self.current = start;
87 self.stop = stop;
88 self.step = step;
89 self.done = if step > 0 { start > stop } else { start < stop };
90 debug!(start, stop, step, "generate_series: initialized cursor");
91 Ok(())
92 }
93}
94
95impl VirtualTableCursor for GenerateSeriesCursor {
96 fn filter(
97 &mut self,
98 _cx: &Cx,
99 _idx_num: i32,
100 _idx_str: Option<&str>,
101 args: &[SqliteValue],
102 ) -> Result<()> {
103 #[allow(clippy::similar_names)]
104 let start = args.first().and_then(SqliteValue::as_integer).unwrap_or(0);
105 let end = args.get(1).and_then(SqliteValue::as_integer).unwrap_or(0);
106 let step = args.get(2).and_then(SqliteValue::as_integer).unwrap_or(1);
107 self.init(start, end, step)
108 }
109
110 fn next(&mut self, _cx: &Cx) -> Result<()> {
111 if self.done {
112 return Ok(());
113 }
114 self.current = self.current.saturating_add(self.step);
116 self.done = if self.step > 0 {
117 self.current > self.stop
118 } else {
119 self.current < self.stop
120 };
121 Ok(())
122 }
123
124 fn eof(&self) -> bool {
125 self.done
126 }
127
128 fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
129 let val = match col {
130 0 => SqliteValue::Integer(self.current), 1 => SqliteValue::Integer(self.current - self.step), 2 => SqliteValue::Integer(self.stop), 3 => SqliteValue::Integer(self.step), _ => SqliteValue::Null,
135 };
136 ctx.set_value(val);
137 Ok(())
138 }
139
140 fn rowid(&self) -> Result<i64> {
141 Ok(self.current)
142 }
143}
144
145fn decimal_normalize(s: &str) -> String {
154 let s = s.trim();
155 let (negative, s) = if let Some(stripped) = s.strip_prefix('-') {
156 (true, stripped)
157 } else {
158 (false, s)
159 };
160
161 let (int_part, frac_part) = match s.split_once('.') {
162 Some((i, f)) => (i, Some(f)),
163 None => (s, None),
164 };
165
166 let int_part = int_part.trim_start_matches('0');
168 let int_part = if int_part.is_empty() { "0" } else { int_part };
169
170 let result = match frac_part {
172 Some(f) => {
173 let f = f.trim_end_matches('0');
174 if f.is_empty() {
175 int_part.to_owned()
176 } else {
177 format!("{int_part}.{f}")
178 }
179 }
180 None => int_part.to_owned(),
181 };
182
183 if negative && result != "0" {
184 format!("-{result}")
185 } else {
186 result
187 }
188}
189
190fn parse_decimal(s: &str) -> (bool, Vec<u8>, Vec<u8>) {
192 let s = s.trim();
193 let (negative, s) = if let Some(stripped) = s.strip_prefix('-') {
194 (true, stripped)
195 } else {
196 (false, s)
197 };
198
199 let (int_str, frac_str) = match s.split_once('.') {
200 Some((i, f)) => (i, f),
201 None => (s, ""),
202 };
203
204 let int_digits: Vec<u8> = int_str.bytes().map(|b| b - b'0').collect();
205 let frac_digits: Vec<u8> = frac_str.bytes().map(|b| b - b'0').collect();
206
207 (negative, int_digits, frac_digits)
208}
209
210fn add_unsigned(int_a: &[u8], frac_a: &[u8], int_b: &[u8], frac_b: &[u8]) -> (Vec<u8>, Vec<u8>) {
214 let frac_len = frac_a.len().max(frac_b.len());
216 let mut fa: Vec<u8> = frac_a.to_vec();
217 fa.resize(frac_len, 0);
218 let mut fb: Vec<u8> = frac_b.to_vec();
219 fb.resize(frac_len, 0);
220
221 let mut carry: u8 = 0;
223 let mut frac_result = vec![0u8; frac_len];
224 for i in (0..frac_len).rev() {
225 let sum = fa[i] + fb[i] + carry;
226 frac_result[i] = sum % 10;
227 carry = sum / 10;
228 }
229
230 let int_len = int_a.len().max(int_b.len());
232 let mut ia = vec![0u8; int_len - int_a.len()];
233 ia.extend_from_slice(int_a);
234 let mut ib = vec![0u8; int_len - int_b.len()];
235 ib.extend_from_slice(int_b);
236
237 let mut int_result = vec![0u8; int_len];
239 for i in (0..int_len).rev() {
240 let sum = ia[i] + ib[i] + carry;
241 int_result[i] = sum % 10;
242 carry = sum / 10;
243 }
244 if carry > 0 {
245 int_result.insert(0, carry);
246 }
247
248 (int_result, frac_result)
249}
250
251fn sub_unsigned(int_a: &[u8], frac_a: &[u8], int_b: &[u8], frac_b: &[u8]) -> (Vec<u8>, Vec<u8>) {
253 let frac_len = frac_a.len().max(frac_b.len());
254 let mut fa: Vec<u8> = frac_a.to_vec();
255 fa.resize(frac_len, 0);
256 let mut fb: Vec<u8> = frac_b.to_vec();
257 fb.resize(frac_len, 0);
258
259 let mut borrow: i16 = 0;
260 let mut frac_result = vec![0u8; frac_len];
261 for i in (0..frac_len).rev() {
262 let diff = i16::from(fa[i]) - i16::from(fb[i]) - borrow;
263 if diff < 0 {
264 frac_result[i] = u8::try_from(diff + 10).unwrap_or(0);
265 borrow = 1;
266 } else {
267 frac_result[i] = u8::try_from(diff).unwrap_or(0);
268 borrow = 0;
269 }
270 }
271
272 let int_len = int_a.len().max(int_b.len());
273 let mut ia = vec![0u8; int_len - int_a.len()];
274 ia.extend_from_slice(int_a);
275 let mut ib = vec![0u8; int_len - int_b.len()];
276 ib.extend_from_slice(int_b);
277
278 let mut int_result = vec![0u8; int_len];
279 for i in (0..int_len).rev() {
280 let diff = i16::from(ia[i]) - i16::from(ib[i]) - borrow;
281 if diff < 0 {
282 int_result[i] = u8::try_from(diff + 10).unwrap_or(0);
283 borrow = 1;
284 } else {
285 int_result[i] = u8::try_from(diff).unwrap_or(0);
286 borrow = 0;
287 }
288 }
289
290 (int_result, frac_result)
291}
292
293fn cmp_unsigned(int_a: &[u8], frac_a: &[u8], int_b: &[u8], frac_b: &[u8]) -> Ordering {
295 let ia = strip_leading_zeros(int_a);
297 let ib = strip_leading_zeros(int_b);
298
299 match ia.len().cmp(&ib.len()) {
300 Ordering::Equal => {}
301 ord => return ord,
302 }
303
304 for (a, b) in ia.iter().zip(ib.iter()) {
306 match a.cmp(b) {
307 Ordering::Equal => {}
308 ord => return ord,
309 }
310 }
311
312 let frac_len = frac_a.len().max(frac_b.len());
314 for i in 0..frac_len {
315 let a = frac_a.get(i).copied().unwrap_or(0);
316 let b = frac_b.get(i).copied().unwrap_or(0);
317 match a.cmp(&b) {
318 Ordering::Equal => {}
319 ord => return ord,
320 }
321 }
322
323 Ordering::Equal
324}
325
326fn strip_leading_zeros(digits: &[u8]) -> &[u8] {
327 let start = digits.iter().position(|&d| d != 0).unwrap_or(digits.len());
328 if start == digits.len() {
329 &digits[digits.len().saturating_sub(1)..]
331 } else {
332 &digits[start..]
333 }
334}
335
336fn format_decimal(negative: bool, int_digits: &[u8], frac_digits: &[u8]) -> String {
338 let int_str: String = strip_leading_zeros(int_digits)
339 .iter()
340 .map(|d| char::from(b'0' + d))
341 .collect();
342 let int_str = if int_str.is_empty() {
343 "0".to_owned()
344 } else {
345 int_str
346 };
347
348 let frac_end = frac_digits
350 .iter()
351 .rposition(|&d| d != 0)
352 .map_or(0, |p| p + 1);
353 let frac = &frac_digits[..frac_end];
354
355 let result = if frac.is_empty() {
356 int_str
357 } else {
358 let frac_str: String = frac.iter().map(|d| char::from(b'0' + d)).collect();
359 format!("{int_str}.{frac_str}")
360 };
361
362 if negative && result != "0" {
363 format!("-{result}")
364 } else {
365 result
366 }
367}
368
369fn decimal_add_impl(a: &str, b: &str) -> String {
371 let (neg_a, int_a, frac_a) = parse_decimal(a);
372 let (neg_b, int_b, frac_b) = parse_decimal(b);
373
374 match (neg_a, neg_b) {
375 (false, false) => {
376 let (ir, fr) = add_unsigned(&int_a, &frac_a, &int_b, &frac_b);
377 format_decimal(false, &ir, &fr)
378 }
379 (true, true) => {
380 let (ir, fr) = add_unsigned(&int_a, &frac_a, &int_b, &frac_b);
381 format_decimal(true, &ir, &fr)
382 }
383 (false, true) => {
384 match cmp_unsigned(&int_a, &frac_a, &int_b, &frac_b) {
386 Ordering::Less => {
387 let (ir, fr) = sub_unsigned(&int_b, &frac_b, &int_a, &frac_a);
388 format_decimal(true, &ir, &fr)
389 }
390 Ordering::Equal => "0".to_owned(),
391 Ordering::Greater => {
392 let (ir, fr) = sub_unsigned(&int_a, &frac_a, &int_b, &frac_b);
393 format_decimal(false, &ir, &fr)
394 }
395 }
396 }
397 (true, false) => {
398 match cmp_unsigned(&int_b, &frac_b, &int_a, &frac_a) {
400 Ordering::Less => {
401 let (ir, fr) = sub_unsigned(&int_a, &frac_a, &int_b, &frac_b);
402 format_decimal(true, &ir, &fr)
403 }
404 Ordering::Equal => "0".to_owned(),
405 Ordering::Greater => {
406 let (ir, fr) = sub_unsigned(&int_b, &frac_b, &int_a, &frac_a);
407 format_decimal(false, &ir, &fr)
408 }
409 }
410 }
411 }
412}
413
414fn decimal_sub_impl(a: &str, b: &str) -> String {
416 let neg_b = if let Some(stripped) = b.strip_prefix('-') {
418 stripped.to_owned()
419 } else {
420 format!("-{b}")
421 };
422 decimal_add_impl(a, &neg_b)
423}
424
425fn decimal_mul_impl(a: &str, b: &str) -> String {
427 let (neg_a, int_a, frac_a) = parse_decimal(a);
428 let (neg_b, int_b, frac_b) = parse_decimal(b);
429
430 let result_negative = neg_a != neg_b;
431 let frac_places = frac_a.len() + frac_b.len();
432
433 let mut digits_a: Vec<u8> = int_a;
435 digits_a.extend_from_slice(&frac_a);
436 let mut digits_b: Vec<u8> = int_b;
437 digits_b.extend_from_slice(&frac_b);
438
439 let len_a = digits_a.len();
441 let len_b = digits_b.len();
442 let mut product = vec![0u16; len_a + len_b];
443
444 for (i, &da) in digits_a.iter().enumerate().rev() {
445 for (j, &db) in digits_b.iter().enumerate().rev() {
446 let pos = i + j + 1;
447 product[pos] += u16::from(da) * u16::from(db);
448 product[i + j] += product[pos] / 10;
449 product[pos] %= 10;
450 }
451 }
452
453 let product: Vec<u8> = product
456 .iter()
457 .map(|&d| u8::try_from(d).unwrap_or(0))
458 .collect();
459
460 let total_len = product.len();
462 let int_end = total_len.saturating_sub(frac_places);
463 let int_digits = &product[..int_end];
464 let frac_digits = &product[int_end..];
465
466 format_decimal(result_negative, int_digits, frac_digits)
467}
468
469fn decimal_cmp_impl(a: &str, b: &str) -> i64 {
471 let (neg_a, int_a, frac_a) = parse_decimal(a);
472 let (neg_b, int_b, frac_b) = parse_decimal(b);
473
474 let a_is_zero = int_a.iter().all(|&d| d == 0) && frac_a.iter().all(|&d| d == 0);
475 let b_is_zero = int_b.iter().all(|&d| d == 0) && frac_b.iter().all(|&d| d == 0);
476
477 if a_is_zero && b_is_zero {
478 return 0;
479 }
480
481 match (neg_a && !a_is_zero, neg_b && !b_is_zero) {
482 (true, false) => -1,
483 (false, true) => 1,
484 (true, true) => {
485 match cmp_unsigned(&int_a, &frac_a, &int_b, &frac_b) {
487 Ordering::Less => 1,
488 Ordering::Equal => 0,
489 Ordering::Greater => -1,
490 }
491 }
492 (false, false) => match cmp_unsigned(&int_a, &frac_a, &int_b, &frac_b) {
493 Ordering::Less => -1,
494 Ordering::Equal => 0,
495 Ordering::Greater => 1,
496 },
497 }
498}
499
500pub struct DecimalFunc;
504
505impl ScalarFunction for DecimalFunc {
506 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
507 if args.len() != 1 {
508 return Err(FrankenError::internal(
509 "decimal requires exactly 1 argument",
510 ));
511 }
512 if args[0].is_null() {
513 return Ok(SqliteValue::Null);
514 }
515 let text = args[0].to_text();
516 Ok(SqliteValue::Text(decimal_normalize(&text)))
517 }
518
519 fn num_args(&self) -> i32 {
520 1
521 }
522
523 fn name(&self) -> &'static str {
524 "decimal"
525 }
526}
527
528pub struct DecimalAddFunc;
530
531impl ScalarFunction for DecimalAddFunc {
532 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
533 if args.len() != 2 {
534 return Err(FrankenError::internal(
535 "decimal_add requires exactly 2 arguments",
536 ));
537 }
538 if args[0].is_null() || args[1].is_null() {
539 return Ok(SqliteValue::Null);
540 }
541 let a = args[0].to_text();
542 let b = args[1].to_text();
543 debug!(a = %a, b = %b, "decimal_add invoked");
544 Ok(SqliteValue::Text(decimal_add_impl(&a, &b)))
545 }
546
547 fn num_args(&self) -> i32 {
548 2
549 }
550
551 fn name(&self) -> &'static str {
552 "decimal_add"
553 }
554}
555
556pub struct DecimalSubFunc;
558
559impl ScalarFunction for DecimalSubFunc {
560 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
561 if args.len() != 2 {
562 return Err(FrankenError::internal(
563 "decimal_sub requires exactly 2 arguments",
564 ));
565 }
566 if args[0].is_null() || args[1].is_null() {
567 return Ok(SqliteValue::Null);
568 }
569 let a = args[0].to_text();
570 let b = args[1].to_text();
571 debug!(a = %a, b = %b, "decimal_sub invoked");
572 Ok(SqliteValue::Text(decimal_sub_impl(&a, &b)))
573 }
574
575 fn num_args(&self) -> i32 {
576 2
577 }
578
579 fn name(&self) -> &'static str {
580 "decimal_sub"
581 }
582}
583
584pub struct DecimalMulFunc;
586
587impl ScalarFunction for DecimalMulFunc {
588 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
589 if args.len() != 2 {
590 return Err(FrankenError::internal(
591 "decimal_mul requires exactly 2 arguments",
592 ));
593 }
594 if args[0].is_null() || args[1].is_null() {
595 return Ok(SqliteValue::Null);
596 }
597 let a = args[0].to_text();
598 let b = args[1].to_text();
599 debug!(a = %a, b = %b, "decimal_mul invoked");
600 Ok(SqliteValue::Text(decimal_mul_impl(&a, &b)))
601 }
602
603 fn num_args(&self) -> i32 {
604 2
605 }
606
607 fn name(&self) -> &'static str {
608 "decimal_mul"
609 }
610}
611
612pub struct DecimalCmpFunc;
614
615impl ScalarFunction for DecimalCmpFunc {
616 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
617 if args.len() != 2 {
618 return Err(FrankenError::internal(
619 "decimal_cmp requires exactly 2 arguments",
620 ));
621 }
622 if args[0].is_null() || args[1].is_null() {
623 return Ok(SqliteValue::Null);
624 }
625 let a = args[0].to_text();
626 let b = args[1].to_text();
627 debug!(a = %a, b = %b, "decimal_cmp invoked");
628 Ok(SqliteValue::Integer(decimal_cmp_impl(&a, &b)))
629 }
630
631 fn num_args(&self) -> i32 {
632 2
633 }
634
635 fn name(&self) -> &'static str {
636 "decimal_cmp"
637 }
638}
639
640fn xorshift64(state: &mut u64) -> u64 {
649 let mut x = *state;
650 x ^= x << 13;
651 x ^= x >> 7;
652 x ^= x << 17;
653 *state = x;
654 x
655}
656
657fn generate_uuid_v4() -> String {
659 use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
662 static COUNTER: AtomicU64 = AtomicU64::new(0);
663
664 let count = COUNTER.fetch_add(1, AtomicOrdering::Relaxed);
665 let stack_var: u64 = 0;
667 #[allow(clippy::ptr_as_ptr)]
668 let addr = std::ptr::addr_of!(stack_var) as u64;
669 let mut state = addr.wrapping_mul(6_364_136_223_846_793_005)
670 ^ count.wrapping_mul(1_442_695_040_888_963_407);
671 if state == 0 {
672 state = 0x5DEE_CE66_D1A4_F87D; }
674
675 let mut bytes = [0u8; 16];
676 for chunk in bytes.chunks_exact_mut(8) {
677 let val = xorshift64(&mut state);
678 chunk.copy_from_slice(&val.to_le_bytes());
679 }
680
681 bytes[6] = (bytes[6] & 0x0F) | 0x40; bytes[8] = (bytes[8] & 0x3F) | 0x80; format!(
686 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
687 bytes[0],
688 bytes[1],
689 bytes[2],
690 bytes[3],
691 bytes[4],
692 bytes[5],
693 bytes[6],
694 bytes[7],
695 bytes[8],
696 bytes[9],
697 bytes[10],
698 bytes[11],
699 bytes[12],
700 bytes[13],
701 bytes[14],
702 bytes[15],
703 )
704}
705
706fn uuid_str_to_blob(s: &str) -> Result<Vec<u8>> {
708 let hex: String = s.chars().filter(char::is_ascii_hexdigit).collect();
709 if hex.len() != 32 {
710 return Err(FrankenError::internal(format!(
711 "invalid UUID string: expected 32 hex digits, got {}",
712 hex.len()
713 )));
714 }
715
716 let mut bytes = Vec::with_capacity(16);
717 for i in (0..32).step_by(2) {
718 let byte = u8::from_str_radix(&hex[i..i + 2], 16)
719 .map_err(|_| FrankenError::internal(format!("invalid hex in UUID at position {i}")))?;
720 bytes.push(byte);
721 }
722 Ok(bytes)
723}
724
725fn blob_to_uuid_str(bytes: &[u8]) -> Result<String> {
727 if bytes.len() != 16 {
728 return Err(FrankenError::internal(format!(
729 "uuid_str: expected 16-byte blob, got {} bytes",
730 bytes.len()
731 )));
732 }
733 Ok(format!(
734 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
735 bytes[0],
736 bytes[1],
737 bytes[2],
738 bytes[3],
739 bytes[4],
740 bytes[5],
741 bytes[6],
742 bytes[7],
743 bytes[8],
744 bytes[9],
745 bytes[10],
746 bytes[11],
747 bytes[12],
748 bytes[13],
749 bytes[14],
750 bytes[15],
751 ))
752}
753
754pub struct UuidFunc;
758
759impl ScalarFunction for UuidFunc {
760 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
761 if !args.is_empty() {
762 return Err(FrankenError::internal("uuid takes no arguments"));
763 }
764 let uuid = generate_uuid_v4();
765 debug!(uuid = %uuid, "uuid() generated");
766 Ok(SqliteValue::Text(uuid))
767 }
768
769 fn is_deterministic(&self) -> bool {
770 false }
772
773 fn num_args(&self) -> i32 {
774 0
775 }
776
777 fn name(&self) -> &'static str {
778 "uuid"
779 }
780}
781
782pub struct UuidStrFunc;
784
785impl ScalarFunction for UuidStrFunc {
786 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
787 if args.len() != 1 {
788 return Err(FrankenError::internal(
789 "uuid_str requires exactly 1 argument",
790 ));
791 }
792 if args[0].is_null() {
793 return Ok(SqliteValue::Null);
794 }
795 match &args[0] {
796 SqliteValue::Blob(b) => {
797 let s = blob_to_uuid_str(b)?;
798 Ok(SqliteValue::Text(s))
799 }
800 SqliteValue::Text(s) => {
801 let blob = uuid_str_to_blob(s)?;
803 let normalized = blob_to_uuid_str(&blob)?;
804 Ok(SqliteValue::Text(normalized))
805 }
806 _ => Err(FrankenError::internal(
807 "uuid_str: argument must be a blob or text",
808 )),
809 }
810 }
811
812 fn num_args(&self) -> i32 {
813 1
814 }
815
816 fn name(&self) -> &'static str {
817 "uuid_str"
818 }
819}
820
821pub struct UuidBlobFunc;
823
824impl ScalarFunction for UuidBlobFunc {
825 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
826 if args.len() != 1 {
827 return Err(FrankenError::internal(
828 "uuid_blob requires exactly 1 argument",
829 ));
830 }
831 if args[0].is_null() {
832 return Ok(SqliteValue::Null);
833 }
834 let Some(s) = args[0].as_text() else {
835 return Err(FrankenError::internal("uuid_blob: argument must be text"));
836 };
837 let blob = uuid_str_to_blob(s)?;
838 Ok(SqliteValue::Blob(blob))
839 }
840
841 fn num_args(&self) -> i32 {
842 1
843 }
844
845 fn name(&self) -> &'static str {
846 "uuid_blob"
847 }
848}
849
850pub fn register_misc_scalars(registry: &mut FunctionRegistry) {
856 info!("misc extension: registering scalar functions");
857 registry.register_scalar(DecimalFunc);
858 registry.register_scalar(DecimalAddFunc);
859 registry.register_scalar(DecimalSubFunc);
860 registry.register_scalar(DecimalMulFunc);
861 registry.register_scalar(DecimalCmpFunc);
862 registry.register_scalar(UuidFunc);
863 registry.register_scalar(UuidStrFunc);
864 registry.register_scalar(UuidBlobFunc);
865}
866
867#[cfg(test)]
872mod tests {
873 use super::*;
874
875 #[test]
876 fn test_extension_name_matches_crate_suffix() {
877 let expected = env!("CARGO_PKG_NAME")
878 .strip_prefix("fsqlite-ext-")
879 .expect("extension crates should use fsqlite-ext-* naming");
880 assert_eq!(extension_name(), expected);
881 }
882
883 #[test]
886 fn test_generate_series_basic() {
887 let table = GenerateSeriesTable;
888 let mut cursor = table.open().unwrap();
889 cursor.init(1, 5, 1).unwrap();
890
891 let mut values = Vec::new();
892 let cx = Cx::new();
893 while !cursor.eof() {
894 let mut ctx = ColumnContext::new();
895 cursor.column(&mut ctx, 0).unwrap();
896 if let Some(SqliteValue::Integer(v)) = ctx.take_value() {
897 values.push(v);
898 }
899 cursor.next(&cx).unwrap();
900 }
901 assert_eq!(values, vec![1, 2, 3, 4, 5]);
902 }
903
904 #[test]
905 fn test_generate_series_step() {
906 let table = GenerateSeriesTable;
907 let mut cursor = table.open().unwrap();
908 cursor.init(0, 10, 2).unwrap();
909
910 let mut values = Vec::new();
911 let cx = Cx::new();
912 while !cursor.eof() {
913 let mut ctx = ColumnContext::new();
914 cursor.column(&mut ctx, 0).unwrap();
915 if let Some(SqliteValue::Integer(v)) = ctx.take_value() {
916 values.push(v);
917 }
918 cursor.next(&cx).unwrap();
919 }
920 assert_eq!(values, vec![0, 2, 4, 6, 8, 10]);
921 }
922
923 #[test]
924 fn test_generate_series_negative_step() {
925 let table = GenerateSeriesTable;
926 let mut cursor = table.open().unwrap();
927 cursor.init(5, 1, -1).unwrap();
928
929 let mut values = Vec::new();
930 let cx = Cx::new();
931 while !cursor.eof() {
932 let mut ctx = ColumnContext::new();
933 cursor.column(&mut ctx, 0).unwrap();
934 if let Some(SqliteValue::Integer(v)) = ctx.take_value() {
935 values.push(v);
936 }
937 cursor.next(&cx).unwrap();
938 }
939 assert_eq!(values, vec![5, 4, 3, 2, 1]);
940 }
941
942 #[test]
943 fn test_generate_series_single() {
944 let table = GenerateSeriesTable;
945 let mut cursor = table.open().unwrap();
946 cursor.init(5, 5, 1).unwrap();
947
948 let mut values = Vec::new();
949 let cx = Cx::new();
950 while !cursor.eof() {
951 let mut ctx = ColumnContext::new();
952 cursor.column(&mut ctx, 0).unwrap();
953 if let Some(SqliteValue::Integer(v)) = ctx.take_value() {
954 values.push(v);
955 }
956 cursor.next(&cx).unwrap();
957 }
958 assert_eq!(values, vec![5]);
959 }
960
961 #[test]
962 fn test_generate_series_empty() {
963 let table = GenerateSeriesTable;
964 let mut cursor = table.open().unwrap();
965 cursor.init(5, 1, 1).unwrap();
966 assert!(
967 cursor.eof(),
968 "positive step with start > stop should be empty"
969 );
970 }
971
972 #[test]
973 fn test_generate_series_step_zero_error() {
974 let table = GenerateSeriesTable;
975 let mut cursor = table.open().unwrap();
976 assert!(cursor.init(1, 10, 0).is_err());
977 }
978
979 #[test]
980 fn test_generate_series_filter() {
981 let table = GenerateSeriesTable;
982 let mut cursor = table.open().unwrap();
983 let cx = Cx::new();
984 cursor
985 .filter(
986 &cx,
987 0,
988 None,
989 &[
990 SqliteValue::Integer(1),
991 SqliteValue::Integer(3),
992 SqliteValue::Integer(1),
993 ],
994 )
995 .unwrap();
996
997 let mut values = Vec::new();
998 while !cursor.eof() {
999 let mut ctx = ColumnContext::new();
1000 cursor.column(&mut ctx, 0).unwrap();
1001 if let Some(SqliteValue::Integer(v)) = ctx.take_value() {
1002 values.push(v);
1003 }
1004 cursor.next(&cx).unwrap();
1005 }
1006 assert_eq!(values, vec![1, 2, 3]);
1007 }
1008
1009 #[test]
1012 fn test_decimal_normalize() {
1013 assert_eq!(decimal_normalize("1.23"), "1.23");
1014 assert_eq!(decimal_normalize("001.230"), "1.23");
1015 assert_eq!(decimal_normalize("0.0"), "0");
1016 assert_eq!(decimal_normalize("-1.50"), "-1.5");
1017 assert_eq!(decimal_normalize("42"), "42");
1018 }
1019
1020 #[test]
1021 fn test_decimal_func_basic() {
1022 let args = [SqliteValue::Text("1.23".into())];
1023 let result = DecimalFunc.invoke(&args).unwrap();
1024 assert_eq!(result, SqliteValue::Text("1.23".into()));
1025 }
1026
1027 #[test]
1028 fn test_decimal_func_null() {
1029 let args = [SqliteValue::Null];
1030 let result = DecimalFunc.invoke(&args).unwrap();
1031 assert_eq!(result, SqliteValue::Null);
1032 }
1033
1034 #[test]
1035 fn test_decimal_add() {
1036 let args = [
1037 SqliteValue::Text("1.1".into()),
1038 SqliteValue::Text("2.2".into()),
1039 ];
1040 let result = DecimalAddFunc.invoke(&args).unwrap();
1041 assert_eq!(result, SqliteValue::Text("3.3".into()));
1042 }
1043
1044 #[test]
1045 fn test_decimal_add_no_fp_loss() {
1046 let args = [
1048 SqliteValue::Text("0.1".into()),
1049 SqliteValue::Text("0.2".into()),
1050 ];
1051 let result = DecimalAddFunc.invoke(&args).unwrap();
1052 assert_eq!(
1053 result,
1054 SqliteValue::Text("0.3".into()),
1055 "decimal_add should avoid floating-point precision loss"
1056 );
1057 }
1058
1059 #[test]
1060 fn test_decimal_sub() {
1061 let args = [
1062 SqliteValue::Text("5.00".into()),
1063 SqliteValue::Text("1.23".into()),
1064 ];
1065 let result = DecimalSubFunc.invoke(&args).unwrap();
1066 assert_eq!(result, SqliteValue::Text("3.77".into()));
1067 }
1068
1069 #[test]
1070 fn test_decimal_sub_negative_result() {
1071 let args = [
1072 SqliteValue::Text("1.0".into()),
1073 SqliteValue::Text("3.0".into()),
1074 ];
1075 let result = DecimalSubFunc.invoke(&args).unwrap();
1076 assert_eq!(result, SqliteValue::Text("-2".into()));
1077 }
1078
1079 #[test]
1080 fn test_decimal_mul() {
1081 let args = [
1082 SqliteValue::Text("1.5".into()),
1083 SqliteValue::Text("2.5".into()),
1084 ];
1085 let result = DecimalMulFunc.invoke(&args).unwrap();
1086 assert_eq!(result, SqliteValue::Text("3.75".into()));
1087 }
1088
1089 #[test]
1090 fn test_decimal_mul_large() {
1091 let args = [
1092 SqliteValue::Text("1.1".into()),
1093 SqliteValue::Text("2.0".into()),
1094 ];
1095 let result = DecimalMulFunc.invoke(&args).unwrap();
1096 assert_eq!(result, SqliteValue::Text("2.2".into()));
1097 }
1098
1099 #[test]
1100 fn test_decimal_cmp_less() {
1101 let args = [
1102 SqliteValue::Text("1.23".into()),
1103 SqliteValue::Text("4.56".into()),
1104 ];
1105 let result = DecimalCmpFunc.invoke(&args).unwrap();
1106 assert_eq!(result, SqliteValue::Integer(-1));
1107 }
1108
1109 #[test]
1110 fn test_decimal_cmp_greater() {
1111 let args = [
1112 SqliteValue::Text("4.56".into()),
1113 SqliteValue::Text("1.23".into()),
1114 ];
1115 let result = DecimalCmpFunc.invoke(&args).unwrap();
1116 assert_eq!(result, SqliteValue::Integer(1));
1117 }
1118
1119 #[test]
1120 fn test_decimal_cmp_equal() {
1121 let args = [
1122 SqliteValue::Text("1.0".into()),
1123 SqliteValue::Text("1.0".into()),
1124 ];
1125 let result = DecimalCmpFunc.invoke(&args).unwrap();
1126 assert_eq!(result, SqliteValue::Integer(0));
1127 }
1128
1129 #[test]
1130 fn test_decimal_cmp_negative() {
1131 let args = [
1132 SqliteValue::Text("-5".into()),
1133 SqliteValue::Text("3".into()),
1134 ];
1135 let result = DecimalCmpFunc.invoke(&args).unwrap();
1136 assert_eq!(result, SqliteValue::Integer(-1));
1137 }
1138
1139 #[test]
1140 fn test_decimal_precision_financial() {
1141 let result = decimal_mul_impl("19.99", "100");
1143 assert_eq!(result, "1999");
1144
1145 let sum = decimal_add_impl("10.50", "3.75");
1147 assert_eq!(sum, "14.25");
1148 let product = decimal_mul_impl(&sum, "2");
1149 assert_eq!(product, "28.5");
1150 }
1151
1152 #[test]
1155 fn test_uuid_v4_format() {
1156 let uuid = generate_uuid_v4();
1157 let parts: Vec<&str> = uuid.split('-').collect();
1159 assert_eq!(parts.len(), 5, "UUID should have 5 dash-separated parts");
1160 assert_eq!(parts[0].len(), 8);
1161 assert_eq!(parts[1].len(), 4);
1162 assert_eq!(parts[2].len(), 4);
1163 assert_eq!(parts[3].len(), 4);
1164 assert_eq!(parts[4].len(), 12);
1165 }
1166
1167 #[test]
1168 fn test_uuid_v4_version() {
1169 let uuid = generate_uuid_v4();
1170 let version_char = uuid.chars().nth(14).unwrap();
1172 assert_eq!(version_char, '4', "UUID v4 must have version nibble = 4");
1173 }
1174
1175 #[test]
1176 fn test_uuid_v4_variant() {
1177 let uuid = generate_uuid_v4();
1178 let variant_char = uuid.chars().nth(19).unwrap();
1180 let variant_nibble = u8::from_str_radix(&variant_char.to_string(), 16).unwrap();
1181 assert!(
1182 (0x8..=0xB).contains(&variant_nibble),
1183 "UUID v4 variant bits should be 10xx, got {variant_nibble:#X}"
1184 );
1185 }
1186
1187 #[test]
1188 fn test_uuid_uniqueness() {
1189 let mut uuids: Vec<String> = (0..100).map(|_| generate_uuid_v4()).collect();
1190 uuids.sort();
1191 uuids.dedup();
1192 assert_eq!(
1193 uuids.len(),
1194 100,
1195 "100 uuid() calls should produce 100 unique values"
1196 );
1197 }
1198
1199 #[test]
1200 fn test_uuid_func() {
1201 let result = UuidFunc.invoke(&[]).unwrap();
1202 if let SqliteValue::Text(s) = result {
1203 assert_eq!(s.len(), 36, "UUID string should be 36 characters");
1204 } else {
1205 panic!("uuid() should return Text");
1206 }
1207 }
1208
1209 #[test]
1210 fn test_uuid_str_blob_roundtrip() {
1211 let uuid_str = generate_uuid_v4();
1212 let blob = uuid_str_to_blob(&uuid_str).unwrap();
1213 assert_eq!(blob.len(), 16);
1214 let back = blob_to_uuid_str(&blob).unwrap();
1215 assert_eq!(back, uuid_str, "uuid_str(uuid_blob(X)) should roundtrip");
1216 }
1217
1218 #[test]
1219 fn test_uuid_blob_length() {
1220 let result = UuidBlobFunc
1221 .invoke(&[SqliteValue::Text(generate_uuid_v4())])
1222 .unwrap();
1223 if let SqliteValue::Blob(b) = result {
1224 assert_eq!(b.len(), 16, "uuid_blob should return 16-byte blob");
1225 } else {
1226 panic!("uuid_blob should return Blob");
1227 }
1228 }
1229
1230 #[test]
1231 fn test_uuid_str_func() {
1232 let uuid = generate_uuid_v4();
1233 let blob = uuid_str_to_blob(&uuid).unwrap();
1234 let result = UuidStrFunc.invoke(&[SqliteValue::Blob(blob)]).unwrap();
1235 assert_eq!(result, SqliteValue::Text(uuid));
1236 }
1237
1238 #[test]
1241 fn test_register_misc_scalars() {
1242 let mut registry = FunctionRegistry::new();
1243 register_misc_scalars(&mut registry);
1244 assert!(registry.find_scalar("decimal", 1).is_some());
1245 assert!(registry.find_scalar("decimal_add", 2).is_some());
1246 assert!(registry.find_scalar("decimal_sub", 2).is_some());
1247 assert!(registry.find_scalar("decimal_mul", 2).is_some());
1248 assert!(registry.find_scalar("decimal_cmp", 2).is_some());
1249 assert!(registry.find_scalar("uuid", 0).is_some());
1250 assert!(registry.find_scalar("uuid_str", 1).is_some());
1251 assert!(registry.find_scalar("uuid_blob", 1).is_some());
1252 }
1253
1254 #[test]
1257 fn test_generate_series_large_step() {
1258 let table = GenerateSeriesTable;
1259 let mut cursor = table.open().unwrap();
1260 cursor.init(0, 100, 50).unwrap();
1261 let mut values = Vec::new();
1262 while !cursor.eof() {
1263 values.push(cursor.current);
1264 cursor.next(&Cx::default()).unwrap();
1265 }
1266 assert_eq!(values, vec![0, 50, 100]);
1267 }
1268
1269 #[test]
1270 fn test_generate_series_negative_range() {
1271 let table = GenerateSeriesTable;
1272 let mut cursor = table.open().unwrap();
1273 cursor.init(-5, -1, 1).unwrap();
1274 let mut count = 0;
1275 while !cursor.eof() {
1276 count += 1;
1277 cursor.next(&Cx::default()).unwrap();
1278 }
1279 assert_eq!(count, 5);
1280 }
1281
1282 #[test]
1283 fn test_generate_series_reverse_with_wrong_step_empty() {
1284 let table = GenerateSeriesTable;
1285 let mut cursor = table.open().unwrap();
1286 cursor.init(10, 1, 1).unwrap();
1288 assert!(cursor.eof());
1289 }
1290
1291 #[test]
1292 fn test_generate_series_forward_with_negative_step_empty() {
1293 let table = GenerateSeriesTable;
1294 let mut cursor = table.open().unwrap();
1295 cursor.init(1, 10, -1).unwrap();
1297 assert!(cursor.eof());
1298 }
1299
1300 #[test]
1301 fn test_generate_series_rowid() {
1302 let table = GenerateSeriesTable;
1303 let mut cursor = table.open().unwrap();
1304 cursor.init(42, 42, 1).unwrap();
1305 assert_eq!(cursor.rowid().unwrap(), 42);
1306 }
1307
1308 #[test]
1309 fn test_generate_series_column_values() {
1310 let table = GenerateSeriesTable;
1311 let mut cursor = table.open().unwrap();
1312 cursor.init(10, 20, 5).unwrap();
1313 let mut ctx = ColumnContext::new();
1315 cursor.column(&mut ctx, 0).unwrap();
1316 assert_eq!(ctx.take_value(), Some(SqliteValue::Integer(10)));
1317 let mut ctx2 = ColumnContext::new();
1319 cursor.column(&mut ctx2, 2).unwrap();
1320 assert_eq!(ctx2.take_value(), Some(SqliteValue::Integer(20)));
1321 let mut ctx3 = ColumnContext::new();
1323 cursor.column(&mut ctx3, 3).unwrap();
1324 assert_eq!(ctx3.take_value(), Some(SqliteValue::Integer(5)));
1325 let mut ctx4 = ColumnContext::new();
1327 cursor.column(&mut ctx4, 99).unwrap();
1328 assert_eq!(ctx4.take_value(), Some(SqliteValue::Null));
1329 }
1330
1331 #[test]
1332 fn test_generate_series_vtable_create_connect() {
1333 let cx = Cx::default();
1334 let _ = GenerateSeriesTable::create(&cx, &[]).unwrap();
1335 let _ = GenerateSeriesTable::connect(&cx, &[]).unwrap();
1336 }
1337
1338 #[test]
1339 fn test_generate_series_best_index() {
1340 let table = GenerateSeriesTable;
1341 let mut info = IndexInfo::new(Vec::new(), Vec::new());
1342 table.best_index(&mut info).unwrap();
1343 assert!(info.estimated_cost > 0.0);
1344 assert!(info.estimated_rows > 0);
1345 }
1346
1347 #[test]
1350 fn test_decimal_normalize_zero() {
1351 assert_eq!(decimal_normalize("0"), "0");
1352 assert_eq!(decimal_normalize("0.0"), "0");
1353 assert_eq!(decimal_normalize("000.000"), "0");
1354 }
1355
1356 #[test]
1357 fn test_decimal_normalize_negative_zero() {
1358 let result = decimal_normalize("-0.0");
1360 assert!(result == "0" || result == "-0");
1361 }
1362
1363 #[test]
1364 fn test_decimal_normalize_integer() {
1365 assert_eq!(decimal_normalize("42"), "42");
1366 assert_eq!(decimal_normalize("00042"), "42");
1367 }
1368
1369 #[test]
1370 fn test_decimal_normalize_trailing_zeros() {
1371 assert_eq!(decimal_normalize("1.50000"), "1.5");
1372 assert_eq!(decimal_normalize("3.14000"), "3.14");
1373 }
1374
1375 #[test]
1378 fn test_decimal_add_zeros() {
1379 assert_eq!(decimal_add_impl("0", "0"), "0");
1380 }
1381
1382 #[test]
1383 fn test_decimal_add_negative_plus_positive() {
1384 let result = decimal_add_impl("-5", "3");
1385 assert_eq!(result, "-2");
1386 }
1387
1388 #[test]
1389 fn test_decimal_add_positive_plus_negative() {
1390 let result = decimal_add_impl("3", "-5");
1391 assert_eq!(result, "-2");
1392 }
1393
1394 #[test]
1395 fn test_decimal_sub_same_number() {
1396 assert_eq!(decimal_sub_impl("42.5", "42.5"), "0");
1397 }
1398
1399 #[test]
1400 fn test_decimal_sub_produces_negative() {
1401 let result = decimal_sub_impl("1", "5");
1402 assert_eq!(result, "-4");
1403 }
1404
1405 #[test]
1406 fn test_decimal_mul_by_zero() {
1407 assert_eq!(decimal_mul_impl("12345.6789", "0"), "0");
1408 }
1409
1410 #[test]
1411 fn test_decimal_mul_by_one() {
1412 assert_eq!(decimal_mul_impl("3.14", "1"), "3.14");
1413 }
1414
1415 #[test]
1416 fn test_decimal_mul_negative_times_negative() {
1417 let result = decimal_mul_impl("-3", "-4");
1418 assert_eq!(result, "12");
1419 }
1420
1421 #[test]
1422 fn test_decimal_mul_small_decimals() {
1423 let result = decimal_mul_impl("0.001", "0.001");
1424 assert_eq!(result, "0.000001");
1425 }
1426
1427 #[test]
1428 fn test_decimal_cmp_equal_values() {
1429 assert_eq!(decimal_cmp_impl("3.14", "3.14"), 0);
1430 }
1431
1432 #[test]
1433 fn test_decimal_cmp_leading_zeros_equal() {
1434 assert_eq!(decimal_cmp_impl("007.50", "7.5"), 0);
1435 }
1436
1437 #[test]
1438 fn test_decimal_cmp_negative_ordering() {
1439 assert_eq!(decimal_cmp_impl("-10", "-5"), -1);
1440 assert_eq!(decimal_cmp_impl("-5", "-10"), 1);
1441 }
1442
1443 #[test]
1446 fn test_decimal_add_func_null_propagation() {
1447 let result = DecimalAddFunc
1448 .invoke(&[SqliteValue::Null, SqliteValue::Text("1".to_owned())])
1449 .unwrap();
1450 assert_eq!(result, SqliteValue::Null);
1451 }
1452
1453 #[test]
1454 fn test_decimal_sub_func_null_propagation() {
1455 let result = DecimalSubFunc
1456 .invoke(&[SqliteValue::Text("1".to_owned()), SqliteValue::Null])
1457 .unwrap();
1458 assert_eq!(result, SqliteValue::Null);
1459 }
1460
1461 #[test]
1462 fn test_decimal_mul_func_null_propagation() {
1463 let result = DecimalMulFunc
1464 .invoke(&[SqliteValue::Null, SqliteValue::Null])
1465 .unwrap();
1466 assert_eq!(result, SqliteValue::Null);
1467 }
1468
1469 #[test]
1470 fn test_decimal_cmp_func_null_propagation() {
1471 let result = DecimalCmpFunc
1472 .invoke(&[SqliteValue::Null, SqliteValue::Text("1".to_owned())])
1473 .unwrap();
1474 assert_eq!(result, SqliteValue::Null);
1475 }
1476
1477 #[test]
1480 fn test_uuid_str_to_blob_invalid_length() {
1481 assert!(uuid_str_to_blob("abc").is_err());
1483 }
1484
1485 #[test]
1486 fn test_uuid_str_to_blob_invalid_hex() {
1487 assert!(uuid_str_to_blob("ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ").is_err());
1488 }
1489
1490 #[test]
1491 fn test_blob_to_uuid_str_wrong_length() {
1492 assert!(blob_to_uuid_str(&[0u8; 15]).is_err());
1493 assert!(blob_to_uuid_str(&[0u8; 17]).is_err());
1494 }
1495
1496 #[test]
1497 fn test_uuid_func_with_args_errors() {
1498 let result = UuidFunc.invoke(&[SqliteValue::Integer(1)]);
1499 assert!(result.is_err());
1500 }
1501
1502 #[test]
1503 fn test_uuid_str_func_null_returns_null() {
1504 let result = UuidStrFunc.invoke(&[SqliteValue::Null]).unwrap();
1505 assert_eq!(result, SqliteValue::Null);
1506 }
1507
1508 #[test]
1509 fn test_uuid_blob_func_null_returns_null() {
1510 let result = UuidBlobFunc.invoke(&[SqliteValue::Null]).unwrap();
1511 assert_eq!(result, SqliteValue::Null);
1512 }
1513
1514 #[test]
1515 fn test_uuid_blob_func_non_text_errors() {
1516 let result = UuidBlobFunc.invoke(&[SqliteValue::Integer(42)]);
1517 assert!(result.is_err());
1518 }
1519
1520 #[test]
1521 fn test_uuid_str_func_normalizes_text() {
1522 let uuid = generate_uuid_v4();
1524 let result = UuidStrFunc
1525 .invoke(&[SqliteValue::Text(uuid.clone())])
1526 .unwrap();
1527 assert_eq!(result, SqliteValue::Text(uuid));
1528 }
1529
1530 #[test]
1531 fn test_uuid_str_func_non_blob_non_text_errors() {
1532 let result = UuidStrFunc.invoke(&[SqliteValue::Integer(42)]);
1533 assert!(result.is_err());
1534 }
1535
1536 #[test]
1539 fn test_uuid_all_lowercase_hex() {
1540 let uuid = generate_uuid_v4();
1541 assert!(uuid.chars().all(|c| c.is_ascii_hexdigit() || c == '-'));
1543 assert!(!uuid.contains(|c: char| c.is_ascii_uppercase()));
1544 }
1545
1546 #[test]
1547 fn test_uuid_v4_multiple_unique() {
1548 let uuids: Vec<String> = (0..50).map(|_| generate_uuid_v4()).collect();
1549 let mut sorted = uuids.clone();
1551 sorted.sort();
1552 sorted.dedup();
1553 assert_eq!(sorted.len(), uuids.len(), "all UUIDs should be unique");
1554 }
1555
1556 #[test]
1559 fn test_scalar_function_names() {
1560 assert_eq!(DecimalFunc.name(), "decimal");
1561 assert_eq!(DecimalAddFunc.name(), "decimal_add");
1562 assert_eq!(DecimalSubFunc.name(), "decimal_sub");
1563 assert_eq!(DecimalMulFunc.name(), "decimal_mul");
1564 assert_eq!(DecimalCmpFunc.name(), "decimal_cmp");
1565 assert_eq!(UuidFunc.name(), "uuid");
1566 assert_eq!(UuidStrFunc.name(), "uuid_str");
1567 assert_eq!(UuidBlobFunc.name(), "uuid_blob");
1568 }
1569
1570 #[test]
1571 fn test_scalar_function_arg_counts() {
1572 assert_eq!(DecimalFunc.num_args(), 1);
1573 assert_eq!(DecimalAddFunc.num_args(), 2);
1574 assert_eq!(DecimalSubFunc.num_args(), 2);
1575 assert_eq!(DecimalMulFunc.num_args(), 2);
1576 assert_eq!(DecimalCmpFunc.num_args(), 2);
1577 assert_eq!(UuidFunc.num_args(), 0);
1578 assert_eq!(UuidStrFunc.num_args(), 1);
1579 assert_eq!(UuidBlobFunc.num_args(), 1);
1580 }
1581
1582 #[test]
1583 fn test_uuid_func_not_deterministic() {
1584 assert!(!UuidFunc.is_deterministic());
1585 }
1586}