1#![allow(clippy::unnecessary_literal_bound)]
13
14use std::any::Any;
15use std::collections::HashMap;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::{Arc, OnceLock};
18
19use fsqlite_error::FrankenError;
20use fsqlite_types::SqliteValue;
21use tracing::debug;
22
23static FSQLITE_FUNC_CALLS_TOTAL: AtomicU64 = AtomicU64::new(0);
27static FSQLITE_FUNC_EVAL_DURATION_US_TOTAL: AtomicU64 = AtomicU64::new(0);
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct FuncMetricsSnapshot {
33 pub calls_total: u64,
35 pub eval_duration_us_total: u64,
37}
38
39#[must_use]
41pub fn func_metrics_snapshot() -> FuncMetricsSnapshot {
42 FuncMetricsSnapshot {
43 calls_total: FSQLITE_FUNC_CALLS_TOTAL.load(Ordering::Relaxed),
44 eval_duration_us_total: FSQLITE_FUNC_EVAL_DURATION_US_TOTAL.load(Ordering::Relaxed),
45 }
46}
47
48pub fn reset_func_metrics() {
50 FSQLITE_FUNC_CALLS_TOTAL.store(0, Ordering::Relaxed);
51 FSQLITE_FUNC_EVAL_DURATION_US_TOTAL.store(0, Ordering::Relaxed);
52}
53
54pub fn record_func_call(duration_us: u64) {
56 FSQLITE_FUNC_CALLS_TOTAL.fetch_add(1, Ordering::Relaxed);
57 FSQLITE_FUNC_EVAL_DURATION_US_TOTAL.fetch_add(duration_us, Ordering::Relaxed);
58}
59
60pub fn record_func_call_count_only() {
62 FSQLITE_FUNC_CALLS_TOTAL.fetch_add(1, Ordering::Relaxed);
63}
64
65static FSQLITE_UDF_REGISTERED: AtomicU64 = AtomicU64::new(0);
69
70pub fn record_udf_registered() {
72 FSQLITE_UDF_REGISTERED.fetch_add(1, Ordering::Relaxed);
73}
74
75#[must_use]
77pub fn udf_registered_count() -> u64 {
78 FSQLITE_UDF_REGISTERED.load(Ordering::Relaxed)
79}
80
81pub fn reset_udf_metrics() {
83 FSQLITE_UDF_REGISTERED.store(0, Ordering::Relaxed);
84}
85
86pub mod agg_builtins;
87pub mod aggregate;
88pub mod authorizer;
89pub mod builtins;
90pub mod collation;
91pub mod datetime;
92pub mod math;
93pub mod scalar;
94pub mod vtab;
95pub mod window;
96pub mod window_builtins;
97
98pub use agg_builtins::register_aggregate_builtins;
99pub use aggregate::{AggregateAdapter, AggregateFunction};
100pub use authorizer::{AuthAction, AuthResult, Authorizer, AuthorizerAction, AuthorizerDecision};
101pub use builtins::{
102 ChangeTrackingState, case_sensitive_like_active, get_last_changes, get_last_insert_rowid,
103 get_total_changes, register_builtins, reset_total_changes, set_case_sensitive_like,
104 set_change_tracking_state, set_last_changes, set_last_insert_rowid,
105 set_statement_text_encoding, sqlite_compile_options, sqlite_compileoption_used,
106 statement_text_encoding,
107};
108pub use collation::{
109 BinaryCollation, CollationAnnotation, CollationFunction, CollationRegistry, CollationSource,
110 NoCaseCollation, RtrimCollation, resolve_collation,
111};
112pub use datetime::register_datetime_builtins;
113pub use math::register_math_builtins;
114pub use scalar::{JSON_SUBTYPE, ScalarFunction};
115pub use vtab::{
116 ColumnContext, ConstraintOp, IndexConstraint, IndexConstraintUsage, IndexInfo, IndexOrderBy,
117 VirtualTable, VirtualTableCursor,
118};
119pub use window::{WindowAdapter, WindowFunction};
120pub use window_builtins::register_window_builtins;
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
124pub enum BuiltinFunctionFamily {
125 Scalar,
126 Aggregate,
127 Window,
128}
129
130impl BuiltinFunctionFamily {
131 #[must_use]
132 pub const fn label(self) -> &'static str {
133 match self {
134 Self::Scalar => "scalar",
135 Self::Aggregate => "aggregate",
136 Self::Window => "window",
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
143pub enum BuiltinFunctionClass {
144 CoreScalar,
145 MathScalar,
146 DateTimeScalar,
147 Aggregate,
148 Window,
149}
150
151impl BuiltinFunctionClass {
152 #[must_use]
153 pub const fn label(self) -> &'static str {
154 match self {
155 Self::CoreScalar => "core_scalar",
156 Self::MathScalar => "math_scalar",
157 Self::DateTimeScalar => "datetime_scalar",
158 Self::Aggregate => "aggregate",
159 Self::Window => "window",
160 }
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct BuiltinFunctionSurfaceEntry {
167 pub name: String,
169 pub num_args: i32,
171 pub family: BuiltinFunctionFamily,
173 pub class: BuiltinFunctionClass,
175 pub is_alias: bool,
177 pub surface_id: &'static str,
179}
180
181const CORE_FUNCTION_SURFACE_ID: &str = "SURF-FUNC-CORE-011";
182const WINDOW_FUNCTION_SURFACE_ID: &str = "SURF-FUNC-WINDOW-012";
183
184#[must_use]
190pub fn builtin_function_surface_inventory() -> &'static [BuiltinFunctionSurfaceEntry] {
191 static INVENTORY: OnceLock<Vec<BuiltinFunctionSurfaceEntry>> = OnceLock::new();
192 INVENTORY
193 .get_or_init(|| {
194 let mut registry = FunctionRegistry::new();
195 register_builtins(&mut registry);
196 register_window_builtins(&mut registry);
197
198 let mut entries = Vec::with_capacity(
199 registry.scalars.len() + registry.aggregates.len() + registry.windows.len(),
200 );
201 extend_builtin_surface_entries(
202 &mut entries,
203 BuiltinFunctionFamily::Scalar,
204 registry.scalars.keys(),
205 );
206 extend_builtin_surface_entries(
207 &mut entries,
208 BuiltinFunctionFamily::Aggregate,
209 registry.aggregates.keys(),
210 );
211 extend_builtin_surface_entries(
212 &mut entries,
213 BuiltinFunctionFamily::Window,
214 registry.windows.keys(),
215 );
216 entries.sort_by(|left, right| {
217 (left.family, left.class, &left.name, left.num_args).cmp(&(
218 right.family,
219 right.class,
220 &right.name,
221 right.num_args,
222 ))
223 });
224 entries
225 })
226 .as_slice()
227}
228
229pub type ErasedAggregateFunction = dyn AggregateFunction<State = Box<dyn Any + Send>>;
231
232pub type ErasedWindowFunction = dyn WindowFunction<State = Box<dyn Any + Send>>;
234
235#[derive(Debug, Clone, Hash, Eq, PartialEq)]
240pub struct FunctionKey {
241 name: String,
243 num_args: i32,
245}
246
247impl FunctionKey {
248 #[must_use]
250 pub fn new(name: &str, num_args: i32) -> Self {
251 assert_valid_declared_args(num_args);
252 Self {
253 name: canonical_name(name),
254 num_args,
255 }
256 }
257}
258
259fn assert_valid_declared_args(num_args: i32) {
260 assert!(
261 num_args >= -1,
262 "function argument count must be -1 or non-negative"
263 );
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
272#[allow(clippy::struct_field_names)]
273pub struct FunctionArity {
274 declared_args: i32,
275 min_args: i32,
276 max_args: Option<i32>,
277}
278
279impl FunctionArity {
280 #[must_use]
282 pub fn exact(num_args: i32) -> Self {
283 assert!(num_args >= 0, "exact function arity must be non-negative");
284 Self {
285 declared_args: num_args,
286 min_args: num_args,
287 max_args: Some(num_args),
288 }
289 }
290
291 #[must_use]
293 pub fn variadic(min_args: i32, max_args: Option<i32>) -> Self {
294 assert!(min_args >= 0, "minimum function arity must be non-negative");
295 assert!(
296 max_args.is_none_or(|max| max >= min_args),
297 "maximum function arity must not be below its minimum"
298 );
299 Self {
300 declared_args: -1,
301 min_args,
302 max_args,
303 }
304 }
305
306 #[must_use]
308 pub const fn declared_args(self) -> i32 {
309 self.declared_args
310 }
311
312 #[must_use]
314 pub const fn min_args(self) -> i32 {
315 self.min_args
316 }
317
318 #[must_use]
320 pub const fn max_args(self) -> Option<i32> {
321 self.max_args
322 }
323
324 #[must_use]
326 pub fn accepts(self, num_args: i32) -> bool {
327 num_args >= self.min_args && self.max_args.is_none_or(|max| num_args <= max)
328 }
329
330 pub(crate) fn from_declared_args(
331 declared_args: i32,
332 variadic_bounds: impl FnOnce() -> (i32, Option<i32>),
333 ) -> Self {
334 assert_valid_declared_args(declared_args);
335 if declared_args == -1 {
336 let (min_args, max_args) = variadic_bounds();
337 Self::variadic(min_args, max_args)
338 } else {
339 Self::exact(declared_args)
340 }
341 }
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
351pub enum ApplicationFunctionKind {
352 Scalar,
354 Aggregate,
356 Window,
358}
359
360impl ApplicationFunctionKind {
361 #[must_use]
363 pub const fn label(self) -> &'static str {
364 match self {
365 Self::Scalar => "scalar",
366 Self::Aggregate => "aggregate",
367 Self::Window => "window",
368 }
369 }
370
371 #[must_use]
373 pub const fn is_aggregate_callable(self) -> bool {
374 matches!(self, Self::Aggregate | Self::Window)
375 }
376
377 #[must_use]
379 pub const fn is_window_callable(self) -> bool {
380 matches!(self, Self::Window)
381 }
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
386pub struct ApplicationFunctionResolution {
387 kind: ApplicationFunctionKind,
388 arity: FunctionArity,
389}
390
391impl ApplicationFunctionResolution {
392 #[must_use]
394 pub const fn kind(self) -> ApplicationFunctionKind {
395 self.kind
396 }
397
398 #[must_use]
400 pub const fn arity(self) -> FunctionArity {
401 self.arity
402 }
403}
404
405fn assert_key_matches_arity(key: &FunctionKey, arity: FunctionArity) {
406 assert_eq!(
407 key.num_args,
408 arity.declared_args(),
409 "function key and frozen arity contract must have the same declared argument count"
410 );
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq)]
421pub enum ScalarSchemaSafety {
422 Always,
424 Never,
426 DateTimeConditional,
428}
429
430impl ScalarSchemaSafety {
431 const fn from_deterministic(deterministic: bool) -> Self {
432 if deterministic {
433 Self::Always
434 } else {
435 Self::Never
436 }
437 }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq)]
449pub enum ScalarQueryConstancy {
450 Constant,
452 SlowChanging,
454 Volatile,
456}
457
458impl ScalarQueryConstancy {
459 const fn from_deterministic(deterministic: bool) -> Self {
460 if deterministic {
461 Self::Constant
462 } else {
463 Self::Volatile
464 }
465 }
466
467 #[must_use]
469 pub const fn is_query_constant(self) -> bool {
470 matches!(self, Self::Constant | Self::SlowChanging)
471 }
472}
473
474#[derive(Clone)]
481pub struct ResolvedScalarFunction {
482 function: Arc<dyn ScalarFunction>,
483 schema_safety: ScalarSchemaSafety,
484 query_constancy: ScalarQueryConstancy,
485 consumes_argument_collation: bool,
486}
487
488impl ResolvedScalarFunction {
489 #[must_use]
492 pub fn function(&self) -> Arc<dyn ScalarFunction> {
493 Arc::clone(&self.function)
494 }
495
496 #[must_use]
498 pub const fn schema_safety(&self) -> ScalarSchemaSafety {
499 self.schema_safety
500 }
501
502 #[must_use]
504 pub const fn query_constancy(&self) -> ScalarQueryConstancy {
505 self.query_constancy
506 }
507
508 #[must_use]
510 pub const fn consumes_argument_collation(&self) -> bool {
511 self.consumes_argument_collation
512 }
513}
514
515enum ApplicationFunction {
516 Scalar {
517 function: Arc<dyn ScalarFunction>,
518 arity: FunctionArity,
519 schema_safety: ScalarSchemaSafety,
520 query_constancy: ScalarQueryConstancy,
521 consumes_argument_collation: bool,
522 },
523 Aggregate {
524 function: Arc<ErasedAggregateFunction>,
525 arity: FunctionArity,
526 },
527 Window {
528 function: Arc<ErasedWindowFunction>,
529 aggregate: Arc<ErasedAggregateFunction>,
530 arity: FunctionArity,
531 },
532}
533
534impl ApplicationFunction {
535 const fn kind(&self) -> ApplicationFunctionKind {
536 match self {
537 Self::Scalar { .. } => ApplicationFunctionKind::Scalar,
538 Self::Aggregate { .. } => ApplicationFunctionKind::Aggregate,
539 Self::Window { .. } => ApplicationFunctionKind::Window,
540 }
541 }
542
543 const fn arity(&self) -> FunctionArity {
544 match self {
545 Self::Scalar { arity, .. }
546 | Self::Aggregate { arity, .. }
547 | Self::Window { arity, .. } => *arity,
548 }
549 }
550
551 const fn resolution(&self) -> ApplicationFunctionResolution {
552 ApplicationFunctionResolution {
553 kind: self.kind(),
554 arity: self.arity(),
555 }
556 }
557}
558
559impl Clone for ApplicationFunction {
560 fn clone(&self) -> Self {
561 match self {
562 Self::Scalar {
563 function,
564 arity,
565 schema_safety,
566 query_constancy,
567 consumes_argument_collation,
568 } => Self::Scalar {
569 function: Arc::clone(function),
570 arity: *arity,
571 schema_safety: *schema_safety,
572 query_constancy: *query_constancy,
573 consumes_argument_collation: *consumes_argument_collation,
574 },
575 Self::Aggregate { function, arity } => Self::Aggregate {
576 function: Arc::clone(function),
577 arity: *arity,
578 },
579 Self::Window {
580 function,
581 aggregate,
582 arity,
583 } => Self::Window {
584 function: Arc::clone(function),
585 aggregate: Arc::clone(aggregate),
586 arity: *arity,
587 },
588 }
589 }
590}
591
592pub struct DisplacedApplicationFunction {
598 _function: ApplicationFunction,
599}
600
601#[derive(Default)]
613pub struct FunctionRegistry {
614 application_functions: HashMap<String, HashMap<i32, ApplicationFunction>>,
618 scalars: HashMap<FunctionKey, Arc<dyn ScalarFunction>>,
619 scalar_arities: HashMap<FunctionKey, FunctionArity>,
620 scalar_schema_safety: HashMap<FunctionKey, ScalarSchemaSafety>,
621 scalar_query_constancy: HashMap<FunctionKey, ScalarQueryConstancy>,
622 scalar_argument_collation: HashMap<FunctionKey, bool>,
623 aggregates: HashMap<FunctionKey, Arc<ErasedAggregateFunction>>,
624 aggregate_arities: HashMap<FunctionKey, FunctionArity>,
625 windows: HashMap<FunctionKey, Arc<ErasedWindowFunction>>,
626 window_arities: HashMap<FunctionKey, FunctionArity>,
627}
628
629struct WrongArgCountScalarFunction {
630 display_name: String,
631}
632
633fn wrong_arg_count_message(display_name: &str) -> String {
634 format!("wrong number of arguments to function {display_name}()")
635}
636
637fn wrong_arg_display_name(canonical: &str) -> String {
638 canonical.to_ascii_lowercase()
639}
640
641impl WrongArgCountScalarFunction {
642 fn new(canonical: &str) -> Self {
643 Self {
644 display_name: wrong_arg_display_name(canonical),
645 }
646 }
647
648 fn message(&self) -> String {
649 wrong_arg_count_message(&self.display_name)
650 }
651}
652
653impl ScalarFunction for WrongArgCountScalarFunction {
654 fn invoke(&self, _args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
655 Err(FrankenError::function_error(self.message()))
656 }
657
658 fn num_args(&self) -> i32 {
659 -1
660 }
661
662 fn name(&self) -> &str {
663 &self.display_name
664 }
665}
666
667struct WrongArgCountAggregateFunction {
668 display_name: String,
669}
670
671impl WrongArgCountAggregateFunction {
672 fn new(canonical: &str) -> Self {
673 Self {
674 display_name: wrong_arg_display_name(canonical),
675 }
676 }
677
678 fn message(&self) -> String {
679 wrong_arg_count_message(&self.display_name)
680 }
681}
682
683impl AggregateFunction for WrongArgCountAggregateFunction {
684 type State = ();
685
686 fn initial_state(&self) -> Self::State {}
687
688 fn step(&self, _state: &mut Self::State, _args: &[SqliteValue]) -> fsqlite_error::Result<()> {
689 Err(FrankenError::function_error(self.message()))
690 }
691
692 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
693 Err(FrankenError::function_error(self.message()))
694 }
695
696 fn num_args(&self) -> i32 {
697 -1
698 }
699
700 fn name(&self) -> &str {
701 &self.display_name
702 }
703}
704
705struct WrongArgCountWindowFunction {
706 display_name: String,
707}
708
709impl WrongArgCountWindowFunction {
710 fn new(canonical: &str) -> Self {
711 Self {
712 display_name: wrong_arg_display_name(canonical),
713 }
714 }
715
716 fn message(&self) -> String {
717 wrong_arg_count_message(&self.display_name)
718 }
719}
720
721impl WindowFunction for WrongArgCountWindowFunction {
722 type State = ();
723
724 fn initial_state(&self) -> Self::State {}
725
726 fn step(&self, _state: &mut Self::State, _args: &[SqliteValue]) -> fsqlite_error::Result<()> {
727 Err(FrankenError::function_error(self.message()))
728 }
729
730 fn inverse(
731 &self,
732 _state: &mut Self::State,
733 _args: &[SqliteValue],
734 ) -> fsqlite_error::Result<()> {
735 Err(FrankenError::function_error(self.message()))
736 }
737
738 fn value(&self, _state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
739 Err(FrankenError::function_error(self.message()))
740 }
741
742 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
743 Err(FrankenError::function_error(self.message()))
744 }
745
746 fn num_args(&self) -> i32 {
747 -1
748 }
749
750 fn name(&self) -> &str {
751 &self.display_name
752 }
753}
754
755struct WindowAggregateBridge {
761 function: Arc<ErasedWindowFunction>,
762 name: String,
763 arity: FunctionArity,
764}
765
766impl AggregateFunction for WindowAggregateBridge {
767 type State = Box<dyn Any + Send>;
768
769 fn initial_state(&self) -> Self::State {
770 self.function.initial_state()
771 }
772
773 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
774 self.function.step(state, args)
775 }
776
777 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
778 self.function.finalize(state)
779 }
780
781 fn num_args(&self) -> i32 {
782 self.arity.declared_args()
783 }
784
785 fn min_args(&self) -> i32 {
786 self.arity.min_args()
787 }
788
789 fn max_args(&self) -> Option<i32> {
790 self.arity.max_args()
791 }
792
793 fn arity(&self) -> FunctionArity {
794 self.arity
795 }
796
797 fn name(&self) -> &str {
798 &self.name
799 }
800}
801
802impl FunctionRegistry {
803 #[must_use]
805 pub fn new() -> Self {
806 Self::default()
807 }
808
809 #[must_use]
814 pub fn clone_from_arc(arc: &Arc<Self>) -> Self {
815 Self {
816 application_functions: arc.application_functions.clone(),
817 scalars: arc.scalars.clone(),
818 scalar_arities: arc.scalar_arities.clone(),
819 scalar_schema_safety: arc.scalar_schema_safety.clone(),
820 scalar_query_constancy: arc.scalar_query_constancy.clone(),
821 scalar_argument_collation: arc.scalar_argument_collation.clone(),
822 aggregates: arc.aggregates.clone(),
823 aggregate_arities: arc.aggregate_arities.clone(),
824 windows: arc.windows.clone(),
825 window_arities: arc.window_arities.clone(),
826 }
827 }
828
829 fn application_function_precanonical(
830 &self,
831 canonical: &str,
832 num_args: i32,
833 ) -> Option<&ApplicationFunction> {
834 let overloads = self.application_functions.get(canonical)?;
835 if let Some(function) = overloads.get(&num_args)
836 && function.arity().accepts(num_args)
837 {
838 return Some(function);
839 }
840
841 overloads
842 .get(&-1)
843 .filter(|function| function.arity().accepts(num_args))
844 }
845
846 fn application_name_has_kind_precanonical(
847 &self,
848 canonical: &str,
849 matches_kind: impl Fn(ApplicationFunctionKind) -> bool,
850 ) -> bool {
851 self.application_functions
852 .get(canonical)
853 .is_some_and(|overloads| {
854 overloads
855 .values()
856 .any(|function| matches_kind(function.kind()))
857 })
858 }
859
860 #[must_use]
866 pub fn resolve_application_function(
867 &self,
868 name: &str,
869 num_args: i32,
870 ) -> Option<ApplicationFunctionResolution> {
871 let canonical = canonical_name(name);
872 self.resolve_application_function_precanonical(&canonical, num_args)
873 }
874
875 #[must_use]
877 pub fn resolve_application_function_precanonical(
878 &self,
879 canonical: &str,
880 num_args: i32,
881 ) -> Option<ApplicationFunctionResolution> {
882 self.application_function_precanonical(canonical, num_args)
883 .map(ApplicationFunction::resolution)
884 }
885
886 #[must_use]
888 pub fn contains_application_function(&self, name: &str) -> bool {
889 let canonical = canonical_name(name);
890 self.application_functions.contains_key(&canonical)
891 }
892
893 #[must_use]
902 pub fn has_application_functions(&self) -> bool {
903 !self.application_functions.is_empty()
904 }
905
906 fn replace_application_function(
907 &mut self,
908 key: FunctionKey,
909 function: ApplicationFunction,
910 ) -> Option<DisplacedApplicationFunction> {
911 assert_key_matches_arity(&key, function.arity());
912 self.application_functions
913 .entry(key.name)
914 .or_default()
915 .insert(key.num_args, function)
916 .map(|function| DisplacedApplicationFunction {
917 _function: function,
918 })
919 }
920
921 pub fn register_application_scalar_captured<F>(
927 &mut self,
928 name: &str,
929 arity: FunctionArity,
930 deterministic: bool,
931 consumes_argument_collation: bool,
932 function: F,
933 ) -> Option<DisplacedApplicationFunction>
934 where
935 F: ScalarFunction + 'static,
936 {
937 let key = FunctionKey::new(name, arity.declared_args());
938 self.replace_application_function(
939 key,
940 ApplicationFunction::Scalar {
941 function: Arc::new(function),
942 arity,
943 schema_safety: ScalarSchemaSafety::from_deterministic(deterministic),
944 query_constancy: ScalarQueryConstancy::from_deterministic(deterministic),
945 consumes_argument_collation,
946 },
947 )
948 }
949
950 pub fn register_application_aggregate_captured<F>(
955 &mut self,
956 name: &str,
957 arity: FunctionArity,
958 function: F,
959 ) -> Option<DisplacedApplicationFunction>
960 where
961 F: AggregateFunction + 'static,
962 F::State: 'static,
963 {
964 let key = FunctionKey::new(name, arity.declared_args());
965 self.replace_application_function(
966 key,
967 ApplicationFunction::Aggregate {
968 function: Arc::new(AggregateAdapter::new(function)),
969 arity,
970 },
971 )
972 }
973
974 pub fn register_application_window_captured<F>(
980 &mut self,
981 name: &str,
982 arity: FunctionArity,
983 function: F,
984 ) -> (
985 Arc<ErasedWindowFunction>,
986 Option<DisplacedApplicationFunction>,
987 )
988 where
989 F: WindowFunction + 'static,
990 F::State: 'static,
991 {
992 let key = FunctionKey::new(name, arity.declared_args());
993 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
994 let aggregate: Arc<ErasedAggregateFunction> = Arc::new(WindowAggregateBridge {
995 function: Arc::clone(®istered),
996 name: key.name.clone(),
997 arity,
998 });
999 let displaced = self.replace_application_function(
1000 key,
1001 ApplicationFunction::Window {
1002 function: Arc::clone(®istered),
1003 aggregate,
1004 arity,
1005 },
1006 );
1007 (registered, displaced)
1008 }
1009
1010 pub fn register_scalar<F>(&mut self, function: F) -> Option<Arc<dyn ScalarFunction>>
1015 where
1016 F: ScalarFunction + 'static,
1017 {
1018 let name = function.name().to_owned();
1019 let arity = function.arity();
1020 let deterministic = function.is_deterministic();
1021 let consumes_argument_collation = function.consumes_argument_collation();
1022 self.register_scalar_captured(
1023 &name,
1024 arity,
1025 deterministic,
1026 consumes_argument_collation,
1027 function,
1028 )
1029 }
1030
1031 pub fn register_scalar_keyed<F>(
1038 &mut self,
1039 key: FunctionKey,
1040 arity: FunctionArity,
1041 deterministic: bool,
1042 consumes_argument_collation: bool,
1043 function: F,
1044 ) -> Option<Arc<dyn ScalarFunction>>
1045 where
1046 F: ScalarFunction + 'static,
1047 {
1048 assert_key_matches_arity(&key, arity);
1049 self.scalar_arities.insert(key.clone(), arity);
1050 self.scalar_schema_safety.insert(
1051 key.clone(),
1052 ScalarSchemaSafety::from_deterministic(deterministic),
1053 );
1054 self.scalar_query_constancy.insert(
1055 key.clone(),
1056 ScalarQueryConstancy::from_deterministic(deterministic),
1057 );
1058 self.scalar_argument_collation
1059 .insert(key.clone(), consumes_argument_collation);
1060 self.scalars.insert(key, Arc::new(function))
1061 }
1062
1063 pub fn register_scalar_captured<F>(
1069 &mut self,
1070 name: &str,
1071 arity: FunctionArity,
1072 deterministic: bool,
1073 consumes_argument_collation: bool,
1074 function: F,
1075 ) -> Option<Arc<dyn ScalarFunction>>
1076 where
1077 F: ScalarFunction + 'static,
1078 {
1079 let key = FunctionKey::new(name, arity.declared_args());
1080 self.scalar_arities.insert(key.clone(), arity);
1081 self.scalar_schema_safety.insert(
1082 key.clone(),
1083 ScalarSchemaSafety::from_deterministic(deterministic),
1084 );
1085 self.scalar_query_constancy.insert(
1086 key.clone(),
1087 ScalarQueryConstancy::from_deterministic(deterministic),
1088 );
1089 self.scalar_argument_collation
1090 .insert(key.clone(), consumes_argument_collation);
1091 self.scalars.insert(key, Arc::new(function))
1092 }
1093
1094 pub(crate) fn register_conditionally_deterministic_scalar<F>(
1098 &mut self,
1099 function: F,
1100 ) -> Option<Arc<dyn ScalarFunction>>
1101 where
1102 F: ScalarFunction + 'static,
1103 {
1104 let name = function.name().to_owned();
1105 let arity = function.arity();
1106 let consumes_argument_collation = function.consumes_argument_collation();
1107 let key = FunctionKey::new(&name, arity.declared_args());
1108 self.scalar_arities.insert(key.clone(), arity);
1109 self.scalar_schema_safety
1110 .insert(key.clone(), ScalarSchemaSafety::DateTimeConditional);
1111 self.scalar_query_constancy
1112 .insert(key.clone(), ScalarQueryConstancy::SlowChanging);
1113 self.scalar_argument_collation
1114 .insert(key.clone(), consumes_argument_collation);
1115 self.scalars.insert(key, Arc::new(function))
1116 }
1117
1118 pub(crate) fn register_slow_changing_scalar<F>(
1124 &mut self,
1125 function: F,
1126 ) -> Option<Arc<dyn ScalarFunction>>
1127 where
1128 F: ScalarFunction + 'static,
1129 {
1130 let name = function.name().to_owned();
1131 let arity = function.arity();
1132 let consumes_argument_collation = function.consumes_argument_collation();
1133 let key = FunctionKey::new(&name, arity.declared_args());
1134 self.scalar_arities.insert(key.clone(), arity);
1135 self.scalar_schema_safety
1136 .insert(key.clone(), ScalarSchemaSafety::Never);
1137 self.scalar_query_constancy
1138 .insert(key.clone(), ScalarQueryConstancy::SlowChanging);
1139 self.scalar_argument_collation
1140 .insert(key.clone(), consumes_argument_collation);
1141 self.scalars.insert(key, Arc::new(function))
1142 }
1143
1144 pub fn register_aggregate<F>(&mut self, function: F) -> Option<Arc<ErasedAggregateFunction>>
1148 where
1149 F: AggregateFunction + 'static,
1150 F::State: 'static,
1151 {
1152 let name = function.name().to_owned();
1153 let arity = function.arity();
1154 self.register_aggregate_captured(&name, arity, function)
1155 }
1156
1157 pub fn register_aggregate_keyed<F>(
1162 &mut self,
1163 key: FunctionKey,
1164 arity: FunctionArity,
1165 function: F,
1166 ) -> Option<Arc<ErasedAggregateFunction>>
1167 where
1168 F: AggregateFunction + 'static,
1169 F::State: 'static,
1170 {
1171 assert_key_matches_arity(&key, arity);
1172 self.aggregate_arities.insert(key.clone(), arity);
1173 self.aggregates
1174 .insert(key, Arc::new(AggregateAdapter::new(function)))
1175 }
1176
1177 pub fn register_aggregate_captured<F>(
1182 &mut self,
1183 name: &str,
1184 arity: FunctionArity,
1185 function: F,
1186 ) -> Option<Arc<ErasedAggregateFunction>>
1187 where
1188 F: AggregateFunction + 'static,
1189 F::State: 'static,
1190 {
1191 let key = FunctionKey::new(name, arity.declared_args());
1192 self.aggregate_arities.insert(key.clone(), arity);
1193 self.aggregates
1194 .insert(key, Arc::new(AggregateAdapter::new(function)))
1195 }
1196
1197 pub fn register_window<F>(&mut self, function: F) -> Option<Arc<ErasedWindowFunction>>
1201 where
1202 F: WindowFunction + 'static,
1203 F::State: 'static,
1204 {
1205 let name = function.name().to_owned();
1206 let arity = function.arity();
1207 let (_, displaced) = self.register_window_captured(&name, arity, function);
1208 displaced
1209 }
1210
1211 pub fn register_window_keyed<F>(
1217 &mut self,
1218 key: FunctionKey,
1219 arity: FunctionArity,
1220 function: F,
1221 ) -> (Arc<ErasedWindowFunction>, Option<Arc<ErasedWindowFunction>>)
1222 where
1223 F: WindowFunction + 'static,
1224 F::State: 'static,
1225 {
1226 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
1227 assert_key_matches_arity(&key, arity);
1228 self.window_arities.insert(key.clone(), arity);
1229 let displaced = self.windows.insert(key, Arc::clone(®istered));
1230 (registered, displaced)
1231 }
1232
1233 pub fn register_window_captured<F>(
1238 &mut self,
1239 name: &str,
1240 arity: FunctionArity,
1241 function: F,
1242 ) -> (Arc<ErasedWindowFunction>, Option<Arc<ErasedWindowFunction>>)
1243 where
1244 F: WindowFunction + 'static,
1245 F::State: 'static,
1246 {
1247 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
1248 let key = FunctionKey::new(name, arity.declared_args());
1249 self.window_arities.insert(key.clone(), arity);
1250 let displaced = self.windows.insert(key, Arc::clone(®istered));
1251 (registered, displaced)
1252 }
1253
1254 #[must_use]
1259 pub fn find_scalar(&self, name: &str, num_args: i32) -> Option<Arc<dyn ScalarFunction>> {
1260 self.resolve_scalar(name, num_args)
1261 .map(|resolved| resolved.function)
1262 }
1263
1264 #[must_use]
1269 pub fn find_scalar_precanonical(
1270 &self,
1271 canonical: &str,
1272 num_args: i32,
1273 ) -> Option<Arc<dyn ScalarFunction>> {
1274 self.resolve_scalar_precanonical(canonical, num_args)
1275 .map(|resolved| resolved.function)
1276 }
1277
1278 #[must_use]
1281 pub fn resolve_scalar(&self, name: &str, num_args: i32) -> Option<ResolvedScalarFunction> {
1282 let canonical = canonical_name(name);
1283 self.resolve_scalar_precanonical(&canonical, num_args)
1284 }
1285
1286 #[must_use]
1288 pub fn resolve_scalar_precanonical(
1289 &self,
1290 canonical: &str,
1291 num_args: i32,
1292 ) -> Option<ResolvedScalarFunction> {
1293 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1294 return match application {
1295 ApplicationFunction::Scalar {
1296 function,
1297 schema_safety,
1298 query_constancy,
1299 consumes_argument_collation,
1300 ..
1301 } => {
1302 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "application", "registry lookup");
1303 Some(ResolvedScalarFunction {
1304 function: Arc::clone(function),
1305 schema_safety: *schema_safety,
1306 query_constancy: *query_constancy,
1307 consumes_argument_collation: *consumes_argument_collation,
1308 })
1309 }
1310 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => {
1311 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "shadowed_by_application", "registry lookup");
1312 None
1313 }
1314 };
1315 }
1316 let exact = FunctionKey {
1317 name: canonical.to_owned(),
1318 num_args,
1319 };
1320 if let Some(function) = self.scalars.get(&exact) {
1321 let Some(arity) = self.scalar_arities.get(&exact).copied() else {
1322 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "missing_arity", "registry lookup");
1323 return Some(ResolvedScalarFunction {
1324 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1325 schema_safety: ScalarSchemaSafety::Never,
1326 query_constancy: ScalarQueryConstancy::Volatile,
1327 consumes_argument_collation: false,
1328 });
1329 };
1330 if arity.accepts(num_args) {
1331 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "exact", "registry lookup");
1332 return Some(ResolvedScalarFunction {
1333 function: Arc::clone(function),
1334 schema_safety: self
1335 .scalar_schema_safety
1336 .get(&exact)
1337 .copied()
1338 .unwrap_or(ScalarSchemaSafety::Never),
1339 query_constancy: self
1340 .scalar_query_constancy
1341 .get(&exact)
1342 .copied()
1343 .unwrap_or(ScalarQueryConstancy::Volatile),
1344 consumes_argument_collation: self
1345 .scalar_argument_collation
1346 .get(&exact)
1347 .copied()
1348 .unwrap_or(false),
1349 });
1350 }
1351 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "wrong_arity", "registry lookup");
1352 return Some(ResolvedScalarFunction {
1353 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1354 schema_safety: ScalarSchemaSafety::Never,
1355 query_constancy: ScalarQueryConstancy::Volatile,
1356 consumes_argument_collation: false,
1357 });
1358 }
1359 let variadic = FunctionKey {
1360 name: canonical.to_owned(),
1361 num_args: -1,
1362 };
1363 if let Some(function) = self.scalars.get(&variadic) {
1364 let Some(arity) = self.scalar_arities.get(&variadic).copied() else {
1365 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "missing_arity", "registry lookup");
1366 return Some(ResolvedScalarFunction {
1367 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1368 schema_safety: ScalarSchemaSafety::Never,
1369 query_constancy: ScalarQueryConstancy::Volatile,
1370 consumes_argument_collation: false,
1371 });
1372 };
1373 if arity.accepts(num_args) {
1374 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "variadic", "registry lookup");
1375 return Some(ResolvedScalarFunction {
1376 function: Arc::clone(function),
1377 schema_safety: self
1378 .scalar_schema_safety
1379 .get(&variadic)
1380 .copied()
1381 .unwrap_or(ScalarSchemaSafety::Never),
1382 query_constancy: self
1383 .scalar_query_constancy
1384 .get(&variadic)
1385 .copied()
1386 .unwrap_or(ScalarQueryConstancy::Volatile),
1387 consumes_argument_collation: self
1388 .scalar_argument_collation
1389 .get(&variadic)
1390 .copied()
1391 .unwrap_or(false),
1392 });
1393 }
1394 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "wrong_arity", "registry lookup");
1395 return Some(ResolvedScalarFunction {
1396 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1397 schema_safety: ScalarSchemaSafety::Never,
1398 query_constancy: ScalarQueryConstancy::Volatile,
1399 consumes_argument_collation: false,
1400 });
1401 }
1402 if self.scalars.keys().any(|key| key.name == canonical)
1403 || self.application_name_has_kind_precanonical(canonical, |kind| {
1404 kind == ApplicationFunctionKind::Scalar
1405 })
1406 {
1407 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "wrong_arity", "registry lookup");
1408 return Some(ResolvedScalarFunction {
1409 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1410 schema_safety: ScalarSchemaSafety::Never,
1411 query_constancy: ScalarQueryConstancy::Volatile,
1412 consumes_argument_collation: false,
1413 });
1414 }
1415 debug!(
1416 name = %canonical,
1417 arity = num_args,
1418 kind = "scalar",
1419 hit = "miss",
1420 "registry lookup"
1421 );
1422 None
1423 }
1424
1425 #[must_use]
1429 pub fn find_aggregate(
1430 &self,
1431 name: &str,
1432 num_args: i32,
1433 ) -> Option<Arc<ErasedAggregateFunction>> {
1434 let canon = canonical_name(name);
1435 self.find_aggregate_precanonical(&canon, num_args)
1436 }
1437
1438 #[must_use]
1440 pub fn find_aggregate_precanonical(
1441 &self,
1442 canonical: &str,
1443 num_args: i32,
1444 ) -> Option<Arc<ErasedAggregateFunction>> {
1445 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1446 return match application {
1447 ApplicationFunction::Aggregate { function, .. } => {
1448 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "application", "registry lookup");
1449 Some(Arc::clone(function))
1450 }
1451 ApplicationFunction::Window { aggregate, .. } => {
1452 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "application_window", "registry lookup");
1453 Some(Arc::clone(aggregate))
1454 }
1455 ApplicationFunction::Scalar { .. } => {
1456 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "shadowed_by_application", "registry lookup");
1457 None
1458 }
1459 };
1460 }
1461 let exact = FunctionKey {
1462 name: canonical.to_owned(),
1463 num_args,
1464 };
1465 if let Some(function) = self.aggregates.get(&exact) {
1466 let Some(arity) = self.aggregate_arities.get(&exact).copied() else {
1467 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "missing_arity", "registry lookup");
1468 return Some(Arc::new(AggregateAdapter::new(
1469 WrongArgCountAggregateFunction::new(canonical),
1470 )));
1471 };
1472 if arity.accepts(num_args) {
1473 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "exact", "registry lookup");
1474 return Some(Arc::clone(function));
1475 }
1476 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1477 return Some(Arc::new(AggregateAdapter::new(
1478 WrongArgCountAggregateFunction::new(canonical),
1479 )));
1480 }
1481 let variadic = FunctionKey {
1482 name: canonical.to_owned(),
1483 num_args: -1,
1484 };
1485 if let Some(function) = self.aggregates.get(&variadic) {
1486 let Some(arity) = self.aggregate_arities.get(&variadic).copied() else {
1487 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "missing_arity", "registry lookup");
1488 return Some(Arc::new(AggregateAdapter::new(
1489 WrongArgCountAggregateFunction::new(canonical),
1490 )));
1491 };
1492 if arity.accepts(num_args) {
1493 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "variadic", "registry lookup");
1494 return Some(Arc::clone(function));
1495 }
1496 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1497 return Some(Arc::new(AggregateAdapter::new(
1498 WrongArgCountAggregateFunction::new(canonical),
1499 )));
1500 }
1501 if self.aggregates.keys().any(|key| key.name == canonical)
1502 || self.application_name_has_kind_precanonical(canonical, |kind| {
1503 kind.is_aggregate_callable()
1504 })
1505 {
1506 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1507 return Some(Arc::new(AggregateAdapter::new(
1508 WrongArgCountAggregateFunction::new(canonical),
1509 )));
1510 }
1511 debug!(
1512 name = %canonical,
1513 arity = num_args,
1514 kind = "aggregate",
1515 hit = "miss",
1516 "registry lookup"
1517 );
1518 None
1519 }
1520
1521 #[must_use]
1525 pub fn find_window(&self, name: &str, num_args: i32) -> Option<Arc<ErasedWindowFunction>> {
1526 let canon = canonical_name(name);
1527 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1528 return match application {
1529 ApplicationFunction::Window { function, .. } => {
1530 debug!(name = %canon, arity = num_args, kind = "window", hit = "application", "registry lookup");
1531 Some(Arc::clone(function))
1532 }
1533 ApplicationFunction::Scalar { .. } | ApplicationFunction::Aggregate { .. } => {
1534 debug!(name = %canon, arity = num_args, kind = "window", hit = "shadowed_by_application", "registry lookup");
1535 None
1536 }
1537 };
1538 }
1539 let exact = FunctionKey {
1540 name: canon.clone(),
1541 num_args,
1542 };
1543 if let Some(function) = self.windows.get(&exact) {
1544 let Some(arity) = self.window_arities.get(&exact).copied() else {
1545 debug!(name = %canon, arity = num_args, kind = "window", hit = "missing_arity", "registry lookup");
1546 return Some(Arc::new(WindowAdapter::new(
1547 WrongArgCountWindowFunction::new(&canon),
1548 )));
1549 };
1550 if arity.accepts(num_args) {
1551 debug!(name = %canon, arity = num_args, kind = "window", hit = "exact", "registry lookup");
1552 return Some(Arc::clone(function));
1553 }
1554 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1555 return Some(Arc::new(WindowAdapter::new(
1556 WrongArgCountWindowFunction::new(&canon),
1557 )));
1558 }
1559 let variadic = FunctionKey {
1560 name: canon.clone(),
1561 num_args: -1,
1562 };
1563 if let Some(function) = self.windows.get(&variadic) {
1564 let Some(arity) = self.window_arities.get(&variadic).copied() else {
1565 debug!(name = %canon, arity = num_args, kind = "window", hit = "missing_arity", "registry lookup");
1566 return Some(Arc::new(WindowAdapter::new(
1567 WrongArgCountWindowFunction::new(&canon),
1568 )));
1569 };
1570 if arity.accepts(num_args) {
1571 debug!(name = %canon, arity = num_args, kind = "window", hit = "variadic", "registry lookup");
1572 return Some(Arc::clone(function));
1573 }
1574 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1575 return Some(Arc::new(WindowAdapter::new(
1576 WrongArgCountWindowFunction::new(&canon),
1577 )));
1578 }
1579 if self.windows.keys().any(|key| key.name == canon)
1580 || self.application_name_has_kind_precanonical(&canon, |kind| {
1581 kind == ApplicationFunctionKind::Window
1582 })
1583 {
1584 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1585 return Some(Arc::new(WindowAdapter::new(
1586 WrongArgCountWindowFunction::new(&canon),
1587 )));
1588 }
1589 debug!(
1590 name = %canon,
1591 arity = num_args,
1592 kind = "window",
1593 hit = "miss",
1594 "registry lookup"
1595 );
1596 None
1597 }
1598
1599 #[must_use]
1602 pub fn contains_scalar(&self, name: &str) -> bool {
1603 let canon = canonical_name(name);
1604 self.scalars.keys().any(|k| k.name == canon)
1605 || self.application_name_has_kind_precanonical(&canon, |kind| {
1606 kind == ApplicationFunctionKind::Scalar
1607 })
1608 }
1609
1610 #[must_use]
1613 pub fn contains_aggregate(&self, name: &str) -> bool {
1614 let canon = canonical_name(name);
1615 self.aggregates.keys().any(|k| k.name == canon)
1616 || self
1617 .application_name_has_kind_precanonical(&canon, |kind| kind.is_aggregate_callable())
1618 }
1619
1620 #[must_use]
1623 pub fn contains_window(&self, name: &str) -> bool {
1624 let canon = canonical_name(name);
1625 self.windows.keys().any(|k| k.name == canon)
1626 || self.application_name_has_kind_precanonical(&canon, |kind| {
1627 kind == ApplicationFunctionKind::Window
1628 })
1629 }
1630
1631 #[must_use]
1638 pub fn scalar_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1639 let canon = canonical_name(name);
1640 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1641 return matches!(application, ApplicationFunction::Scalar { .. }).then_some(true);
1642 }
1643 let exact = FunctionKey {
1644 name: canon.clone(),
1645 num_args,
1646 };
1647 if self.scalars.contains_key(&exact) {
1648 return Some(
1649 self.scalar_arities
1650 .get(&exact)
1651 .is_some_and(|arity| arity.accepts(num_args)),
1652 );
1653 }
1654
1655 let variadic = FunctionKey {
1656 name: canon.clone(),
1657 num_args: -1,
1658 };
1659 if self.scalars.contains_key(&variadic) {
1660 return Some(
1661 self.scalar_arities
1662 .get(&variadic)
1663 .is_some_and(|arity| arity.accepts(num_args)),
1664 );
1665 }
1666
1667 (self.scalars.keys().any(|key| key.name == canon)
1668 || self.application_name_has_kind_precanonical(&canon, |kind| {
1669 kind == ApplicationFunctionKind::Scalar
1670 }))
1671 .then_some(false)
1672 }
1673
1674 #[must_use]
1679 pub fn scalar_schema_safety(&self, name: &str, num_args: i32) -> Option<ScalarSchemaSafety> {
1680 let canonical = canonical_name(name);
1681 self.scalar_schema_safety_precanonical(&canonical, num_args)
1682 }
1683
1684 #[must_use]
1686 pub fn scalar_schema_safety_precanonical(
1687 &self,
1688 canonical: &str,
1689 num_args: i32,
1690 ) -> Option<ScalarSchemaSafety> {
1691 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1692 return match application {
1693 ApplicationFunction::Scalar { schema_safety, .. } => Some(*schema_safety),
1694 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => None,
1695 };
1696 }
1697 let exact = FunctionKey {
1698 name: canonical.to_owned(),
1699 num_args,
1700 };
1701 if self.scalars.contains_key(&exact) {
1702 let accepts = self
1703 .scalar_arities
1704 .get(&exact)
1705 .is_some_and(|arity| arity.accepts(num_args));
1706 return Some(if accepts {
1707 self.scalar_schema_safety
1708 .get(&exact)
1709 .copied()
1710 .unwrap_or(ScalarSchemaSafety::Never)
1711 } else {
1712 ScalarSchemaSafety::Never
1713 });
1714 }
1715
1716 let variadic = FunctionKey {
1717 name: canonical.to_owned(),
1718 num_args: -1,
1719 };
1720 if self.scalars.contains_key(&variadic) {
1721 let Some(arity) = self.scalar_arities.get(&variadic).copied() else {
1722 return Some(ScalarSchemaSafety::Never);
1723 };
1724 if !arity.accepts(num_args) {
1725 return None;
1726 }
1727 return Some(
1728 self.scalar_schema_safety
1729 .get(&variadic)
1730 .copied()
1731 .unwrap_or(ScalarSchemaSafety::Never),
1732 );
1733 }
1734 None
1735 }
1736
1737 #[must_use]
1741 pub fn scalar_is_deterministic(&self, name: &str, num_args: i32) -> Option<bool> {
1742 self.scalar_schema_safety(name, num_args)
1743 .map(|safety| safety != ScalarSchemaSafety::Never)
1744 }
1745
1746 #[must_use]
1751 pub fn scalar_consumes_argument_collation(&self, name: &str, num_args: i32) -> Option<bool> {
1752 let canonical = canonical_name(name);
1753 self.scalar_consumes_argument_collation_precanonical(&canonical, num_args)
1754 }
1755
1756 #[must_use]
1759 pub fn scalar_consumes_argument_collation_precanonical(
1760 &self,
1761 canonical: &str,
1762 num_args: i32,
1763 ) -> Option<bool> {
1764 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1765 return match application {
1766 ApplicationFunction::Scalar {
1767 consumes_argument_collation,
1768 ..
1769 } => Some(*consumes_argument_collation),
1770 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => None,
1771 };
1772 }
1773 let exact = FunctionKey {
1774 name: canonical.to_owned(),
1775 num_args,
1776 };
1777 if self.scalars.contains_key(&exact) {
1778 if !self
1779 .scalar_arities
1780 .get(&exact)
1781 .is_some_and(|arity| arity.accepts(num_args))
1782 {
1783 return None;
1784 }
1785 return Some(
1786 self.scalar_argument_collation
1787 .get(&exact)
1788 .copied()
1789 .unwrap_or(false),
1790 );
1791 }
1792
1793 let variadic = FunctionKey {
1794 name: canonical.to_owned(),
1795 num_args: -1,
1796 };
1797 if self.scalars.contains_key(&variadic) {
1798 if !self
1799 .scalar_arities
1800 .get(&variadic)
1801 .is_some_and(|arity| arity.accepts(num_args))
1802 {
1803 return None;
1804 }
1805 return Some(
1806 self.scalar_argument_collation
1807 .get(&variadic)
1808 .copied()
1809 .unwrap_or(false),
1810 );
1811 }
1812 None
1813 }
1814
1815 #[must_use]
1821 pub fn aggregate_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1822 let canon = canonical_name(name);
1823 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1824 return application.kind().is_aggregate_callable().then_some(true);
1825 }
1826 let exact = FunctionKey {
1827 name: canon.clone(),
1828 num_args,
1829 };
1830 if self.aggregates.contains_key(&exact) {
1831 return Some(
1832 self.aggregate_arities
1833 .get(&exact)
1834 .is_some_and(|arity| arity.accepts(num_args)),
1835 );
1836 }
1837
1838 let variadic = FunctionKey {
1839 name: canon.clone(),
1840 num_args: -1,
1841 };
1842 if self.aggregates.contains_key(&variadic) {
1843 return Some(
1844 self.aggregate_arities
1845 .get(&variadic)
1846 .is_some_and(|arity| arity.accepts(num_args)),
1847 );
1848 }
1849
1850 (self.aggregates.keys().any(|key| key.name == canon)
1851 || self.application_name_has_kind_precanonical(&canon, |kind| {
1852 kind.is_aggregate_callable()
1853 }))
1854 .then_some(false)
1855 }
1856
1857 #[must_use]
1864 pub fn window_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1865 let canon = canonical_name(name);
1866 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1867 return (application.kind() == ApplicationFunctionKind::Window).then_some(true);
1868 }
1869 let exact = FunctionKey {
1870 name: canon.clone(),
1871 num_args,
1872 };
1873 if self.windows.contains_key(&exact) {
1874 return Some(
1875 self.window_arities
1876 .get(&exact)
1877 .is_some_and(|arity| arity.accepts(num_args)),
1878 );
1879 }
1880
1881 let variadic = FunctionKey {
1882 name: canon.clone(),
1883 num_args: -1,
1884 };
1885 if self.windows.contains_key(&variadic) {
1886 return Some(
1887 self.window_arities
1888 .get(&variadic)
1889 .is_some_and(|arity| arity.accepts(num_args)),
1890 );
1891 }
1892
1893 (self.windows.keys().any(|key| key.name == canon)
1894 || self.application_name_has_kind_precanonical(&canon, |kind| {
1895 kind == ApplicationFunctionKind::Window
1896 }))
1897 .then_some(false)
1898 }
1899
1900 #[must_use]
1904 pub fn aggregate_names_lowercase(&self) -> Vec<String> {
1905 let mut names: Vec<String> = self
1906 .aggregates
1907 .keys()
1908 .map(|k| k.name.to_ascii_lowercase())
1909 .chain(
1910 self.application_functions
1911 .iter()
1912 .filter(|(_, overloads)| {
1913 overloads
1914 .values()
1915 .any(|function| function.kind().is_aggregate_callable())
1916 })
1917 .map(|(name, _)| name.to_ascii_lowercase()),
1918 )
1919 .collect();
1920 names.sort();
1921 names.dedup();
1922 names
1923 }
1924}
1925
1926fn extend_builtin_surface_entries<'a>(
1927 entries: &mut Vec<BuiltinFunctionSurfaceEntry>,
1928 family: BuiltinFunctionFamily,
1929 keys: impl Iterator<Item = &'a FunctionKey>,
1930) {
1931 for key in keys {
1932 let name = key.name.to_ascii_lowercase();
1933 let class = builtin_function_class(&name, family);
1934 entries.push(BuiltinFunctionSurfaceEntry {
1935 is_alias: builtin_function_alias_flag(&name, family),
1936 surface_id: builtin_function_surface_id(family),
1937 name,
1938 num_args: key.num_args,
1939 family,
1940 class,
1941 });
1942 }
1943}
1944
1945fn builtin_function_class(name: &str, family: BuiltinFunctionFamily) -> BuiltinFunctionClass {
1946 match family {
1947 BuiltinFunctionFamily::Aggregate => BuiltinFunctionClass::Aggregate,
1948 BuiltinFunctionFamily::Window => BuiltinFunctionClass::Window,
1949 BuiltinFunctionFamily::Scalar => {
1950 if matches!(
1951 name,
1952 "acos"
1953 | "acosh"
1954 | "asin"
1955 | "asinh"
1956 | "atan"
1957 | "atan2"
1958 | "atanh"
1959 | "ceil"
1960 | "ceiling"
1961 | "cos"
1962 | "cosh"
1963 | "degrees"
1964 | "exp"
1965 | "floor"
1966 | "ln"
1967 | "log"
1968 | "log10"
1969 | "log2"
1970 | "mod"
1971 | "pi"
1972 | "pow"
1973 | "power"
1974 | "radians"
1975 | "sin"
1976 | "sinh"
1977 | "sqrt"
1978 | "tan"
1979 | "tanh"
1980 | "trunc"
1981 ) {
1982 BuiltinFunctionClass::MathScalar
1983 } else if matches!(
1984 name,
1985 "date" | "datetime" | "julianday" | "strftime" | "time" | "timediff" | "unixepoch"
1986 ) {
1987 BuiltinFunctionClass::DateTimeScalar
1988 } else {
1989 BuiltinFunctionClass::CoreScalar
1990 }
1991 }
1992 }
1993}
1994
1995fn builtin_function_alias_flag(name: &str, family: BuiltinFunctionFamily) -> bool {
1996 match family {
1997 BuiltinFunctionFamily::Scalar => {
1998 matches!(name, "ceiling" | "if" | "power" | "printf" | "substring")
1999 }
2000 BuiltinFunctionFamily::Aggregate | BuiltinFunctionFamily::Window => name == "string_agg",
2001 }
2002}
2003
2004const fn builtin_function_surface_id(family: BuiltinFunctionFamily) -> &'static str {
2005 match family {
2006 BuiltinFunctionFamily::Window => WINDOW_FUNCTION_SURFACE_ID,
2007 BuiltinFunctionFamily::Scalar | BuiltinFunctionFamily::Aggregate => {
2008 CORE_FUNCTION_SURFACE_ID
2009 }
2010 }
2011}
2012
2013fn canonical_name(name: &str) -> String {
2014 name.trim().to_ascii_uppercase()
2015}
2016
2017#[cfg(test)]
2018mod tests {
2019 use std::collections::BTreeSet;
2020
2021 use fsqlite_types::SqliteValue;
2022
2023 use super::*;
2024
2025 #[test]
2026 fn keyed_registration_preserves_bounds_without_reinvoking_user_metadata() {
2027 struct MetadataPanicsScalar;
2028
2029 impl ScalarFunction for MetadataPanicsScalar {
2030 fn invoke(&self, _args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2031 Ok(SqliteValue::Integer(1))
2032 }
2033
2034 fn num_args(&self) -> i32 {
2035 panic!("keyed scalar registration must not ask for arity")
2036 }
2037
2038 fn is_deterministic(&self) -> bool {
2039 panic!("keyed scalar registration must not ask for determinism")
2040 }
2041
2042 fn consumes_argument_collation(&self) -> bool {
2043 panic!("keyed scalar registration must not ask for collation metadata")
2044 }
2045
2046 fn name(&self) -> &str {
2047 panic!("keyed scalar registration must not ask for name")
2048 }
2049 }
2050
2051 struct MetadataPanicsAggregate;
2052
2053 impl AggregateFunction for MetadataPanicsAggregate {
2054 type State = ();
2055
2056 fn initial_state(&self) -> Self::State {}
2057
2058 fn step(
2059 &self,
2060 _state: &mut Self::State,
2061 _args: &[SqliteValue],
2062 ) -> fsqlite_error::Result<()> {
2063 Ok(())
2064 }
2065
2066 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2067 Ok(SqliteValue::Integer(1))
2068 }
2069
2070 fn num_args(&self) -> i32 {
2071 panic!("keyed aggregate registration must not ask for arity")
2072 }
2073
2074 fn name(&self) -> &str {
2075 panic!("keyed aggregate registration must not ask for name")
2076 }
2077 }
2078
2079 struct MetadataPanicsWindow;
2080
2081 impl WindowFunction for MetadataPanicsWindow {
2082 type State = ();
2083
2084 fn initial_state(&self) -> Self::State {}
2085
2086 fn step(
2087 &self,
2088 _state: &mut Self::State,
2089 _args: &[SqliteValue],
2090 ) -> fsqlite_error::Result<()> {
2091 Ok(())
2092 }
2093
2094 fn inverse(
2095 &self,
2096 _state: &mut Self::State,
2097 _args: &[SqliteValue],
2098 ) -> fsqlite_error::Result<()> {
2099 Ok(())
2100 }
2101
2102 fn value(&self, _state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2103 Ok(SqliteValue::Integer(1))
2104 }
2105
2106 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2107 Ok(SqliteValue::Integer(1))
2108 }
2109
2110 fn num_args(&self) -> i32 {
2111 panic!("keyed window registration must not ask for arity")
2112 }
2113
2114 fn name(&self) -> &str {
2115 panic!("keyed window registration must not ask for name")
2116 }
2117 }
2118
2119 let arity = FunctionArity::variadic(1, Some(2));
2120 let scalar_key = FunctionKey::new("keyed_scalar", -1);
2121 let aggregate_key = FunctionKey::new("keyed_aggregate", -1);
2122 let window_key = FunctionKey::new("keyed_window", -1);
2123 let mut registry = FunctionRegistry::new();
2124 let scalar_displaced = registry.register_scalar_keyed(
2125 scalar_key.clone(),
2126 arity,
2127 false,
2128 false,
2129 MetadataPanicsScalar,
2130 );
2131 assert!(scalar_displaced.is_none());
2132 assert!(registry.find_scalar("keyed_scalar", 1).is_some());
2133
2134 let aggregate_displaced = registry.register_aggregate_keyed(
2135 aggregate_key.clone(),
2136 arity,
2137 MetadataPanicsAggregate,
2138 );
2139 assert!(aggregate_displaced.is_none());
2140 assert!(registry.find_aggregate("keyed_aggregate", 1).is_some());
2141
2142 let (window, window_displaced) =
2143 registry.register_window_keyed(window_key.clone(), arity, MetadataPanicsWindow);
2144 assert!(window_displaced.is_none());
2145 assert!(Arc::ptr_eq(
2146 &window,
2147 ®istry.find_window("keyed_window", 1).unwrap()
2148 ));
2149
2150 for num_args in [1, 2] {
2151 assert_eq!(
2152 registry.scalar_accepts_arg_count("keyed_scalar", num_args),
2153 Some(true)
2154 );
2155 assert_eq!(
2156 registry.aggregate_accepts_arg_count("keyed_aggregate", num_args),
2157 Some(true)
2158 );
2159 assert_eq!(
2160 registry.window_accepts_arg_count("keyed_window", num_args),
2161 Some(true)
2162 );
2163 assert_eq!(
2164 registry.scalar_is_deterministic("keyed_scalar", num_args),
2165 Some(false)
2166 );
2167 assert_eq!(
2168 registry
2169 .resolve_scalar("keyed_scalar", num_args)
2170 .unwrap()
2171 .query_constancy(),
2172 ScalarQueryConstancy::Volatile
2173 );
2174 }
2175 for num_args in [0, 3] {
2176 assert_eq!(
2177 registry.scalar_accepts_arg_count("keyed_scalar", num_args),
2178 Some(false)
2179 );
2180 assert_eq!(
2181 registry.aggregate_accepts_arg_count("keyed_aggregate", num_args),
2182 Some(false)
2183 );
2184 assert_eq!(
2185 registry.window_accepts_arg_count("keyed_window", num_args),
2186 Some(false)
2187 );
2188 }
2189
2190 registry.scalar_arities.remove(&scalar_key);
2191 registry.aggregate_arities.remove(&aggregate_key);
2192 registry.window_arities.remove(&window_key);
2193 assert_eq!(
2194 registry.scalar_accepts_arg_count("keyed_scalar", 1),
2195 Some(false)
2196 );
2197 assert_eq!(
2198 registry.scalar_is_deterministic("keyed_scalar", 1),
2199 Some(false)
2200 );
2201 assert_eq!(
2202 registry.aggregate_accepts_arg_count("keyed_aggregate", 1),
2203 Some(false)
2204 );
2205 assert_eq!(
2206 registry.window_accepts_arg_count("keyed_window", 1),
2207 Some(false)
2208 );
2209 assert_wrong_arg_count(
2210 registry.find_scalar("keyed_scalar", 1).unwrap().as_ref(),
2211 &[SqliteValue::Null],
2212 "keyed_scalar",
2213 );
2214 assert_wrong_arg_count_aggregate(
2215 registry
2216 .find_aggregate("keyed_aggregate", 1)
2217 .unwrap()
2218 .as_ref(),
2219 &[SqliteValue::Null],
2220 "keyed_aggregate",
2221 );
2222 assert_wrong_arg_count_window(
2223 registry.find_window("keyed_window", 1).unwrap().as_ref(),
2224 &[SqliteValue::Null],
2225 "keyed_window",
2226 );
2227 }
2228
2229 #[test]
2230 fn exact_registration_fails_closed_when_parallel_arity_metadata_is_missing() {
2231 let scalar_key = FunctionKey::new("double", 1);
2232 let aggregate_key = FunctionKey::new("product", 1);
2233 let window_key = FunctionKey::new("moving_sum", 1);
2234 let mut registry = FunctionRegistry::new();
2235 registry.register_scalar(Double);
2236 registry.register_aggregate(Product);
2237 registry.register_window(MovingSum);
2238
2239 assert_eq!(
2240 registry.scalar_arities.remove(&scalar_key),
2241 Some(FunctionArity::exact(1))
2242 );
2243 assert_eq!(
2244 registry.aggregate_arities.remove(&aggregate_key),
2245 Some(FunctionArity::exact(1))
2246 );
2247 assert_eq!(
2248 registry.window_arities.remove(&window_key),
2249 Some(FunctionArity::exact(1))
2250 );
2251
2252 assert_eq!(registry.scalar_accepts_arg_count("double", 1), Some(false));
2253 assert_eq!(
2254 registry.aggregate_accepts_arg_count("product", 1),
2255 Some(false)
2256 );
2257 assert_eq!(
2258 registry.window_accepts_arg_count("moving_sum", 1),
2259 Some(false)
2260 );
2261 assert_wrong_arg_count(
2262 registry.find_scalar("double", 1).unwrap().as_ref(),
2263 &[SqliteValue::Null],
2264 "double",
2265 );
2266 assert_wrong_arg_count_aggregate(
2267 registry.find_aggregate("product", 1).unwrap().as_ref(),
2268 &[SqliteValue::Null],
2269 "product",
2270 );
2271 assert_wrong_arg_count_window(
2272 registry.find_window("moving_sum", 1).unwrap().as_ref(),
2273 &[SqliteValue::Null],
2274 "moving_sum",
2275 );
2276 }
2277
2278 #[test]
2279 #[should_panic(
2280 expected = "function key and frozen arity contract must have the same declared argument count"
2281 )]
2282 fn keyed_registration_rejects_mismatched_arity_contract() {
2283 assert_key_matches_arity(&FunctionKey::new("mismatched", -1), FunctionArity::exact(1));
2284 }
2285
2286 #[test]
2287 #[should_panic(expected = "function argument count must be -1 or non-negative")]
2288 fn function_key_rejects_argument_counts_below_variadic_sentinel() {
2289 let _ = FunctionKey::new("invalid", -2);
2290 }
2291
2292 #[test]
2293 #[should_panic(expected = "function argument count must be -1 or non-negative")]
2294 fn default_arity_contract_rejects_argument_counts_below_variadic_sentinel() {
2295 let _ = FunctionArity::from_declared_args(-2, || {
2296 panic!("invalid declared arity must be rejected before reading variadic bounds")
2297 });
2298 }
2299
2300 fn runtime_registry_surface_keys() -> BTreeSet<(BuiltinFunctionFamily, String, i32)> {
2301 let mut registry = FunctionRegistry::new();
2302 register_builtins(&mut registry);
2303 register_window_builtins(&mut registry);
2304
2305 let scalar_keys = registry
2306 .scalars
2307 .keys()
2308 .map(|key| {
2309 (
2310 BuiltinFunctionFamily::Scalar,
2311 key.name.to_ascii_lowercase(),
2312 key.num_args,
2313 )
2314 })
2315 .collect::<BTreeSet<_>>();
2316 let aggregate_keys = registry
2317 .aggregates
2318 .keys()
2319 .map(|key| {
2320 (
2321 BuiltinFunctionFamily::Aggregate,
2322 key.name.to_ascii_lowercase(),
2323 key.num_args,
2324 )
2325 })
2326 .collect::<BTreeSet<_>>();
2327 let window_keys = registry
2328 .windows
2329 .keys()
2330 .map(|key| {
2331 (
2332 BuiltinFunctionFamily::Window,
2333 key.name.to_ascii_lowercase(),
2334 key.num_args,
2335 )
2336 })
2337 .collect::<BTreeSet<_>>();
2338
2339 scalar_keys
2340 .into_iter()
2341 .chain(aggregate_keys)
2342 .chain(window_keys)
2343 .collect()
2344 }
2345
2346 fn inventory_surface_keys() -> BTreeSet<(BuiltinFunctionFamily, String, i32)> {
2347 builtin_function_surface_inventory()
2348 .iter()
2349 .map(|entry| (entry.family, entry.name.clone(), entry.num_args))
2350 .collect()
2351 }
2352
2353 fn find_surface_entry(
2354 family: BuiltinFunctionFamily,
2355 name: &str,
2356 num_args: i32,
2357 ) -> &'static BuiltinFunctionSurfaceEntry {
2358 builtin_function_surface_inventory()
2359 .iter()
2360 .find(|entry| {
2361 entry.family == family && entry.name == name && entry.num_args == num_args
2362 })
2363 .unwrap_or_else(|| {
2364 unreachable!(
2365 "missing builtin surface entry: family={} name={} arity={}",
2366 family.label(),
2367 name,
2368 num_args
2369 )
2370 })
2371 }
2372
2373 #[test]
2374 fn test_builtin_function_surface_inventory_matches_live_registry() {
2375 let inventory = builtin_function_surface_inventory();
2376 let inventory_keys = inventory_surface_keys();
2377 let runtime_keys = runtime_registry_surface_keys();
2378
2379 assert_eq!(
2380 inventory.len(),
2381 inventory_keys.len(),
2382 "inventory must not contain duplicate family/name/arity tuples"
2383 );
2384 assert_eq!(
2385 inventory_keys, runtime_keys,
2386 "inventory must exactly match the live registration path"
2387 );
2388 assert!(
2389 inventory.windows(2).all(|entries| {
2390 (
2391 entries[0].family,
2392 entries[0].class,
2393 &entries[0].name,
2394 entries[0].num_args,
2395 ) <= (
2396 entries[1].family,
2397 entries[1].class,
2398 &entries[1].name,
2399 entries[1].num_args,
2400 )
2401 }),
2402 "inventory must stay deterministically sorted"
2403 );
2404 }
2405
2406 #[test]
2407 fn test_builtin_function_surface_inventory_classifies_representative_entries() {
2408 let abs = find_surface_entry(BuiltinFunctionFamily::Scalar, "abs", 1);
2409 assert_eq!(abs.class, BuiltinFunctionClass::CoreScalar);
2410 assert!(!abs.is_alias);
2411 assert_eq!(abs.surface_id, CORE_FUNCTION_SURFACE_ID);
2412
2413 let date = find_surface_entry(BuiltinFunctionFamily::Scalar, "date", -1);
2414 assert_eq!(date.class, BuiltinFunctionClass::DateTimeScalar);
2415 assert!(!date.is_alias);
2416 assert_eq!(date.surface_id, CORE_FUNCTION_SURFACE_ID);
2417
2418 let power = find_surface_entry(BuiltinFunctionFamily::Scalar, "power", 2);
2419 assert_eq!(power.class, BuiltinFunctionClass::MathScalar);
2420 assert!(power.is_alias);
2421 assert_eq!(power.surface_id, CORE_FUNCTION_SURFACE_ID);
2422
2423 let count = find_surface_entry(BuiltinFunctionFamily::Aggregate, "count", 0);
2424 assert_eq!(count.class, BuiltinFunctionClass::Aggregate);
2425 assert!(!count.is_alias);
2426 assert_eq!(count.surface_id, CORE_FUNCTION_SURFACE_ID);
2427
2428 let row_number = find_surface_entry(BuiltinFunctionFamily::Window, "row_number", 0);
2429 assert_eq!(row_number.class, BuiltinFunctionClass::Window);
2430 assert!(!row_number.is_alias);
2431 assert_eq!(row_number.surface_id, WINDOW_FUNCTION_SURFACE_ID);
2432
2433 let string_agg_window = find_surface_entry(BuiltinFunctionFamily::Window, "string_agg", 2);
2434 assert_eq!(string_agg_window.class, BuiltinFunctionClass::Window);
2435 assert!(string_agg_window.is_alias);
2436 assert_eq!(string_agg_window.surface_id, WINDOW_FUNCTION_SURFACE_ID);
2437 }
2438
2439 struct Double;
2442
2443 impl ScalarFunction for Double {
2444 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2445 Ok(SqliteValue::Integer(args[0].to_integer() * 2))
2446 }
2447
2448 fn num_args(&self) -> i32 {
2449 1
2450 }
2451
2452 fn name(&self) -> &str {
2453 "double"
2454 }
2455 }
2456
2457 struct VariadicConcat;
2460
2461 impl ScalarFunction for VariadicConcat {
2462 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2463 let mut out = String::new();
2464 for a in args {
2465 out.push_str(&a.to_text());
2466 }
2467 Ok(SqliteValue::Text(out.into()))
2468 }
2469
2470 fn num_args(&self) -> i32 {
2471 -1
2472 }
2473
2474 fn min_args(&self) -> i32 {
2475 1
2476 }
2477
2478 fn max_args(&self) -> Option<i32> {
2479 Some(3)
2480 }
2481
2482 fn name(&self) -> &str {
2483 "my_func"
2484 }
2485 }
2486
2487 struct TwoArgFunc;
2490
2491 impl ScalarFunction for TwoArgFunc {
2492 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2493 Ok(SqliteValue::Integer(
2494 args[0].to_integer() + args[1].to_integer(),
2495 ))
2496 }
2497
2498 fn num_args(&self) -> i32 {
2499 2
2500 }
2501
2502 fn name(&self) -> &str {
2503 "my_func"
2504 }
2505 }
2506
2507 fn assert_wrong_arg_count(
2508 function: &dyn ScalarFunction,
2509 args: &[SqliteValue],
2510 expected_name: &str,
2511 ) {
2512 let err = function.invoke(args).expect_err("wrong arity should fail");
2513 let expected = format!("wrong number of arguments to function {expected_name}()");
2514 assert!(
2515 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2516 "expected {expected:?}, got {err:?}"
2517 );
2518 }
2519
2520 fn assert_wrong_arg_count_aggregate(
2521 function: &ErasedAggregateFunction,
2522 args: &[SqliteValue],
2523 expected_name: &str,
2524 ) {
2525 let mut state = function.initial_state();
2526 let err = function
2527 .step(&mut state, args)
2528 .expect_err("wrong aggregate arity should fail");
2529 let expected = format!("wrong number of arguments to function {expected_name}()");
2530 assert!(
2531 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2532 "expected {expected:?}, got {err:?}"
2533 );
2534 }
2535
2536 fn assert_wrong_arg_count_window(
2537 function: &ErasedWindowFunction,
2538 args: &[SqliteValue],
2539 expected_name: &str,
2540 ) {
2541 let mut state = function.initial_state();
2542 let err = function
2543 .step(&mut state, args)
2544 .expect_err("wrong window arity should fail");
2545 let expected = format!("wrong number of arguments to function {expected_name}()");
2546 assert!(
2547 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2548 "expected {expected:?}, got {err:?}"
2549 );
2550 }
2551
2552 struct Product;
2553
2554 impl AggregateFunction for Product {
2555 type State = i64;
2556
2557 fn initial_state(&self) -> Self::State {
2558 1
2559 }
2560
2561 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2562 *state *= args[0].to_integer();
2563 Ok(())
2564 }
2565
2566 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2567 Ok(SqliteValue::Integer(state))
2568 }
2569
2570 fn num_args(&self) -> i32 {
2571 1
2572 }
2573
2574 fn name(&self) -> &str {
2575 "product"
2576 }
2577 }
2578
2579 struct MovingSum;
2580
2581 impl WindowFunction for MovingSum {
2582 type State = i64;
2583
2584 fn initial_state(&self) -> Self::State {
2585 0
2586 }
2587
2588 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2589 *state += args[0].to_integer();
2590 Ok(())
2591 }
2592
2593 fn inverse(
2594 &self,
2595 state: &mut Self::State,
2596 args: &[SqliteValue],
2597 ) -> fsqlite_error::Result<()> {
2598 *state -= args[0].to_integer();
2599 Ok(())
2600 }
2601
2602 fn value(&self, state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2603 Ok(SqliteValue::Integer(*state))
2604 }
2605
2606 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2607 Ok(SqliteValue::Integer(state))
2608 }
2609
2610 fn num_args(&self) -> i32 {
2611 1
2612 }
2613
2614 fn name(&self) -> &str {
2615 "moving_sum"
2616 }
2617 }
2618
2619 struct TaggedScalar {
2620 name: &'static str,
2621 num_args: i32,
2622 tag: i64,
2623 }
2624
2625 impl ScalarFunction for TaggedScalar {
2626 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2627 Ok(SqliteValue::Integer(
2628 self.tag + args.iter().map(SqliteValue::to_integer).sum::<i64>(),
2629 ))
2630 }
2631
2632 fn num_args(&self) -> i32 {
2633 self.num_args
2634 }
2635
2636 fn name(&self) -> &str {
2637 self.name
2638 }
2639 }
2640
2641 struct TaggedAggregate {
2642 name: &'static str,
2643 num_args: i32,
2644 tag: i64,
2645 }
2646
2647 impl AggregateFunction for TaggedAggregate {
2648 type State = i64;
2649
2650 fn initial_state(&self) -> Self::State {
2651 0
2652 }
2653
2654 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2655 *state += args.iter().map(SqliteValue::to_integer).sum::<i64>();
2656 Ok(())
2657 }
2658
2659 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2660 Ok(SqliteValue::Integer(self.tag + state))
2661 }
2662
2663 fn num_args(&self) -> i32 {
2664 self.num_args
2665 }
2666
2667 fn name(&self) -> &str {
2668 self.name
2669 }
2670 }
2671
2672 struct TaggedWindow {
2673 name: &'static str,
2674 num_args: i32,
2675 tag: i64,
2676 }
2677
2678 impl WindowFunction for TaggedWindow {
2679 type State = i64;
2680
2681 fn initial_state(&self) -> Self::State {
2682 0
2683 }
2684
2685 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2686 *state += args.iter().map(SqliteValue::to_integer).sum::<i64>();
2687 Ok(())
2688 }
2689
2690 fn inverse(
2691 &self,
2692 state: &mut Self::State,
2693 args: &[SqliteValue],
2694 ) -> fsqlite_error::Result<()> {
2695 *state -= args.iter().map(SqliteValue::to_integer).sum::<i64>();
2696 Ok(())
2697 }
2698
2699 fn value(&self, state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2700 Ok(SqliteValue::Integer(self.tag + state))
2701 }
2702
2703 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2704 Ok(SqliteValue::Integer(self.tag + state))
2705 }
2706
2707 fn num_args(&self) -> i32 {
2708 self.num_args
2709 }
2710
2711 fn name(&self) -> &str {
2712 self.name
2713 }
2714 }
2715
2716 fn finalize_aggregate(
2717 function: &ErasedAggregateFunction,
2718 rows: &[&[SqliteValue]],
2719 ) -> SqliteValue {
2720 let mut state = function.initial_state();
2721 for args in rows {
2722 function.step(&mut state, args).unwrap();
2723 }
2724 function.finalize(state).unwrap()
2725 }
2726
2727 #[test]
2728 fn application_resolution_prefers_exact_across_function_kinds() {
2729 let mut registry = FunctionRegistry::new();
2730 registry.register_application_aggregate_captured(
2731 "app_precedence",
2732 FunctionArity::variadic(0, None),
2733 TaggedAggregate {
2734 name: "app_precedence",
2735 num_args: -1,
2736 tag: 20_000,
2737 },
2738 );
2739 registry.register_application_scalar_captured(
2740 "app_precedence",
2741 FunctionArity::exact(1),
2742 true,
2743 false,
2744 TaggedScalar {
2745 name: "app_precedence",
2746 num_args: 1,
2747 tag: 10_000,
2748 },
2749 );
2750
2751 assert_eq!(
2752 registry
2753 .resolve_application_function("APP_PRECEDENCE", 1)
2754 .map(ApplicationFunctionResolution::kind),
2755 Some(ApplicationFunctionKind::Scalar)
2756 );
2757 assert_eq!(
2758 registry
2759 .find_scalar("app_precedence", 1)
2760 .unwrap()
2761 .invoke(&[SqliteValue::Integer(7)])
2762 .unwrap(),
2763 SqliteValue::Integer(10_007)
2764 );
2765 assert!(registry.find_aggregate("app_precedence", 1).is_none());
2766
2767 assert_eq!(
2768 registry
2769 .resolve_application_function("app_precedence", 2)
2770 .map(ApplicationFunctionResolution::kind),
2771 Some(ApplicationFunctionKind::Aggregate)
2772 );
2773 assert!(registry.find_scalar("app_precedence", 2).is_none());
2774 let args = [SqliteValue::Integer(2), SqliteValue::Integer(3)];
2775 assert_eq!(
2776 finalize_aggregate(
2777 registry
2778 .find_aggregate("app_precedence", 2)
2779 .unwrap()
2780 .as_ref(),
2781 &[&args],
2782 ),
2783 SqliteValue::Integer(20_005)
2784 );
2785
2786 let mut reverse = FunctionRegistry::new();
2787 reverse.register_application_scalar_captured(
2788 "app_precedence",
2789 FunctionArity::variadic(0, None),
2790 true,
2791 false,
2792 TaggedScalar {
2793 name: "app_precedence",
2794 num_args: -1,
2795 tag: 30_000,
2796 },
2797 );
2798 reverse.register_application_aggregate_captured(
2799 "app_precedence",
2800 FunctionArity::exact(1),
2801 TaggedAggregate {
2802 name: "app_precedence",
2803 num_args: 1,
2804 tag: 40_000,
2805 },
2806 );
2807 assert_eq!(
2808 reverse
2809 .resolve_application_function("app_precedence", 1)
2810 .map(ApplicationFunctionResolution::kind),
2811 Some(ApplicationFunctionKind::Aggregate)
2812 );
2813 assert!(reverse.find_scalar("app_precedence", 1).is_none());
2814 assert_eq!(
2815 reverse
2816 .resolve_application_function("app_precedence", 2)
2817 .map(ApplicationFunctionResolution::kind),
2818 Some(ApplicationFunctionKind::Scalar)
2819 );
2820 }
2821
2822 #[test]
2823 fn application_variadic_shadows_builtin_exact_only_when_compatible() {
2824 let mut registry = FunctionRegistry::new();
2825 registry.register_scalar(TaggedScalar {
2826 name: "layered",
2827 num_args: 1,
2828 tag: 1_000,
2829 });
2830 registry.register_application_scalar_captured(
2831 "layered",
2832 FunctionArity::variadic(2, Some(3)),
2833 true,
2834 false,
2835 TaggedScalar {
2836 name: "layered",
2837 num_args: -1,
2838 tag: 2_000,
2839 },
2840 );
2841
2842 assert_eq!(registry.resolve_application_function("layered", 1), None);
2843 assert_eq!(
2844 registry
2845 .find_scalar("layered", 1)
2846 .unwrap()
2847 .invoke(&[SqliteValue::Integer(7)])
2848 .unwrap(),
2849 SqliteValue::Integer(1_007),
2850 "an incompatible application variadic must leave the builtin layer visible"
2851 );
2852 let two_args = [SqliteValue::Integer(7), SqliteValue::Integer(8)];
2853 assert_eq!(
2854 registry
2855 .find_scalar("layered", 2)
2856 .unwrap()
2857 .invoke(&two_args)
2858 .unwrap(),
2859 SqliteValue::Integer(2_015)
2860 );
2861
2862 let displaced = registry.register_application_scalar_captured(
2863 "layered",
2864 FunctionArity::variadic(0, None),
2865 true,
2866 false,
2867 TaggedScalar {
2868 name: "layered",
2869 num_args: -1,
2870 tag: 3_000,
2871 },
2872 );
2873 assert!(displaced.is_some());
2874 assert_eq!(
2875 registry
2876 .find_scalar("layered", 1)
2877 .unwrap()
2878 .invoke(&[SqliteValue::Integer(7)])
2879 .unwrap(),
2880 SqliteValue::Integer(3_007),
2881 "a compatible application variadic must shadow an exact builtin"
2882 );
2883
2884 registry.register_aggregate(TaggedAggregate {
2885 name: "sum_like",
2886 num_args: 1,
2887 tag: 4_000,
2888 });
2889 registry.register_application_scalar_captured(
2890 "sum_like",
2891 FunctionArity::variadic(0, None),
2892 true,
2893 false,
2894 TaggedScalar {
2895 name: "sum_like",
2896 num_args: -1,
2897 tag: 5_000,
2898 },
2899 );
2900 assert!(registry.find_aggregate("sum_like", 1).is_none());
2901 assert!(registry.find_scalar("sum_like", 1).is_some());
2902
2903 let bounded_window_arity = FunctionArity::variadic(1, Some(2));
2904 registry.register_application_window_captured(
2905 "bounded_window",
2906 bounded_window_arity,
2907 TaggedWindow {
2908 name: "bounded_window",
2909 num_args: -1,
2910 tag: 6_000,
2911 },
2912 );
2913 assert_eq!(
2914 registry
2915 .find_aggregate("bounded_window", 2)
2916 .unwrap()
2917 .arity(),
2918 bounded_window_arity,
2919 "the aggregate bridge must preserve frozen variadic bounds"
2920 );
2921 assert_eq!(
2922 registry.aggregate_accepts_arg_count("bounded_window", 0),
2923 Some(false)
2924 );
2925 assert_eq!(
2926 registry.window_accepts_arg_count("bounded_window", 3),
2927 Some(false)
2928 );
2929 }
2930
2931 #[test]
2932 fn same_application_key_replaces_kind_and_window_retains_aggregate_form() {
2933 let mut registry = FunctionRegistry::new();
2934 let mut displaced = Vec::new();
2935 assert!(
2936 registry
2937 .register_application_scalar_captured(
2938 "cross_kind",
2939 FunctionArity::exact(1),
2940 true,
2941 false,
2942 TaggedScalar {
2943 name: "cross_kind",
2944 num_args: 1,
2945 tag: 70_000,
2946 },
2947 )
2948 .is_none()
2949 );
2950
2951 displaced.push(
2952 registry
2953 .register_application_aggregate_captured(
2954 "cross_kind",
2955 FunctionArity::exact(1),
2956 TaggedAggregate {
2957 name: "cross_kind",
2958 num_args: 1,
2959 tag: 80_000,
2960 },
2961 )
2962 .expect("aggregate must displace same-key scalar"),
2963 );
2964 assert_eq!(
2965 registry
2966 .resolve_application_function("cross_kind", 1)
2967 .map(ApplicationFunctionResolution::kind),
2968 Some(ApplicationFunctionKind::Aggregate)
2969 );
2970 assert!(registry.find_scalar("cross_kind", 1).is_none());
2971 assert!(registry.find_window("cross_kind", 1).is_none());
2972
2973 let (registered_window, old_aggregate) = registry.register_application_window_captured(
2974 "cross_kind",
2975 FunctionArity::exact(1),
2976 TaggedWindow {
2977 name: "cross_kind",
2978 num_args: 1,
2979 tag: 90_000,
2980 },
2981 );
2982 displaced.push(old_aggregate.expect("window must displace same-key aggregate"));
2983 assert_eq!(
2984 registry
2985 .resolve_application_function("cross_kind", 1)
2986 .map(ApplicationFunctionResolution::kind),
2987 Some(ApplicationFunctionKind::Window)
2988 );
2989 assert!(Arc::ptr_eq(
2990 ®istered_window,
2991 ®istry.find_window("cross_kind", 1).unwrap()
2992 ));
2993 let row_one = [SqliteValue::Integer(1)];
2994 let row_two = [SqliteValue::Integer(2)];
2995 let row_three = [SqliteValue::Integer(3)];
2996 assert_eq!(
2997 finalize_aggregate(
2998 registry.find_aggregate("cross_kind", 1).unwrap().as_ref(),
2999 &[&row_one, &row_two, &row_three],
3000 ),
3001 SqliteValue::Integer(90_006),
3002 "window registration must keep its ordinary aggregate form"
3003 );
3004
3005 displaced.push(
3006 registry
3007 .register_application_scalar_captured(
3008 "cross_kind",
3009 FunctionArity::exact(1),
3010 true,
3011 false,
3012 TaggedScalar {
3013 name: "cross_kind",
3014 num_args: 1,
3015 tag: 100_000,
3016 },
3017 )
3018 .expect("scalar must displace same-key window"),
3019 );
3020 assert_eq!(
3021 registry
3022 .resolve_application_function("cross_kind", 1)
3023 .map(ApplicationFunctionResolution::kind),
3024 Some(ApplicationFunctionKind::Scalar)
3025 );
3026 assert_eq!(registry.aggregate_accepts_arg_count("cross_kind", 1), None);
3027 assert_eq!(registry.window_accepts_arg_count("cross_kind", 1), None);
3028 assert!(registry.find_aggregate("cross_kind", 1).is_none());
3029 assert!(registry.find_window("cross_kind", 1).is_none());
3030 assert_eq!(displaced.len(), 3);
3031 }
3032
3033 #[test]
3034 fn test_registry_register_scalar() {
3035 let mut registry = FunctionRegistry::new();
3036 let previous = registry.register_scalar(Double);
3037 assert!(previous.is_none());
3038 assert!(registry.contains_scalar("double"));
3039 assert!(registry.contains_scalar("DOUBLE"));
3040 let f = registry
3041 .find_scalar(" Double ", 1)
3042 .expect("double registered");
3043 assert_eq!(
3044 f.invoke(&[SqliteValue::Integer(21)])
3045 .expect("invoke succeeds"),
3046 SqliteValue::Integer(42)
3047 );
3048 }
3049
3050 #[test]
3051 fn test_registry_case_insensitive_lookup() {
3052 let mut registry = FunctionRegistry::new();
3053 registry.register_scalar(Double);
3054
3055 assert!(registry.find_scalar("DOUBLE", 1).is_some());
3057 assert!(registry.find_scalar("Double", 1).is_some());
3058 assert!(registry.find_scalar(" double ", 1).is_some());
3059 }
3060
3061 #[test]
3062 fn test_registry_overwrite() {
3063 let mut registry = FunctionRegistry::new();
3064
3065 let prev = registry.register_scalar(Double);
3067 assert!(prev.is_none());
3068
3069 let prev = registry.register_scalar(Double);
3071 assert!(prev.is_some());
3072
3073 let f = registry.find_scalar("double", 1).unwrap();
3075 assert_eq!(
3076 f.invoke(&[SqliteValue::Integer(5)]).unwrap(),
3077 SqliteValue::Integer(10)
3078 );
3079 }
3080
3081 #[test]
3082 fn test_registry_variadic_fallback() {
3083 let mut registry = FunctionRegistry::new();
3084
3085 registry.register_scalar(VariadicConcat);
3087
3088 let too_few = registry
3089 .find_scalar("my_func", 0)
3090 .expect("known function with bad arity returns erroring scalar");
3091 assert_wrong_arg_count(too_few.as_ref(), &[], "my_func");
3092
3093 let f = registry
3095 .find_scalar("my_func", 3)
3096 .expect("variadic fallback");
3097 assert_eq!(
3098 f.invoke(&[
3099 SqliteValue::Text("a".into()),
3100 SqliteValue::Text("b".into()),
3101 SqliteValue::Text("c".into()),
3102 ])
3103 .unwrap(),
3104 SqliteValue::Text("abc".into())
3105 );
3106 let too_many = registry
3107 .find_scalar("my_func", 4)
3108 .expect("known function with bad arity returns erroring scalar");
3109 assert_wrong_arg_count(
3110 too_many.as_ref(),
3111 &[
3112 SqliteValue::Null,
3113 SqliteValue::Null,
3114 SqliteValue::Null,
3115 SqliteValue::Null,
3116 ],
3117 "my_func",
3118 );
3119 }
3120
3121 #[test]
3122 fn test_registry_exact_wrong_arity_returns_function_error() {
3123 let mut registry = FunctionRegistry::new();
3124 registry.register_scalar(Double);
3125
3126 let f = registry
3127 .find_scalar("double", 2)
3128 .expect("known function with wrong arity returns erroring scalar");
3129 assert_wrong_arg_count(
3130 f.as_ref(),
3131 &[SqliteValue::Integer(1), SqliteValue::Integer(2)],
3132 "double",
3133 );
3134 }
3135
3136 #[test]
3137 fn test_registry_exact_match_over_variadic() {
3138 let mut registry = FunctionRegistry::new();
3139
3140 registry.register_scalar(VariadicConcat);
3142 registry.register_scalar(TwoArgFunc);
3143
3144 let f = registry
3146 .find_scalar("my_func", 2)
3147 .expect("exact match found");
3148 assert_eq!(
3149 f.invoke(&[SqliteValue::Integer(10), SqliteValue::Integer(32)])
3150 .unwrap(),
3151 SqliteValue::Integer(42)
3152 );
3153
3154 let f = registry
3156 .find_scalar("my_func", 3)
3157 .expect("variadic fallback");
3158 assert_eq!(f.num_args(), -1);
3159 }
3160
3161 #[test]
3162 fn test_registry_not_found_returns_none() {
3163 let registry = FunctionRegistry::new();
3164 assert!(registry.find_scalar("nonexistent", 1).is_none());
3165 assert!(registry.find_aggregate("nonexistent", 1).is_none());
3166 assert!(registry.find_window("nonexistent", 1).is_none());
3167 }
3168
3169 #[test]
3170 fn test_registry_scalar_aggregate_arity_introspection_is_side_effect_free() {
3171 let mut registry = FunctionRegistry::new();
3172 registry.register_scalar(Double);
3173 registry.register_scalar(VariadicConcat);
3174 registry.register_aggregate(Product);
3175
3176 assert_eq!(registry.scalar_accepts_arg_count("double", 1), Some(true));
3177 assert_eq!(registry.scalar_accepts_arg_count("double", 2), Some(false));
3178 assert_eq!(registry.scalar_accepts_arg_count("my_func", 1), Some(true));
3179 assert_eq!(registry.scalar_accepts_arg_count("my_func", 3), Some(true));
3180 assert_eq!(registry.scalar_accepts_arg_count("my_func", 0), Some(false));
3181 assert_eq!(registry.scalar_accepts_arg_count("my_func", 4), Some(false));
3182 assert_eq!(registry.scalar_accepts_arg_count("missing_scalar", 1), None);
3183 assert_eq!(registry.scalar_is_deterministic("double", 1), Some(true));
3184 assert_eq!(registry.scalar_is_deterministic("my_func", 1), Some(true));
3185 assert_eq!(registry.scalar_is_deterministic("my_func", 0), None);
3186 assert_eq!(registry.scalar_is_deterministic("missing_scalar", 1), None);
3187
3188 assert_eq!(
3189 registry.aggregate_accepts_arg_count("product", 1),
3190 Some(true)
3191 );
3192 assert_eq!(
3193 registry.aggregate_accepts_arg_count("product", 0),
3194 Some(false)
3195 );
3196 assert_eq!(
3197 registry.aggregate_accepts_arg_count("missing_aggregate", 1),
3198 None
3199 );
3200
3201 registry
3202 .scalar_schema_safety
3203 .remove(&FunctionKey::new("double", 1));
3204 assert_eq!(
3205 registry.scalar_is_deterministic("double", 1),
3206 Some(false),
3207 "missing immutable metadata must fail closed"
3208 );
3209 }
3210
3211 #[test]
3212 fn scalar_query_constancy_is_frozen_cloned_and_fails_closed() {
3213 let mut registry = FunctionRegistry::new();
3214 registry.register_scalar(Double);
3215 registry.register_scalar_captured(
3216 "volatile_scalar",
3217 FunctionArity::exact(1),
3218 false,
3219 false,
3220 TaggedScalar {
3221 name: "ignored_by_captured_registration",
3222 num_args: 1,
3223 tag: 1,
3224 },
3225 );
3226 registry.register_conditionally_deterministic_scalar(TaggedScalar {
3227 name: "conditional_scalar",
3228 num_args: 1,
3229 tag: 2,
3230 });
3231 registry.register_slow_changing_scalar(TaggedScalar {
3232 name: "slow_scalar",
3233 num_args: 1,
3234 tag: 3,
3235 });
3236
3237 let resolved = registry.resolve_scalar("double", 1).unwrap();
3238 assert_eq!(resolved.query_constancy(), ScalarQueryConstancy::Constant);
3239 assert!(resolved.query_constancy().is_query_constant());
3240
3241 let resolved = registry.resolve_scalar("volatile_scalar", 1).unwrap();
3242 assert_eq!(resolved.query_constancy(), ScalarQueryConstancy::Volatile);
3243 assert!(!resolved.query_constancy().is_query_constant());
3244
3245 let conditional = registry.resolve_scalar("conditional_scalar", 1).unwrap();
3246 assert_eq!(
3247 conditional.schema_safety(),
3248 ScalarSchemaSafety::DateTimeConditional
3249 );
3250 assert_eq!(
3251 conditional.query_constancy(),
3252 ScalarQueryConstancy::SlowChanging
3253 );
3254
3255 let slow = registry.resolve_scalar("slow_scalar", 1).unwrap();
3256 assert_eq!(slow.schema_safety(), ScalarSchemaSafety::Never);
3257 assert_eq!(slow.query_constancy(), ScalarQueryConstancy::SlowChanging);
3258
3259 let wrong_arity = registry.resolve_scalar("double", 2).unwrap();
3260 assert_eq!(
3261 wrong_arity.query_constancy(),
3262 ScalarQueryConstancy::Volatile,
3263 "wrong-arity sentinels must never be treated as query constants"
3264 );
3265
3266 let mut cloned = FunctionRegistry::clone_from_arc(&Arc::new(registry));
3267 assert_eq!(
3268 cloned
3269 .resolve_scalar("slow_scalar", 1)
3270 .unwrap()
3271 .query_constancy(),
3272 ScalarQueryConstancy::SlowChanging,
3273 "registry snapshots must retain frozen query metadata"
3274 );
3275 cloned
3276 .scalar_query_constancy
3277 .remove(&FunctionKey::new("double", 1));
3278 assert_eq!(
3279 cloned
3280 .resolve_scalar("double", 1)
3281 .unwrap()
3282 .query_constancy(),
3283 ScalarQueryConstancy::Volatile,
3284 "missing immutable query metadata must fail closed"
3285 );
3286 }
3287
3288 #[test]
3289 fn application_shadowing_selects_matching_scalar_query_constancy() {
3290 let mut registry = FunctionRegistry::new();
3291 registry.register_slow_changing_scalar(TaggedScalar {
3292 name: "layered_constancy",
3293 num_args: 1,
3294 tag: 10,
3295 });
3296 registry.register_application_scalar_captured(
3297 "layered_constancy",
3298 FunctionArity::variadic(2, Some(3)),
3299 false,
3300 false,
3301 TaggedScalar {
3302 name: "layered_constancy",
3303 num_args: -1,
3304 tag: 20,
3305 },
3306 );
3307
3308 assert_eq!(
3309 registry
3310 .resolve_scalar("layered_constancy", 1)
3311 .unwrap()
3312 .query_constancy(),
3313 ScalarQueryConstancy::SlowChanging,
3314 "an incompatible application variadic must leave base metadata visible"
3315 );
3316 assert_eq!(
3317 registry
3318 .resolve_scalar("layered_constancy", 2)
3319 .unwrap()
3320 .query_constancy(),
3321 ScalarQueryConstancy::Volatile,
3322 "a matching non-deterministic application scalar must publish volatile metadata"
3323 );
3324
3325 registry.register_application_scalar_captured(
3326 "layered_constancy",
3327 FunctionArity::variadic(0, None),
3328 true,
3329 false,
3330 TaggedScalar {
3331 name: "layered_constancy",
3332 num_args: -1,
3333 tag: 30,
3334 },
3335 );
3336 assert_eq!(
3337 registry
3338 .resolve_scalar("layered_constancy", 1)
3339 .unwrap()
3340 .query_constancy(),
3341 ScalarQueryConstancy::Constant,
3342 "a compatible deterministic application scalar must shadow base metadata"
3343 );
3344
3345 let cloned = FunctionRegistry::clone_from_arc(&Arc::new(registry));
3346 assert_eq!(
3347 cloned
3348 .resolve_scalar("layered_constancy", 1)
3349 .unwrap()
3350 .query_constancy(),
3351 ScalarQueryConstancy::Constant,
3352 "registry snapshots must retain application query metadata"
3353 );
3354
3355 let mut shadowed = cloned;
3356 shadowed.register_application_aggregate_captured(
3357 "layered_constancy",
3358 FunctionArity::exact(1),
3359 TaggedAggregate {
3360 name: "layered_constancy",
3361 num_args: 1,
3362 tag: 40,
3363 },
3364 );
3365 assert!(
3366 shadowed.resolve_scalar("layered_constancy", 1).is_none(),
3367 "a matching application aggregate must shadow scalar metadata across kinds"
3368 );
3369 assert_eq!(
3370 shadowed
3371 .resolve_scalar("layered_constancy", 2)
3372 .unwrap()
3373 .query_constancy(),
3374 ScalarQueryConstancy::Constant,
3375 "an incompatible exact aggregate must leave the application variadic visible"
3376 );
3377 }
3378
3379 #[test]
3380 fn test_registry_register_and_resolve_aggregate() {
3381 let mut registry = FunctionRegistry::new();
3382 let previous = registry.register_aggregate(Product);
3383 assert!(previous.is_none());
3384 assert!(registry.contains_aggregate("product"));
3385 let f = registry
3386 .find_aggregate("PRODUCT", 1)
3387 .expect("product aggregate registered");
3388
3389 let mut state = f.initial_state();
3390 f.step(&mut state, &[SqliteValue::Integer(2)])
3391 .expect("step 1");
3392 f.step(&mut state, &[SqliteValue::Integer(3)])
3393 .expect("step 2");
3394 f.step(&mut state, &[SqliteValue::Integer(7)])
3395 .expect("step 3");
3396
3397 assert_eq!(
3398 f.finalize(state).expect("finalize succeeds"),
3399 SqliteValue::Integer(42)
3400 );
3401 }
3402
3403 #[test]
3404 fn test_registry_aggregate_type_erased() {
3405 let mut registry = FunctionRegistry::new();
3406 registry.register_aggregate(Product);
3407
3408 let f = registry
3410 .find_aggregate("product", 1)
3411 .expect("product found");
3412 let mut state = f.initial_state();
3413 f.step(&mut state, &[SqliteValue::Integer(6)]).unwrap();
3414 f.step(&mut state, &[SqliteValue::Integer(7)]).unwrap();
3415 assert_eq!(f.finalize(state).unwrap(), SqliteValue::Integer(42));
3416 assert_eq!(f.name(), "product");
3417 }
3418
3419 #[test]
3420 fn test_registry_aggregate_wrong_arity_returns_function_error() {
3421 let mut registry = FunctionRegistry::new();
3422 registry.register_aggregate(Product);
3423
3424 let f = registry
3425 .find_aggregate("product", 0)
3426 .expect("known aggregate with wrong arity returns erroring aggregate");
3427 assert_wrong_arg_count_aggregate(f.as_ref(), &[], "product");
3428 }
3429
3430 #[test]
3431 fn test_registry_register_and_resolve_window() {
3432 let mut registry = FunctionRegistry::new();
3433 let previous = registry.register_window(MovingSum);
3434 assert!(previous.is_none());
3435 assert!(registry.contains_window("moving_sum"));
3436 let f = registry
3437 .find_window("MOVING_SUM", 1)
3438 .expect("moving_sum window registered");
3439
3440 let mut state = f.initial_state();
3441 f.step(&mut state, &[SqliteValue::Integer(10)])
3442 .expect("step 1");
3443 f.step(&mut state, &[SqliteValue::Integer(20)])
3444 .expect("step 2");
3445 f.step(&mut state, &[SqliteValue::Integer(30)])
3446 .expect("step 3");
3447 assert_eq!(f.value(&state).expect("value"), SqliteValue::Integer(60));
3448
3449 f.inverse(&mut state, &[SqliteValue::Integer(10)])
3450 .expect("inverse 1");
3451 f.step(&mut state, &[SqliteValue::Integer(40)])
3452 .expect("step 4");
3453 assert_eq!(f.value(&state).expect("value"), SqliteValue::Integer(90));
3454 }
3455
3456 #[test]
3457 fn test_registry_window_wrong_arity_returns_function_error() {
3458 let mut registry = FunctionRegistry::new();
3459 registry.register_window(MovingSum);
3460
3461 let f = registry
3462 .find_window("moving_sum", 0)
3463 .expect("known window with wrong arity returns erroring window");
3464 assert_wrong_arg_count_window(f.as_ref(), &[], "moving_sum");
3465 }
3466
3467 #[test]
3468 fn test_registry_window_accepts_arg_count_reports_known_bad_arity() {
3469 let mut registry = FunctionRegistry::new();
3470 registry.register_window(MovingSum);
3471
3472 assert_eq!(
3473 registry.window_accepts_arg_count("moving_sum", 1),
3474 Some(true)
3475 );
3476 assert_eq!(
3477 registry.window_accepts_arg_count("moving_sum", 0),
3478 Some(false)
3479 );
3480 assert_eq!(registry.window_accepts_arg_count("missing_window", 1), None);
3481 }
3482
3483 #[test]
3484 fn test_registry_window_type_erased() {
3485 let mut registry = FunctionRegistry::new();
3486 registry.register_window(MovingSum);
3487
3488 let f = registry
3489 .find_window("moving_sum", 1)
3490 .expect("moving_sum found");
3491
3492 let mut state = f.initial_state();
3494 f.step(&mut state, &[SqliteValue::Integer(100)]).unwrap();
3495 assert_eq!(f.value(&state).unwrap(), SqliteValue::Integer(100));
3496
3497 f.inverse(&mut state, &[SqliteValue::Integer(100)]).unwrap();
3498 assert_eq!(f.value(&state).unwrap(), SqliteValue::Integer(0));
3499
3500 f.step(&mut state, &[SqliteValue::Integer(42)]).unwrap();
3501 assert_eq!(f.finalize(state).unwrap(), SqliteValue::Integer(42));
3502 }
3503
3504 #[test]
3505 fn test_function_key_equality() {
3506 let k1 = FunctionKey::new("ABS", 1);
3507 let k2 = FunctionKey::new("abs", 1);
3508 let k3 = FunctionKey::new("ABS", 2);
3509
3510 assert_eq!(k1, k2, "case-insensitive equality");
3511 assert_ne!(k1, k3, "different num_args");
3512 }
3513
3514 #[test]
3517 fn test_e2e_custom_collation_in_order_by() {
3518 use collation::{BinaryCollation, CollationFunction, NoCaseCollation, RtrimCollation};
3519
3520 struct ReverseAlpha;
3522
3523 impl CollationFunction for ReverseAlpha {
3524 fn name(&self) -> &str {
3525 "REVERSE_ALPHA"
3526 }
3527
3528 fn compare(&self, left: &[u8], right: &[u8]) -> std::cmp::Ordering {
3529 right.cmp(left)
3531 }
3532 }
3533
3534 let coll = ReverseAlpha;
3535 let mut data: Vec<&[u8]> = vec![b"banana", b"apple", b"cherry", b"date"];
3536 data.sort_by(|a, b| coll.compare(a, b));
3537
3538 let expected: Vec<&[u8]> = vec![b"date", b"cherry", b"banana", b"apple"];
3540 assert_eq!(data, expected);
3541 assert_eq!(coll.name(), "REVERSE_ALPHA");
3542
3543 let collations: Vec<Box<dyn CollationFunction>> = vec![
3545 Box::new(BinaryCollation),
3546 Box::new(NoCaseCollation),
3547 Box::new(RtrimCollation),
3548 Box::new(ReverseAlpha),
3549 ];
3550 assert_eq!(collations.len(), 4);
3551
3552 let mut binary_sorted = data.clone();
3554 binary_sorted.sort_by(|a, b| collations[0].compare(a, b));
3555 assert_eq!(binary_sorted[0], b"apple");
3556 }
3557
3558 #[test]
3559 fn test_e2e_authorizer_sandboxing() {
3560 use authorizer::{AuthAction, AuthResult, Authorizer};
3561
3562 struct SelectOnlyAuthorizer;
3564
3565 impl Authorizer for SelectOnlyAuthorizer {
3566 fn authorize(
3567 &self,
3568 action: AuthAction,
3569 _arg1: Option<&str>,
3570 arg2: Option<&str>,
3571 _db_name: Option<&str>,
3572 _trigger: Option<&str>,
3573 ) -> AuthResult {
3574 match action {
3575 AuthAction::Select | AuthAction::Read => {
3576 if action == AuthAction::Read && arg2 == Some("secret") {
3578 return AuthResult::Ignore;
3579 }
3580 AuthResult::Ok
3581 }
3582 AuthAction::Insert | AuthAction::Update | AuthAction::Delete => {
3583 AuthResult::Deny
3584 }
3585 _ => AuthResult::Ok,
3586 }
3587 }
3588 }
3589
3590 let auth = SelectOnlyAuthorizer;
3591
3592 assert_eq!(
3594 auth.authorize(AuthAction::Select, None, None, Some("main"), None),
3595 AuthResult::Ok,
3596 "SELECT must be allowed"
3597 );
3598
3599 assert_eq!(
3601 auth.authorize(AuthAction::Insert, Some("users"), None, Some("main"), None),
3602 AuthResult::Deny,
3603 "INSERT must be denied (compile-time auth error)"
3604 );
3605
3606 assert_eq!(
3608 auth.authorize(
3609 AuthAction::Update,
3610 Some("users"),
3611 Some("email"),
3612 Some("main"),
3613 None
3614 ),
3615 AuthResult::Deny,
3616 );
3617
3618 assert_eq!(
3620 auth.authorize(AuthAction::Delete, Some("users"), None, Some("main"), None),
3621 AuthResult::Deny,
3622 );
3623
3624 assert_eq!(
3626 auth.authorize(
3627 AuthAction::Read,
3628 Some("users"),
3629 Some("secret"),
3630 Some("main"),
3631 None
3632 ),
3633 AuthResult::Ignore,
3634 "Ignore must nullify column"
3635 );
3636
3637 assert_eq!(
3639 auth.authorize(
3640 AuthAction::Read,
3641 Some("users"),
3642 Some("name"),
3643 Some("main"),
3644 None
3645 ),
3646 AuthResult::Ok,
3647 );
3648 }
3649
3650 #[test]
3651 fn test_e2e_function_registry_resolution() {
3652 struct Abs1;
3654
3655 impl ScalarFunction for Abs1 {
3656 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
3657 Ok(SqliteValue::Integer(args[0].to_integer().abs()))
3658 }
3659
3660 fn num_args(&self) -> i32 {
3661 1
3662 }
3663
3664 fn name(&self) -> &str {
3665 "abs"
3666 }
3667 }
3668
3669 struct AbsVariadic;
3670
3671 impl ScalarFunction for AbsVariadic {
3672 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
3673 let sum: i64 = args.iter().map(|a| a.to_integer().abs()).sum();
3675 Ok(SqliteValue::Integer(sum))
3676 }
3677
3678 fn num_args(&self) -> i32 {
3679 -1
3680 }
3681
3682 fn name(&self) -> &str {
3683 "abs"
3684 }
3685 }
3686
3687 let mut registry = FunctionRegistry::new();
3688 registry.register_scalar(Abs1);
3689 registry.register_scalar(AbsVariadic);
3690
3691 let f = registry.find_scalar("abs", 1).expect("abs(1) found");
3693 assert_eq!(f.num_args(), 1, "exact 1-arg match");
3694 assert_eq!(
3695 f.invoke(&[SqliteValue::Integer(-5)]).unwrap(),
3696 SqliteValue::Integer(5)
3697 );
3698
3699 let f = registry.find_scalar("abs", 2).expect("abs variadic found");
3701 assert_eq!(f.num_args(), -1, "variadic fallback for 2 args");
3702 assert_eq!(
3703 f.invoke(&[SqliteValue::Integer(-5), SqliteValue::Integer(-3)])
3704 .unwrap(),
3705 SqliteValue::Integer(8)
3706 );
3707
3708 assert!(registry.find_scalar("nonexistent", 1).is_none());
3710 }
3711
3712 #[test]
3713 fn test_authorizer_called_at_compile_time() {
3714 use authorizer::{AuthAction, AuthResult, Authorizer};
3715 use std::sync::Mutex;
3716
3717 struct TrackingAuthorizer {
3719 calls: Mutex<Vec<AuthAction>>,
3720 }
3721
3722 impl TrackingAuthorizer {
3723 fn new() -> Self {
3724 Self {
3725 calls: Mutex::new(Vec::new()),
3726 }
3727 }
3728 }
3729
3730 impl Authorizer for TrackingAuthorizer {
3731 fn authorize(
3732 &self,
3733 action: AuthAction,
3734 _arg1: Option<&str>,
3735 _arg2: Option<&str>,
3736 _db_name: Option<&str>,
3737 _trigger: Option<&str>,
3738 ) -> AuthResult {
3739 self.calls.lock().unwrap().push(action);
3740 AuthResult::Ok
3741 }
3742 }
3743
3744 let auth = TrackingAuthorizer::new();
3745
3746 auth.authorize(AuthAction::Select, None, None, Some("main"), None);
3758 auth.authorize(
3759 AuthAction::Read,
3760 Some("users"),
3761 Some("name"),
3762 Some("main"),
3763 None,
3764 );
3765 auth.authorize(
3766 AuthAction::Read,
3767 Some("users"),
3768 Some("email"),
3769 Some("main"),
3770 None,
3771 );
3772 auth.authorize(
3773 AuthAction::Read,
3774 Some("users"),
3775 Some("id"),
3776 Some("main"),
3777 None,
3778 );
3779
3780 let calls = auth.calls.lock().unwrap();
3781 assert_eq!(calls.len(), 4, "authorizer called 4 times during prepare");
3782 assert_eq!(calls[0], AuthAction::Select);
3783 assert_eq!(calls[1], AuthAction::Read);
3784 assert_eq!(calls[2], AuthAction::Read);
3785 assert_eq!(calls[3], AuthAction::Read);
3786 drop(calls);
3787
3788 let calls_after = auth.calls.lock().unwrap();
3792 assert_eq!(
3793 calls_after.len(),
3794 4,
3795 "authorizer must NOT be called during step/execution"
3796 );
3797 drop(calls_after);
3798 }
3799}