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 fn replace_application_function(
894 &mut self,
895 key: FunctionKey,
896 function: ApplicationFunction,
897 ) -> Option<DisplacedApplicationFunction> {
898 assert_key_matches_arity(&key, function.arity());
899 self.application_functions
900 .entry(key.name)
901 .or_default()
902 .insert(key.num_args, function)
903 .map(|function| DisplacedApplicationFunction {
904 _function: function,
905 })
906 }
907
908 pub fn register_application_scalar_captured<F>(
914 &mut self,
915 name: &str,
916 arity: FunctionArity,
917 deterministic: bool,
918 consumes_argument_collation: bool,
919 function: F,
920 ) -> Option<DisplacedApplicationFunction>
921 where
922 F: ScalarFunction + 'static,
923 {
924 let key = FunctionKey::new(name, arity.declared_args());
925 self.replace_application_function(
926 key,
927 ApplicationFunction::Scalar {
928 function: Arc::new(function),
929 arity,
930 schema_safety: ScalarSchemaSafety::from_deterministic(deterministic),
931 query_constancy: ScalarQueryConstancy::from_deterministic(deterministic),
932 consumes_argument_collation,
933 },
934 )
935 }
936
937 pub fn register_application_aggregate_captured<F>(
942 &mut self,
943 name: &str,
944 arity: FunctionArity,
945 function: F,
946 ) -> Option<DisplacedApplicationFunction>
947 where
948 F: AggregateFunction + 'static,
949 F::State: 'static,
950 {
951 let key = FunctionKey::new(name, arity.declared_args());
952 self.replace_application_function(
953 key,
954 ApplicationFunction::Aggregate {
955 function: Arc::new(AggregateAdapter::new(function)),
956 arity,
957 },
958 )
959 }
960
961 pub fn register_application_window_captured<F>(
967 &mut self,
968 name: &str,
969 arity: FunctionArity,
970 function: F,
971 ) -> (
972 Arc<ErasedWindowFunction>,
973 Option<DisplacedApplicationFunction>,
974 )
975 where
976 F: WindowFunction + 'static,
977 F::State: 'static,
978 {
979 let key = FunctionKey::new(name, arity.declared_args());
980 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
981 let aggregate: Arc<ErasedAggregateFunction> = Arc::new(WindowAggregateBridge {
982 function: Arc::clone(®istered),
983 name: key.name.clone(),
984 arity,
985 });
986 let displaced = self.replace_application_function(
987 key,
988 ApplicationFunction::Window {
989 function: Arc::clone(®istered),
990 aggregate,
991 arity,
992 },
993 );
994 (registered, displaced)
995 }
996
997 pub fn register_scalar<F>(&mut self, function: F) -> Option<Arc<dyn ScalarFunction>>
1002 where
1003 F: ScalarFunction + 'static,
1004 {
1005 let name = function.name().to_owned();
1006 let arity = function.arity();
1007 let deterministic = function.is_deterministic();
1008 let consumes_argument_collation = function.consumes_argument_collation();
1009 self.register_scalar_captured(
1010 &name,
1011 arity,
1012 deterministic,
1013 consumes_argument_collation,
1014 function,
1015 )
1016 }
1017
1018 pub fn register_scalar_keyed<F>(
1025 &mut self,
1026 key: FunctionKey,
1027 arity: FunctionArity,
1028 deterministic: bool,
1029 consumes_argument_collation: bool,
1030 function: F,
1031 ) -> Option<Arc<dyn ScalarFunction>>
1032 where
1033 F: ScalarFunction + 'static,
1034 {
1035 assert_key_matches_arity(&key, arity);
1036 self.scalar_arities.insert(key.clone(), arity);
1037 self.scalar_schema_safety.insert(
1038 key.clone(),
1039 ScalarSchemaSafety::from_deterministic(deterministic),
1040 );
1041 self.scalar_query_constancy.insert(
1042 key.clone(),
1043 ScalarQueryConstancy::from_deterministic(deterministic),
1044 );
1045 self.scalar_argument_collation
1046 .insert(key.clone(), consumes_argument_collation);
1047 self.scalars.insert(key, Arc::new(function))
1048 }
1049
1050 pub fn register_scalar_captured<F>(
1056 &mut self,
1057 name: &str,
1058 arity: FunctionArity,
1059 deterministic: bool,
1060 consumes_argument_collation: bool,
1061 function: F,
1062 ) -> Option<Arc<dyn ScalarFunction>>
1063 where
1064 F: ScalarFunction + 'static,
1065 {
1066 let key = FunctionKey::new(name, arity.declared_args());
1067 self.scalar_arities.insert(key.clone(), arity);
1068 self.scalar_schema_safety.insert(
1069 key.clone(),
1070 ScalarSchemaSafety::from_deterministic(deterministic),
1071 );
1072 self.scalar_query_constancy.insert(
1073 key.clone(),
1074 ScalarQueryConstancy::from_deterministic(deterministic),
1075 );
1076 self.scalar_argument_collation
1077 .insert(key.clone(), consumes_argument_collation);
1078 self.scalars.insert(key, Arc::new(function))
1079 }
1080
1081 pub(crate) fn register_conditionally_deterministic_scalar<F>(
1085 &mut self,
1086 function: F,
1087 ) -> Option<Arc<dyn ScalarFunction>>
1088 where
1089 F: ScalarFunction + 'static,
1090 {
1091 let name = function.name().to_owned();
1092 let arity = function.arity();
1093 let consumes_argument_collation = function.consumes_argument_collation();
1094 let key = FunctionKey::new(&name, arity.declared_args());
1095 self.scalar_arities.insert(key.clone(), arity);
1096 self.scalar_schema_safety
1097 .insert(key.clone(), ScalarSchemaSafety::DateTimeConditional);
1098 self.scalar_query_constancy
1099 .insert(key.clone(), ScalarQueryConstancy::SlowChanging);
1100 self.scalar_argument_collation
1101 .insert(key.clone(), consumes_argument_collation);
1102 self.scalars.insert(key, Arc::new(function))
1103 }
1104
1105 pub(crate) fn register_slow_changing_scalar<F>(
1111 &mut self,
1112 function: F,
1113 ) -> Option<Arc<dyn ScalarFunction>>
1114 where
1115 F: ScalarFunction + 'static,
1116 {
1117 let name = function.name().to_owned();
1118 let arity = function.arity();
1119 let consumes_argument_collation = function.consumes_argument_collation();
1120 let key = FunctionKey::new(&name, arity.declared_args());
1121 self.scalar_arities.insert(key.clone(), arity);
1122 self.scalar_schema_safety
1123 .insert(key.clone(), ScalarSchemaSafety::Never);
1124 self.scalar_query_constancy
1125 .insert(key.clone(), ScalarQueryConstancy::SlowChanging);
1126 self.scalar_argument_collation
1127 .insert(key.clone(), consumes_argument_collation);
1128 self.scalars.insert(key, Arc::new(function))
1129 }
1130
1131 pub fn register_aggregate<F>(&mut self, function: F) -> Option<Arc<ErasedAggregateFunction>>
1135 where
1136 F: AggregateFunction + 'static,
1137 F::State: 'static,
1138 {
1139 let name = function.name().to_owned();
1140 let arity = function.arity();
1141 self.register_aggregate_captured(&name, arity, function)
1142 }
1143
1144 pub fn register_aggregate_keyed<F>(
1149 &mut self,
1150 key: FunctionKey,
1151 arity: FunctionArity,
1152 function: F,
1153 ) -> Option<Arc<ErasedAggregateFunction>>
1154 where
1155 F: AggregateFunction + 'static,
1156 F::State: 'static,
1157 {
1158 assert_key_matches_arity(&key, arity);
1159 self.aggregate_arities.insert(key.clone(), arity);
1160 self.aggregates
1161 .insert(key, Arc::new(AggregateAdapter::new(function)))
1162 }
1163
1164 pub fn register_aggregate_captured<F>(
1169 &mut self,
1170 name: &str,
1171 arity: FunctionArity,
1172 function: F,
1173 ) -> Option<Arc<ErasedAggregateFunction>>
1174 where
1175 F: AggregateFunction + 'static,
1176 F::State: 'static,
1177 {
1178 let key = FunctionKey::new(name, arity.declared_args());
1179 self.aggregate_arities.insert(key.clone(), arity);
1180 self.aggregates
1181 .insert(key, Arc::new(AggregateAdapter::new(function)))
1182 }
1183
1184 pub fn register_window<F>(&mut self, function: F) -> Option<Arc<ErasedWindowFunction>>
1188 where
1189 F: WindowFunction + 'static,
1190 F::State: 'static,
1191 {
1192 let name = function.name().to_owned();
1193 let arity = function.arity();
1194 let (_, displaced) = self.register_window_captured(&name, arity, function);
1195 displaced
1196 }
1197
1198 pub fn register_window_keyed<F>(
1204 &mut self,
1205 key: FunctionKey,
1206 arity: FunctionArity,
1207 function: F,
1208 ) -> (Arc<ErasedWindowFunction>, Option<Arc<ErasedWindowFunction>>)
1209 where
1210 F: WindowFunction + 'static,
1211 F::State: 'static,
1212 {
1213 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
1214 assert_key_matches_arity(&key, arity);
1215 self.window_arities.insert(key.clone(), arity);
1216 let displaced = self.windows.insert(key, Arc::clone(®istered));
1217 (registered, displaced)
1218 }
1219
1220 pub fn register_window_captured<F>(
1225 &mut self,
1226 name: &str,
1227 arity: FunctionArity,
1228 function: F,
1229 ) -> (Arc<ErasedWindowFunction>, Option<Arc<ErasedWindowFunction>>)
1230 where
1231 F: WindowFunction + 'static,
1232 F::State: 'static,
1233 {
1234 let registered: Arc<ErasedWindowFunction> = Arc::new(WindowAdapter::new(function));
1235 let key = FunctionKey::new(name, arity.declared_args());
1236 self.window_arities.insert(key.clone(), arity);
1237 let displaced = self.windows.insert(key, Arc::clone(®istered));
1238 (registered, displaced)
1239 }
1240
1241 #[must_use]
1246 pub fn find_scalar(&self, name: &str, num_args: i32) -> Option<Arc<dyn ScalarFunction>> {
1247 self.resolve_scalar(name, num_args)
1248 .map(|resolved| resolved.function)
1249 }
1250
1251 #[must_use]
1256 pub fn find_scalar_precanonical(
1257 &self,
1258 canonical: &str,
1259 num_args: i32,
1260 ) -> Option<Arc<dyn ScalarFunction>> {
1261 self.resolve_scalar_precanonical(canonical, num_args)
1262 .map(|resolved| resolved.function)
1263 }
1264
1265 #[must_use]
1268 pub fn resolve_scalar(&self, name: &str, num_args: i32) -> Option<ResolvedScalarFunction> {
1269 let canonical = canonical_name(name);
1270 self.resolve_scalar_precanonical(&canonical, num_args)
1271 }
1272
1273 #[must_use]
1275 pub fn resolve_scalar_precanonical(
1276 &self,
1277 canonical: &str,
1278 num_args: i32,
1279 ) -> Option<ResolvedScalarFunction> {
1280 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1281 return match application {
1282 ApplicationFunction::Scalar {
1283 function,
1284 schema_safety,
1285 query_constancy,
1286 consumes_argument_collation,
1287 ..
1288 } => {
1289 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "application", "registry lookup");
1290 Some(ResolvedScalarFunction {
1291 function: Arc::clone(function),
1292 schema_safety: *schema_safety,
1293 query_constancy: *query_constancy,
1294 consumes_argument_collation: *consumes_argument_collation,
1295 })
1296 }
1297 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => {
1298 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "shadowed_by_application", "registry lookup");
1299 None
1300 }
1301 };
1302 }
1303 let exact = FunctionKey {
1304 name: canonical.to_owned(),
1305 num_args,
1306 };
1307 if let Some(function) = self.scalars.get(&exact) {
1308 let Some(arity) = self.scalar_arities.get(&exact).copied() else {
1309 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "missing_arity", "registry lookup");
1310 return Some(ResolvedScalarFunction {
1311 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1312 schema_safety: ScalarSchemaSafety::Never,
1313 query_constancy: ScalarQueryConstancy::Volatile,
1314 consumes_argument_collation: false,
1315 });
1316 };
1317 if arity.accepts(num_args) {
1318 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "exact", "registry lookup");
1319 return Some(ResolvedScalarFunction {
1320 function: Arc::clone(function),
1321 schema_safety: self
1322 .scalar_schema_safety
1323 .get(&exact)
1324 .copied()
1325 .unwrap_or(ScalarSchemaSafety::Never),
1326 query_constancy: self
1327 .scalar_query_constancy
1328 .get(&exact)
1329 .copied()
1330 .unwrap_or(ScalarQueryConstancy::Volatile),
1331 consumes_argument_collation: self
1332 .scalar_argument_collation
1333 .get(&exact)
1334 .copied()
1335 .unwrap_or(false),
1336 });
1337 }
1338 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "wrong_arity", "registry lookup");
1339 return Some(ResolvedScalarFunction {
1340 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1341 schema_safety: ScalarSchemaSafety::Never,
1342 query_constancy: ScalarQueryConstancy::Volatile,
1343 consumes_argument_collation: false,
1344 });
1345 }
1346 let variadic = FunctionKey {
1347 name: canonical.to_owned(),
1348 num_args: -1,
1349 };
1350 if let Some(function) = self.scalars.get(&variadic) {
1351 let Some(arity) = self.scalar_arities.get(&variadic).copied() else {
1352 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "missing_arity", "registry lookup");
1353 return Some(ResolvedScalarFunction {
1354 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1355 schema_safety: ScalarSchemaSafety::Never,
1356 query_constancy: ScalarQueryConstancy::Volatile,
1357 consumes_argument_collation: false,
1358 });
1359 };
1360 if arity.accepts(num_args) {
1361 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "variadic", "registry lookup");
1362 return Some(ResolvedScalarFunction {
1363 function: Arc::clone(function),
1364 schema_safety: self
1365 .scalar_schema_safety
1366 .get(&variadic)
1367 .copied()
1368 .unwrap_or(ScalarSchemaSafety::Never),
1369 query_constancy: self
1370 .scalar_query_constancy
1371 .get(&variadic)
1372 .copied()
1373 .unwrap_or(ScalarQueryConstancy::Volatile),
1374 consumes_argument_collation: self
1375 .scalar_argument_collation
1376 .get(&variadic)
1377 .copied()
1378 .unwrap_or(false),
1379 });
1380 }
1381 debug!(name = %canonical, arity = num_args, kind = "scalar", hit = "wrong_arity", "registry lookup");
1382 return Some(ResolvedScalarFunction {
1383 function: Arc::new(WrongArgCountScalarFunction::new(canonical)),
1384 schema_safety: ScalarSchemaSafety::Never,
1385 query_constancy: ScalarQueryConstancy::Volatile,
1386 consumes_argument_collation: false,
1387 });
1388 }
1389 if self.scalars.keys().any(|key| key.name == canonical)
1390 || self.application_name_has_kind_precanonical(canonical, |kind| {
1391 kind == ApplicationFunctionKind::Scalar
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 debug!(
1403 name = %canonical,
1404 arity = num_args,
1405 kind = "scalar",
1406 hit = "miss",
1407 "registry lookup"
1408 );
1409 None
1410 }
1411
1412 #[must_use]
1416 pub fn find_aggregate(
1417 &self,
1418 name: &str,
1419 num_args: i32,
1420 ) -> Option<Arc<ErasedAggregateFunction>> {
1421 let canon = canonical_name(name);
1422 self.find_aggregate_precanonical(&canon, num_args)
1423 }
1424
1425 #[must_use]
1427 pub fn find_aggregate_precanonical(
1428 &self,
1429 canonical: &str,
1430 num_args: i32,
1431 ) -> Option<Arc<ErasedAggregateFunction>> {
1432 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1433 return match application {
1434 ApplicationFunction::Aggregate { function, .. } => {
1435 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "application", "registry lookup");
1436 Some(Arc::clone(function))
1437 }
1438 ApplicationFunction::Window { aggregate, .. } => {
1439 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "application_window", "registry lookup");
1440 Some(Arc::clone(aggregate))
1441 }
1442 ApplicationFunction::Scalar { .. } => {
1443 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "shadowed_by_application", "registry lookup");
1444 None
1445 }
1446 };
1447 }
1448 let exact = FunctionKey {
1449 name: canonical.to_owned(),
1450 num_args,
1451 };
1452 if let Some(function) = self.aggregates.get(&exact) {
1453 let Some(arity) = self.aggregate_arities.get(&exact).copied() else {
1454 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "missing_arity", "registry lookup");
1455 return Some(Arc::new(AggregateAdapter::new(
1456 WrongArgCountAggregateFunction::new(canonical),
1457 )));
1458 };
1459 if arity.accepts(num_args) {
1460 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "exact", "registry lookup");
1461 return Some(Arc::clone(function));
1462 }
1463 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1464 return Some(Arc::new(AggregateAdapter::new(
1465 WrongArgCountAggregateFunction::new(canonical),
1466 )));
1467 }
1468 let variadic = FunctionKey {
1469 name: canonical.to_owned(),
1470 num_args: -1,
1471 };
1472 if let Some(function) = self.aggregates.get(&variadic) {
1473 let Some(arity) = self.aggregate_arities.get(&variadic).copied() else {
1474 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "missing_arity", "registry lookup");
1475 return Some(Arc::new(AggregateAdapter::new(
1476 WrongArgCountAggregateFunction::new(canonical),
1477 )));
1478 };
1479 if arity.accepts(num_args) {
1480 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "variadic", "registry lookup");
1481 return Some(Arc::clone(function));
1482 }
1483 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1484 return Some(Arc::new(AggregateAdapter::new(
1485 WrongArgCountAggregateFunction::new(canonical),
1486 )));
1487 }
1488 if self.aggregates.keys().any(|key| key.name == canonical)
1489 || self.application_name_has_kind_precanonical(canonical, |kind| {
1490 kind.is_aggregate_callable()
1491 })
1492 {
1493 debug!(name = %canonical, arity = num_args, kind = "aggregate", hit = "wrong_arity", "registry lookup");
1494 return Some(Arc::new(AggregateAdapter::new(
1495 WrongArgCountAggregateFunction::new(canonical),
1496 )));
1497 }
1498 debug!(
1499 name = %canonical,
1500 arity = num_args,
1501 kind = "aggregate",
1502 hit = "miss",
1503 "registry lookup"
1504 );
1505 None
1506 }
1507
1508 #[must_use]
1512 pub fn find_window(&self, name: &str, num_args: i32) -> Option<Arc<ErasedWindowFunction>> {
1513 let canon = canonical_name(name);
1514 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1515 return match application {
1516 ApplicationFunction::Window { function, .. } => {
1517 debug!(name = %canon, arity = num_args, kind = "window", hit = "application", "registry lookup");
1518 Some(Arc::clone(function))
1519 }
1520 ApplicationFunction::Scalar { .. } | ApplicationFunction::Aggregate { .. } => {
1521 debug!(name = %canon, arity = num_args, kind = "window", hit = "shadowed_by_application", "registry lookup");
1522 None
1523 }
1524 };
1525 }
1526 let exact = FunctionKey {
1527 name: canon.clone(),
1528 num_args,
1529 };
1530 if let Some(function) = self.windows.get(&exact) {
1531 let Some(arity) = self.window_arities.get(&exact).copied() else {
1532 debug!(name = %canon, arity = num_args, kind = "window", hit = "missing_arity", "registry lookup");
1533 return Some(Arc::new(WindowAdapter::new(
1534 WrongArgCountWindowFunction::new(&canon),
1535 )));
1536 };
1537 if arity.accepts(num_args) {
1538 debug!(name = %canon, arity = num_args, kind = "window", hit = "exact", "registry lookup");
1539 return Some(Arc::clone(function));
1540 }
1541 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1542 return Some(Arc::new(WindowAdapter::new(
1543 WrongArgCountWindowFunction::new(&canon),
1544 )));
1545 }
1546 let variadic = FunctionKey {
1547 name: canon.clone(),
1548 num_args: -1,
1549 };
1550 if let Some(function) = self.windows.get(&variadic) {
1551 let Some(arity) = self.window_arities.get(&variadic).copied() else {
1552 debug!(name = %canon, arity = num_args, kind = "window", hit = "missing_arity", "registry lookup");
1553 return Some(Arc::new(WindowAdapter::new(
1554 WrongArgCountWindowFunction::new(&canon),
1555 )));
1556 };
1557 if arity.accepts(num_args) {
1558 debug!(name = %canon, arity = num_args, kind = "window", hit = "variadic", "registry lookup");
1559 return Some(Arc::clone(function));
1560 }
1561 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1562 return Some(Arc::new(WindowAdapter::new(
1563 WrongArgCountWindowFunction::new(&canon),
1564 )));
1565 }
1566 if self.windows.keys().any(|key| key.name == canon)
1567 || self.application_name_has_kind_precanonical(&canon, |kind| {
1568 kind == ApplicationFunctionKind::Window
1569 })
1570 {
1571 debug!(name = %canon, arity = num_args, kind = "window", hit = "wrong_arity", "registry lookup");
1572 return Some(Arc::new(WindowAdapter::new(
1573 WrongArgCountWindowFunction::new(&canon),
1574 )));
1575 }
1576 debug!(
1577 name = %canon,
1578 arity = num_args,
1579 kind = "window",
1580 hit = "miss",
1581 "registry lookup"
1582 );
1583 None
1584 }
1585
1586 #[must_use]
1589 pub fn contains_scalar(&self, name: &str) -> bool {
1590 let canon = canonical_name(name);
1591 self.scalars.keys().any(|k| k.name == canon)
1592 || self.application_name_has_kind_precanonical(&canon, |kind| {
1593 kind == ApplicationFunctionKind::Scalar
1594 })
1595 }
1596
1597 #[must_use]
1600 pub fn contains_aggregate(&self, name: &str) -> bool {
1601 let canon = canonical_name(name);
1602 self.aggregates.keys().any(|k| k.name == canon)
1603 || self
1604 .application_name_has_kind_precanonical(&canon, |kind| kind.is_aggregate_callable())
1605 }
1606
1607 #[must_use]
1610 pub fn contains_window(&self, name: &str) -> bool {
1611 let canon = canonical_name(name);
1612 self.windows.keys().any(|k| k.name == canon)
1613 || self.application_name_has_kind_precanonical(&canon, |kind| {
1614 kind == ApplicationFunctionKind::Window
1615 })
1616 }
1617
1618 #[must_use]
1625 pub fn scalar_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1626 let canon = canonical_name(name);
1627 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1628 return matches!(application, ApplicationFunction::Scalar { .. }).then_some(true);
1629 }
1630 let exact = FunctionKey {
1631 name: canon.clone(),
1632 num_args,
1633 };
1634 if self.scalars.contains_key(&exact) {
1635 return Some(
1636 self.scalar_arities
1637 .get(&exact)
1638 .is_some_and(|arity| arity.accepts(num_args)),
1639 );
1640 }
1641
1642 let variadic = FunctionKey {
1643 name: canon.clone(),
1644 num_args: -1,
1645 };
1646 if self.scalars.contains_key(&variadic) {
1647 return Some(
1648 self.scalar_arities
1649 .get(&variadic)
1650 .is_some_and(|arity| arity.accepts(num_args)),
1651 );
1652 }
1653
1654 (self.scalars.keys().any(|key| key.name == canon)
1655 || self.application_name_has_kind_precanonical(&canon, |kind| {
1656 kind == ApplicationFunctionKind::Scalar
1657 }))
1658 .then_some(false)
1659 }
1660
1661 #[must_use]
1666 pub fn scalar_schema_safety(&self, name: &str, num_args: i32) -> Option<ScalarSchemaSafety> {
1667 let canonical = canonical_name(name);
1668 self.scalar_schema_safety_precanonical(&canonical, num_args)
1669 }
1670
1671 #[must_use]
1673 pub fn scalar_schema_safety_precanonical(
1674 &self,
1675 canonical: &str,
1676 num_args: i32,
1677 ) -> Option<ScalarSchemaSafety> {
1678 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1679 return match application {
1680 ApplicationFunction::Scalar { schema_safety, .. } => Some(*schema_safety),
1681 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => None,
1682 };
1683 }
1684 let exact = FunctionKey {
1685 name: canonical.to_owned(),
1686 num_args,
1687 };
1688 if self.scalars.contains_key(&exact) {
1689 let accepts = self
1690 .scalar_arities
1691 .get(&exact)
1692 .is_some_and(|arity| arity.accepts(num_args));
1693 return Some(if accepts {
1694 self.scalar_schema_safety
1695 .get(&exact)
1696 .copied()
1697 .unwrap_or(ScalarSchemaSafety::Never)
1698 } else {
1699 ScalarSchemaSafety::Never
1700 });
1701 }
1702
1703 let variadic = FunctionKey {
1704 name: canonical.to_owned(),
1705 num_args: -1,
1706 };
1707 if self.scalars.contains_key(&variadic) {
1708 let Some(arity) = self.scalar_arities.get(&variadic).copied() else {
1709 return Some(ScalarSchemaSafety::Never);
1710 };
1711 if !arity.accepts(num_args) {
1712 return None;
1713 }
1714 return Some(
1715 self.scalar_schema_safety
1716 .get(&variadic)
1717 .copied()
1718 .unwrap_or(ScalarSchemaSafety::Never),
1719 );
1720 }
1721 None
1722 }
1723
1724 #[must_use]
1728 pub fn scalar_is_deterministic(&self, name: &str, num_args: i32) -> Option<bool> {
1729 self.scalar_schema_safety(name, num_args)
1730 .map(|safety| safety != ScalarSchemaSafety::Never)
1731 }
1732
1733 #[must_use]
1738 pub fn scalar_consumes_argument_collation(&self, name: &str, num_args: i32) -> Option<bool> {
1739 let canonical = canonical_name(name);
1740 self.scalar_consumes_argument_collation_precanonical(&canonical, num_args)
1741 }
1742
1743 #[must_use]
1746 pub fn scalar_consumes_argument_collation_precanonical(
1747 &self,
1748 canonical: &str,
1749 num_args: i32,
1750 ) -> Option<bool> {
1751 if let Some(application) = self.application_function_precanonical(canonical, num_args) {
1752 return match application {
1753 ApplicationFunction::Scalar {
1754 consumes_argument_collation,
1755 ..
1756 } => Some(*consumes_argument_collation),
1757 ApplicationFunction::Aggregate { .. } | ApplicationFunction::Window { .. } => None,
1758 };
1759 }
1760 let exact = FunctionKey {
1761 name: canonical.to_owned(),
1762 num_args,
1763 };
1764 if self.scalars.contains_key(&exact) {
1765 if !self
1766 .scalar_arities
1767 .get(&exact)
1768 .is_some_and(|arity| arity.accepts(num_args))
1769 {
1770 return None;
1771 }
1772 return Some(
1773 self.scalar_argument_collation
1774 .get(&exact)
1775 .copied()
1776 .unwrap_or(false),
1777 );
1778 }
1779
1780 let variadic = FunctionKey {
1781 name: canonical.to_owned(),
1782 num_args: -1,
1783 };
1784 if self.scalars.contains_key(&variadic) {
1785 if !self
1786 .scalar_arities
1787 .get(&variadic)
1788 .is_some_and(|arity| arity.accepts(num_args))
1789 {
1790 return None;
1791 }
1792 return Some(
1793 self.scalar_argument_collation
1794 .get(&variadic)
1795 .copied()
1796 .unwrap_or(false),
1797 );
1798 }
1799 None
1800 }
1801
1802 #[must_use]
1808 pub fn aggregate_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1809 let canon = canonical_name(name);
1810 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1811 return application.kind().is_aggregate_callable().then_some(true);
1812 }
1813 let exact = FunctionKey {
1814 name: canon.clone(),
1815 num_args,
1816 };
1817 if self.aggregates.contains_key(&exact) {
1818 return Some(
1819 self.aggregate_arities
1820 .get(&exact)
1821 .is_some_and(|arity| arity.accepts(num_args)),
1822 );
1823 }
1824
1825 let variadic = FunctionKey {
1826 name: canon.clone(),
1827 num_args: -1,
1828 };
1829 if self.aggregates.contains_key(&variadic) {
1830 return Some(
1831 self.aggregate_arities
1832 .get(&variadic)
1833 .is_some_and(|arity| arity.accepts(num_args)),
1834 );
1835 }
1836
1837 (self.aggregates.keys().any(|key| key.name == canon)
1838 || self.application_name_has_kind_precanonical(&canon, |kind| {
1839 kind.is_aggregate_callable()
1840 }))
1841 .then_some(false)
1842 }
1843
1844 #[must_use]
1851 pub fn window_accepts_arg_count(&self, name: &str, num_args: i32) -> Option<bool> {
1852 let canon = canonical_name(name);
1853 if let Some(application) = self.application_function_precanonical(&canon, num_args) {
1854 return (application.kind() == ApplicationFunctionKind::Window).then_some(true);
1855 }
1856 let exact = FunctionKey {
1857 name: canon.clone(),
1858 num_args,
1859 };
1860 if self.windows.contains_key(&exact) {
1861 return Some(
1862 self.window_arities
1863 .get(&exact)
1864 .is_some_and(|arity| arity.accepts(num_args)),
1865 );
1866 }
1867
1868 let variadic = FunctionKey {
1869 name: canon.clone(),
1870 num_args: -1,
1871 };
1872 if self.windows.contains_key(&variadic) {
1873 return Some(
1874 self.window_arities
1875 .get(&variadic)
1876 .is_some_and(|arity| arity.accepts(num_args)),
1877 );
1878 }
1879
1880 (self.windows.keys().any(|key| key.name == canon)
1881 || self.application_name_has_kind_precanonical(&canon, |kind| {
1882 kind == ApplicationFunctionKind::Window
1883 }))
1884 .then_some(false)
1885 }
1886
1887 #[must_use]
1891 pub fn aggregate_names_lowercase(&self) -> Vec<String> {
1892 let mut names: Vec<String> = self
1893 .aggregates
1894 .keys()
1895 .map(|k| k.name.to_ascii_lowercase())
1896 .chain(
1897 self.application_functions
1898 .iter()
1899 .filter(|(_, overloads)| {
1900 overloads
1901 .values()
1902 .any(|function| function.kind().is_aggregate_callable())
1903 })
1904 .map(|(name, _)| name.to_ascii_lowercase()),
1905 )
1906 .collect();
1907 names.sort();
1908 names.dedup();
1909 names
1910 }
1911}
1912
1913fn extend_builtin_surface_entries<'a>(
1914 entries: &mut Vec<BuiltinFunctionSurfaceEntry>,
1915 family: BuiltinFunctionFamily,
1916 keys: impl Iterator<Item = &'a FunctionKey>,
1917) {
1918 for key in keys {
1919 let name = key.name.to_ascii_lowercase();
1920 let class = builtin_function_class(&name, family);
1921 entries.push(BuiltinFunctionSurfaceEntry {
1922 is_alias: builtin_function_alias_flag(&name, family),
1923 surface_id: builtin_function_surface_id(family),
1924 name,
1925 num_args: key.num_args,
1926 family,
1927 class,
1928 });
1929 }
1930}
1931
1932fn builtin_function_class(name: &str, family: BuiltinFunctionFamily) -> BuiltinFunctionClass {
1933 match family {
1934 BuiltinFunctionFamily::Aggregate => BuiltinFunctionClass::Aggregate,
1935 BuiltinFunctionFamily::Window => BuiltinFunctionClass::Window,
1936 BuiltinFunctionFamily::Scalar => {
1937 if matches!(
1938 name,
1939 "acos"
1940 | "acosh"
1941 | "asin"
1942 | "asinh"
1943 | "atan"
1944 | "atan2"
1945 | "atanh"
1946 | "ceil"
1947 | "ceiling"
1948 | "cos"
1949 | "cosh"
1950 | "degrees"
1951 | "exp"
1952 | "floor"
1953 | "ln"
1954 | "log"
1955 | "log10"
1956 | "log2"
1957 | "mod"
1958 | "pi"
1959 | "pow"
1960 | "power"
1961 | "radians"
1962 | "sin"
1963 | "sinh"
1964 | "sqrt"
1965 | "tan"
1966 | "tanh"
1967 | "trunc"
1968 ) {
1969 BuiltinFunctionClass::MathScalar
1970 } else if matches!(
1971 name,
1972 "date" | "datetime" | "julianday" | "strftime" | "time" | "timediff" | "unixepoch"
1973 ) {
1974 BuiltinFunctionClass::DateTimeScalar
1975 } else {
1976 BuiltinFunctionClass::CoreScalar
1977 }
1978 }
1979 }
1980}
1981
1982fn builtin_function_alias_flag(name: &str, family: BuiltinFunctionFamily) -> bool {
1983 match family {
1984 BuiltinFunctionFamily::Scalar => {
1985 matches!(name, "ceiling" | "if" | "power" | "printf" | "substring")
1986 }
1987 BuiltinFunctionFamily::Aggregate | BuiltinFunctionFamily::Window => name == "string_agg",
1988 }
1989}
1990
1991const fn builtin_function_surface_id(family: BuiltinFunctionFamily) -> &'static str {
1992 match family {
1993 BuiltinFunctionFamily::Window => WINDOW_FUNCTION_SURFACE_ID,
1994 BuiltinFunctionFamily::Scalar | BuiltinFunctionFamily::Aggregate => {
1995 CORE_FUNCTION_SURFACE_ID
1996 }
1997 }
1998}
1999
2000fn canonical_name(name: &str) -> String {
2001 name.trim().to_ascii_uppercase()
2002}
2003
2004#[cfg(test)]
2005mod tests {
2006 use std::collections::BTreeSet;
2007
2008 use fsqlite_types::SqliteValue;
2009
2010 use super::*;
2011
2012 #[test]
2013 fn keyed_registration_preserves_bounds_without_reinvoking_user_metadata() {
2014 struct MetadataPanicsScalar;
2015
2016 impl ScalarFunction for MetadataPanicsScalar {
2017 fn invoke(&self, _args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2018 Ok(SqliteValue::Integer(1))
2019 }
2020
2021 fn num_args(&self) -> i32 {
2022 panic!("keyed scalar registration must not ask for arity")
2023 }
2024
2025 fn is_deterministic(&self) -> bool {
2026 panic!("keyed scalar registration must not ask for determinism")
2027 }
2028
2029 fn consumes_argument_collation(&self) -> bool {
2030 panic!("keyed scalar registration must not ask for collation metadata")
2031 }
2032
2033 fn name(&self) -> &str {
2034 panic!("keyed scalar registration must not ask for name")
2035 }
2036 }
2037
2038 struct MetadataPanicsAggregate;
2039
2040 impl AggregateFunction for MetadataPanicsAggregate {
2041 type State = ();
2042
2043 fn initial_state(&self) -> Self::State {}
2044
2045 fn step(
2046 &self,
2047 _state: &mut Self::State,
2048 _args: &[SqliteValue],
2049 ) -> fsqlite_error::Result<()> {
2050 Ok(())
2051 }
2052
2053 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2054 Ok(SqliteValue::Integer(1))
2055 }
2056
2057 fn num_args(&self) -> i32 {
2058 panic!("keyed aggregate registration must not ask for arity")
2059 }
2060
2061 fn name(&self) -> &str {
2062 panic!("keyed aggregate registration must not ask for name")
2063 }
2064 }
2065
2066 struct MetadataPanicsWindow;
2067
2068 impl WindowFunction for MetadataPanicsWindow {
2069 type State = ();
2070
2071 fn initial_state(&self) -> Self::State {}
2072
2073 fn step(
2074 &self,
2075 _state: &mut Self::State,
2076 _args: &[SqliteValue],
2077 ) -> fsqlite_error::Result<()> {
2078 Ok(())
2079 }
2080
2081 fn inverse(
2082 &self,
2083 _state: &mut Self::State,
2084 _args: &[SqliteValue],
2085 ) -> fsqlite_error::Result<()> {
2086 Ok(())
2087 }
2088
2089 fn value(&self, _state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2090 Ok(SqliteValue::Integer(1))
2091 }
2092
2093 fn finalize(&self, _state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2094 Ok(SqliteValue::Integer(1))
2095 }
2096
2097 fn num_args(&self) -> i32 {
2098 panic!("keyed window registration must not ask for arity")
2099 }
2100
2101 fn name(&self) -> &str {
2102 panic!("keyed window registration must not ask for name")
2103 }
2104 }
2105
2106 let arity = FunctionArity::variadic(1, Some(2));
2107 let scalar_key = FunctionKey::new("keyed_scalar", -1);
2108 let aggregate_key = FunctionKey::new("keyed_aggregate", -1);
2109 let window_key = FunctionKey::new("keyed_window", -1);
2110 let mut registry = FunctionRegistry::new();
2111 let scalar_displaced = registry.register_scalar_keyed(
2112 scalar_key.clone(),
2113 arity,
2114 false,
2115 false,
2116 MetadataPanicsScalar,
2117 );
2118 assert!(scalar_displaced.is_none());
2119 assert!(registry.find_scalar("keyed_scalar", 1).is_some());
2120
2121 let aggregate_displaced = registry.register_aggregate_keyed(
2122 aggregate_key.clone(),
2123 arity,
2124 MetadataPanicsAggregate,
2125 );
2126 assert!(aggregate_displaced.is_none());
2127 assert!(registry.find_aggregate("keyed_aggregate", 1).is_some());
2128
2129 let (window, window_displaced) =
2130 registry.register_window_keyed(window_key.clone(), arity, MetadataPanicsWindow);
2131 assert!(window_displaced.is_none());
2132 assert!(Arc::ptr_eq(
2133 &window,
2134 ®istry.find_window("keyed_window", 1).unwrap()
2135 ));
2136
2137 for num_args in [1, 2] {
2138 assert_eq!(
2139 registry.scalar_accepts_arg_count("keyed_scalar", num_args),
2140 Some(true)
2141 );
2142 assert_eq!(
2143 registry.aggregate_accepts_arg_count("keyed_aggregate", num_args),
2144 Some(true)
2145 );
2146 assert_eq!(
2147 registry.window_accepts_arg_count("keyed_window", num_args),
2148 Some(true)
2149 );
2150 assert_eq!(
2151 registry.scalar_is_deterministic("keyed_scalar", num_args),
2152 Some(false)
2153 );
2154 assert_eq!(
2155 registry
2156 .resolve_scalar("keyed_scalar", num_args)
2157 .unwrap()
2158 .query_constancy(),
2159 ScalarQueryConstancy::Volatile
2160 );
2161 }
2162 for num_args in [0, 3] {
2163 assert_eq!(
2164 registry.scalar_accepts_arg_count("keyed_scalar", num_args),
2165 Some(false)
2166 );
2167 assert_eq!(
2168 registry.aggregate_accepts_arg_count("keyed_aggregate", num_args),
2169 Some(false)
2170 );
2171 assert_eq!(
2172 registry.window_accepts_arg_count("keyed_window", num_args),
2173 Some(false)
2174 );
2175 }
2176
2177 registry.scalar_arities.remove(&scalar_key);
2178 registry.aggregate_arities.remove(&aggregate_key);
2179 registry.window_arities.remove(&window_key);
2180 assert_eq!(
2181 registry.scalar_accepts_arg_count("keyed_scalar", 1),
2182 Some(false)
2183 );
2184 assert_eq!(
2185 registry.scalar_is_deterministic("keyed_scalar", 1),
2186 Some(false)
2187 );
2188 assert_eq!(
2189 registry.aggregate_accepts_arg_count("keyed_aggregate", 1),
2190 Some(false)
2191 );
2192 assert_eq!(
2193 registry.window_accepts_arg_count("keyed_window", 1),
2194 Some(false)
2195 );
2196 assert_wrong_arg_count(
2197 registry.find_scalar("keyed_scalar", 1).unwrap().as_ref(),
2198 &[SqliteValue::Null],
2199 "keyed_scalar",
2200 );
2201 assert_wrong_arg_count_aggregate(
2202 registry
2203 .find_aggregate("keyed_aggregate", 1)
2204 .unwrap()
2205 .as_ref(),
2206 &[SqliteValue::Null],
2207 "keyed_aggregate",
2208 );
2209 assert_wrong_arg_count_window(
2210 registry.find_window("keyed_window", 1).unwrap().as_ref(),
2211 &[SqliteValue::Null],
2212 "keyed_window",
2213 );
2214 }
2215
2216 #[test]
2217 fn exact_registration_fails_closed_when_parallel_arity_metadata_is_missing() {
2218 let scalar_key = FunctionKey::new("double", 1);
2219 let aggregate_key = FunctionKey::new("product", 1);
2220 let window_key = FunctionKey::new("moving_sum", 1);
2221 let mut registry = FunctionRegistry::new();
2222 registry.register_scalar(Double);
2223 registry.register_aggregate(Product);
2224 registry.register_window(MovingSum);
2225
2226 assert_eq!(
2227 registry.scalar_arities.remove(&scalar_key),
2228 Some(FunctionArity::exact(1))
2229 );
2230 assert_eq!(
2231 registry.aggregate_arities.remove(&aggregate_key),
2232 Some(FunctionArity::exact(1))
2233 );
2234 assert_eq!(
2235 registry.window_arities.remove(&window_key),
2236 Some(FunctionArity::exact(1))
2237 );
2238
2239 assert_eq!(registry.scalar_accepts_arg_count("double", 1), Some(false));
2240 assert_eq!(
2241 registry.aggregate_accepts_arg_count("product", 1),
2242 Some(false)
2243 );
2244 assert_eq!(
2245 registry.window_accepts_arg_count("moving_sum", 1),
2246 Some(false)
2247 );
2248 assert_wrong_arg_count(
2249 registry.find_scalar("double", 1).unwrap().as_ref(),
2250 &[SqliteValue::Null],
2251 "double",
2252 );
2253 assert_wrong_arg_count_aggregate(
2254 registry.find_aggregate("product", 1).unwrap().as_ref(),
2255 &[SqliteValue::Null],
2256 "product",
2257 );
2258 assert_wrong_arg_count_window(
2259 registry.find_window("moving_sum", 1).unwrap().as_ref(),
2260 &[SqliteValue::Null],
2261 "moving_sum",
2262 );
2263 }
2264
2265 #[test]
2266 #[should_panic(
2267 expected = "function key and frozen arity contract must have the same declared argument count"
2268 )]
2269 fn keyed_registration_rejects_mismatched_arity_contract() {
2270 assert_key_matches_arity(&FunctionKey::new("mismatched", -1), FunctionArity::exact(1));
2271 }
2272
2273 #[test]
2274 #[should_panic(expected = "function argument count must be -1 or non-negative")]
2275 fn function_key_rejects_argument_counts_below_variadic_sentinel() {
2276 let _ = FunctionKey::new("invalid", -2);
2277 }
2278
2279 #[test]
2280 #[should_panic(expected = "function argument count must be -1 or non-negative")]
2281 fn default_arity_contract_rejects_argument_counts_below_variadic_sentinel() {
2282 let _ = FunctionArity::from_declared_args(-2, || {
2283 panic!("invalid declared arity must be rejected before reading variadic bounds")
2284 });
2285 }
2286
2287 fn runtime_registry_surface_keys() -> BTreeSet<(BuiltinFunctionFamily, String, i32)> {
2288 let mut registry = FunctionRegistry::new();
2289 register_builtins(&mut registry);
2290 register_window_builtins(&mut registry);
2291
2292 let scalar_keys = registry
2293 .scalars
2294 .keys()
2295 .map(|key| {
2296 (
2297 BuiltinFunctionFamily::Scalar,
2298 key.name.to_ascii_lowercase(),
2299 key.num_args,
2300 )
2301 })
2302 .collect::<BTreeSet<_>>();
2303 let aggregate_keys = registry
2304 .aggregates
2305 .keys()
2306 .map(|key| {
2307 (
2308 BuiltinFunctionFamily::Aggregate,
2309 key.name.to_ascii_lowercase(),
2310 key.num_args,
2311 )
2312 })
2313 .collect::<BTreeSet<_>>();
2314 let window_keys = registry
2315 .windows
2316 .keys()
2317 .map(|key| {
2318 (
2319 BuiltinFunctionFamily::Window,
2320 key.name.to_ascii_lowercase(),
2321 key.num_args,
2322 )
2323 })
2324 .collect::<BTreeSet<_>>();
2325
2326 scalar_keys
2327 .into_iter()
2328 .chain(aggregate_keys)
2329 .chain(window_keys)
2330 .collect()
2331 }
2332
2333 fn inventory_surface_keys() -> BTreeSet<(BuiltinFunctionFamily, String, i32)> {
2334 builtin_function_surface_inventory()
2335 .iter()
2336 .map(|entry| (entry.family, entry.name.clone(), entry.num_args))
2337 .collect()
2338 }
2339
2340 fn find_surface_entry(
2341 family: BuiltinFunctionFamily,
2342 name: &str,
2343 num_args: i32,
2344 ) -> &'static BuiltinFunctionSurfaceEntry {
2345 builtin_function_surface_inventory()
2346 .iter()
2347 .find(|entry| {
2348 entry.family == family && entry.name == name && entry.num_args == num_args
2349 })
2350 .unwrap_or_else(|| {
2351 unreachable!(
2352 "missing builtin surface entry: family={} name={} arity={}",
2353 family.label(),
2354 name,
2355 num_args
2356 )
2357 })
2358 }
2359
2360 #[test]
2361 fn test_builtin_function_surface_inventory_matches_live_registry() {
2362 let inventory = builtin_function_surface_inventory();
2363 let inventory_keys = inventory_surface_keys();
2364 let runtime_keys = runtime_registry_surface_keys();
2365
2366 assert_eq!(
2367 inventory.len(),
2368 inventory_keys.len(),
2369 "inventory must not contain duplicate family/name/arity tuples"
2370 );
2371 assert_eq!(
2372 inventory_keys, runtime_keys,
2373 "inventory must exactly match the live registration path"
2374 );
2375 assert!(
2376 inventory.windows(2).all(|entries| {
2377 (
2378 entries[0].family,
2379 entries[0].class,
2380 &entries[0].name,
2381 entries[0].num_args,
2382 ) <= (
2383 entries[1].family,
2384 entries[1].class,
2385 &entries[1].name,
2386 entries[1].num_args,
2387 )
2388 }),
2389 "inventory must stay deterministically sorted"
2390 );
2391 }
2392
2393 #[test]
2394 fn test_builtin_function_surface_inventory_classifies_representative_entries() {
2395 let abs = find_surface_entry(BuiltinFunctionFamily::Scalar, "abs", 1);
2396 assert_eq!(abs.class, BuiltinFunctionClass::CoreScalar);
2397 assert!(!abs.is_alias);
2398 assert_eq!(abs.surface_id, CORE_FUNCTION_SURFACE_ID);
2399
2400 let date = find_surface_entry(BuiltinFunctionFamily::Scalar, "date", -1);
2401 assert_eq!(date.class, BuiltinFunctionClass::DateTimeScalar);
2402 assert!(!date.is_alias);
2403 assert_eq!(date.surface_id, CORE_FUNCTION_SURFACE_ID);
2404
2405 let power = find_surface_entry(BuiltinFunctionFamily::Scalar, "power", 2);
2406 assert_eq!(power.class, BuiltinFunctionClass::MathScalar);
2407 assert!(power.is_alias);
2408 assert_eq!(power.surface_id, CORE_FUNCTION_SURFACE_ID);
2409
2410 let count = find_surface_entry(BuiltinFunctionFamily::Aggregate, "count", 0);
2411 assert_eq!(count.class, BuiltinFunctionClass::Aggregate);
2412 assert!(!count.is_alias);
2413 assert_eq!(count.surface_id, CORE_FUNCTION_SURFACE_ID);
2414
2415 let row_number = find_surface_entry(BuiltinFunctionFamily::Window, "row_number", 0);
2416 assert_eq!(row_number.class, BuiltinFunctionClass::Window);
2417 assert!(!row_number.is_alias);
2418 assert_eq!(row_number.surface_id, WINDOW_FUNCTION_SURFACE_ID);
2419
2420 let string_agg_window = find_surface_entry(BuiltinFunctionFamily::Window, "string_agg", 2);
2421 assert_eq!(string_agg_window.class, BuiltinFunctionClass::Window);
2422 assert!(string_agg_window.is_alias);
2423 assert_eq!(string_agg_window.surface_id, WINDOW_FUNCTION_SURFACE_ID);
2424 }
2425
2426 struct Double;
2429
2430 impl ScalarFunction for Double {
2431 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2432 Ok(SqliteValue::Integer(args[0].to_integer() * 2))
2433 }
2434
2435 fn num_args(&self) -> i32 {
2436 1
2437 }
2438
2439 fn name(&self) -> &str {
2440 "double"
2441 }
2442 }
2443
2444 struct VariadicConcat;
2447
2448 impl ScalarFunction for VariadicConcat {
2449 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2450 let mut out = String::new();
2451 for a in args {
2452 out.push_str(&a.to_text());
2453 }
2454 Ok(SqliteValue::Text(out.into()))
2455 }
2456
2457 fn num_args(&self) -> i32 {
2458 -1
2459 }
2460
2461 fn min_args(&self) -> i32 {
2462 1
2463 }
2464
2465 fn max_args(&self) -> Option<i32> {
2466 Some(3)
2467 }
2468
2469 fn name(&self) -> &str {
2470 "my_func"
2471 }
2472 }
2473
2474 struct TwoArgFunc;
2477
2478 impl ScalarFunction for TwoArgFunc {
2479 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2480 Ok(SqliteValue::Integer(
2481 args[0].to_integer() + args[1].to_integer(),
2482 ))
2483 }
2484
2485 fn num_args(&self) -> i32 {
2486 2
2487 }
2488
2489 fn name(&self) -> &str {
2490 "my_func"
2491 }
2492 }
2493
2494 fn assert_wrong_arg_count(
2495 function: &dyn ScalarFunction,
2496 args: &[SqliteValue],
2497 expected_name: &str,
2498 ) {
2499 let err = function.invoke(args).expect_err("wrong arity should fail");
2500 let expected = format!("wrong number of arguments to function {expected_name}()");
2501 assert!(
2502 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2503 "expected {expected:?}, got {err:?}"
2504 );
2505 }
2506
2507 fn assert_wrong_arg_count_aggregate(
2508 function: &ErasedAggregateFunction,
2509 args: &[SqliteValue],
2510 expected_name: &str,
2511 ) {
2512 let mut state = function.initial_state();
2513 let err = function
2514 .step(&mut state, args)
2515 .expect_err("wrong aggregate arity should fail");
2516 let expected = format!("wrong number of arguments to function {expected_name}()");
2517 assert!(
2518 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2519 "expected {expected:?}, got {err:?}"
2520 );
2521 }
2522
2523 fn assert_wrong_arg_count_window(
2524 function: &ErasedWindowFunction,
2525 args: &[SqliteValue],
2526 expected_name: &str,
2527 ) {
2528 let mut state = function.initial_state();
2529 let err = function
2530 .step(&mut state, args)
2531 .expect_err("wrong window arity should fail");
2532 let expected = format!("wrong number of arguments to function {expected_name}()");
2533 assert!(
2534 matches!(&err, FrankenError::FunctionError(message) if message == &expected),
2535 "expected {expected:?}, got {err:?}"
2536 );
2537 }
2538
2539 struct Product;
2540
2541 impl AggregateFunction for Product {
2542 type State = i64;
2543
2544 fn initial_state(&self) -> Self::State {
2545 1
2546 }
2547
2548 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2549 *state *= args[0].to_integer();
2550 Ok(())
2551 }
2552
2553 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2554 Ok(SqliteValue::Integer(state))
2555 }
2556
2557 fn num_args(&self) -> i32 {
2558 1
2559 }
2560
2561 fn name(&self) -> &str {
2562 "product"
2563 }
2564 }
2565
2566 struct MovingSum;
2567
2568 impl WindowFunction for MovingSum {
2569 type State = i64;
2570
2571 fn initial_state(&self) -> Self::State {
2572 0
2573 }
2574
2575 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2576 *state += args[0].to_integer();
2577 Ok(())
2578 }
2579
2580 fn inverse(
2581 &self,
2582 state: &mut Self::State,
2583 args: &[SqliteValue],
2584 ) -> fsqlite_error::Result<()> {
2585 *state -= args[0].to_integer();
2586 Ok(())
2587 }
2588
2589 fn value(&self, state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2590 Ok(SqliteValue::Integer(*state))
2591 }
2592
2593 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2594 Ok(SqliteValue::Integer(state))
2595 }
2596
2597 fn num_args(&self) -> i32 {
2598 1
2599 }
2600
2601 fn name(&self) -> &str {
2602 "moving_sum"
2603 }
2604 }
2605
2606 struct TaggedScalar {
2607 name: &'static str,
2608 num_args: i32,
2609 tag: i64,
2610 }
2611
2612 impl ScalarFunction for TaggedScalar {
2613 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
2614 Ok(SqliteValue::Integer(
2615 self.tag + args.iter().map(SqliteValue::to_integer).sum::<i64>(),
2616 ))
2617 }
2618
2619 fn num_args(&self) -> i32 {
2620 self.num_args
2621 }
2622
2623 fn name(&self) -> &str {
2624 self.name
2625 }
2626 }
2627
2628 struct TaggedAggregate {
2629 name: &'static str,
2630 num_args: i32,
2631 tag: i64,
2632 }
2633
2634 impl AggregateFunction for TaggedAggregate {
2635 type State = i64;
2636
2637 fn initial_state(&self) -> Self::State {
2638 0
2639 }
2640
2641 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2642 *state += args.iter().map(SqliteValue::to_integer).sum::<i64>();
2643 Ok(())
2644 }
2645
2646 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2647 Ok(SqliteValue::Integer(self.tag + state))
2648 }
2649
2650 fn num_args(&self) -> i32 {
2651 self.num_args
2652 }
2653
2654 fn name(&self) -> &str {
2655 self.name
2656 }
2657 }
2658
2659 struct TaggedWindow {
2660 name: &'static str,
2661 num_args: i32,
2662 tag: i64,
2663 }
2664
2665 impl WindowFunction for TaggedWindow {
2666 type State = i64;
2667
2668 fn initial_state(&self) -> Self::State {
2669 0
2670 }
2671
2672 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> fsqlite_error::Result<()> {
2673 *state += args.iter().map(SqliteValue::to_integer).sum::<i64>();
2674 Ok(())
2675 }
2676
2677 fn inverse(
2678 &self,
2679 state: &mut Self::State,
2680 args: &[SqliteValue],
2681 ) -> fsqlite_error::Result<()> {
2682 *state -= args.iter().map(SqliteValue::to_integer).sum::<i64>();
2683 Ok(())
2684 }
2685
2686 fn value(&self, state: &Self::State) -> fsqlite_error::Result<SqliteValue> {
2687 Ok(SqliteValue::Integer(self.tag + state))
2688 }
2689
2690 fn finalize(&self, state: Self::State) -> fsqlite_error::Result<SqliteValue> {
2691 Ok(SqliteValue::Integer(self.tag + state))
2692 }
2693
2694 fn num_args(&self) -> i32 {
2695 self.num_args
2696 }
2697
2698 fn name(&self) -> &str {
2699 self.name
2700 }
2701 }
2702
2703 fn finalize_aggregate(
2704 function: &ErasedAggregateFunction,
2705 rows: &[&[SqliteValue]],
2706 ) -> SqliteValue {
2707 let mut state = function.initial_state();
2708 for args in rows {
2709 function.step(&mut state, args).unwrap();
2710 }
2711 function.finalize(state).unwrap()
2712 }
2713
2714 #[test]
2715 fn application_resolution_prefers_exact_across_function_kinds() {
2716 let mut registry = FunctionRegistry::new();
2717 registry.register_application_aggregate_captured(
2718 "app_precedence",
2719 FunctionArity::variadic(0, None),
2720 TaggedAggregate {
2721 name: "app_precedence",
2722 num_args: -1,
2723 tag: 20_000,
2724 },
2725 );
2726 registry.register_application_scalar_captured(
2727 "app_precedence",
2728 FunctionArity::exact(1),
2729 true,
2730 false,
2731 TaggedScalar {
2732 name: "app_precedence",
2733 num_args: 1,
2734 tag: 10_000,
2735 },
2736 );
2737
2738 assert_eq!(
2739 registry
2740 .resolve_application_function("APP_PRECEDENCE", 1)
2741 .map(ApplicationFunctionResolution::kind),
2742 Some(ApplicationFunctionKind::Scalar)
2743 );
2744 assert_eq!(
2745 registry
2746 .find_scalar("app_precedence", 1)
2747 .unwrap()
2748 .invoke(&[SqliteValue::Integer(7)])
2749 .unwrap(),
2750 SqliteValue::Integer(10_007)
2751 );
2752 assert!(registry.find_aggregate("app_precedence", 1).is_none());
2753
2754 assert_eq!(
2755 registry
2756 .resolve_application_function("app_precedence", 2)
2757 .map(ApplicationFunctionResolution::kind),
2758 Some(ApplicationFunctionKind::Aggregate)
2759 );
2760 assert!(registry.find_scalar("app_precedence", 2).is_none());
2761 let args = [SqliteValue::Integer(2), SqliteValue::Integer(3)];
2762 assert_eq!(
2763 finalize_aggregate(
2764 registry
2765 .find_aggregate("app_precedence", 2)
2766 .unwrap()
2767 .as_ref(),
2768 &[&args],
2769 ),
2770 SqliteValue::Integer(20_005)
2771 );
2772
2773 let mut reverse = FunctionRegistry::new();
2774 reverse.register_application_scalar_captured(
2775 "app_precedence",
2776 FunctionArity::variadic(0, None),
2777 true,
2778 false,
2779 TaggedScalar {
2780 name: "app_precedence",
2781 num_args: -1,
2782 tag: 30_000,
2783 },
2784 );
2785 reverse.register_application_aggregate_captured(
2786 "app_precedence",
2787 FunctionArity::exact(1),
2788 TaggedAggregate {
2789 name: "app_precedence",
2790 num_args: 1,
2791 tag: 40_000,
2792 },
2793 );
2794 assert_eq!(
2795 reverse
2796 .resolve_application_function("app_precedence", 1)
2797 .map(ApplicationFunctionResolution::kind),
2798 Some(ApplicationFunctionKind::Aggregate)
2799 );
2800 assert!(reverse.find_scalar("app_precedence", 1).is_none());
2801 assert_eq!(
2802 reverse
2803 .resolve_application_function("app_precedence", 2)
2804 .map(ApplicationFunctionResolution::kind),
2805 Some(ApplicationFunctionKind::Scalar)
2806 );
2807 }
2808
2809 #[test]
2810 fn application_variadic_shadows_builtin_exact_only_when_compatible() {
2811 let mut registry = FunctionRegistry::new();
2812 registry.register_scalar(TaggedScalar {
2813 name: "layered",
2814 num_args: 1,
2815 tag: 1_000,
2816 });
2817 registry.register_application_scalar_captured(
2818 "layered",
2819 FunctionArity::variadic(2, Some(3)),
2820 true,
2821 false,
2822 TaggedScalar {
2823 name: "layered",
2824 num_args: -1,
2825 tag: 2_000,
2826 },
2827 );
2828
2829 assert_eq!(registry.resolve_application_function("layered", 1), None);
2830 assert_eq!(
2831 registry
2832 .find_scalar("layered", 1)
2833 .unwrap()
2834 .invoke(&[SqliteValue::Integer(7)])
2835 .unwrap(),
2836 SqliteValue::Integer(1_007),
2837 "an incompatible application variadic must leave the builtin layer visible"
2838 );
2839 let two_args = [SqliteValue::Integer(7), SqliteValue::Integer(8)];
2840 assert_eq!(
2841 registry
2842 .find_scalar("layered", 2)
2843 .unwrap()
2844 .invoke(&two_args)
2845 .unwrap(),
2846 SqliteValue::Integer(2_015)
2847 );
2848
2849 let displaced = registry.register_application_scalar_captured(
2850 "layered",
2851 FunctionArity::variadic(0, None),
2852 true,
2853 false,
2854 TaggedScalar {
2855 name: "layered",
2856 num_args: -1,
2857 tag: 3_000,
2858 },
2859 );
2860 assert!(displaced.is_some());
2861 assert_eq!(
2862 registry
2863 .find_scalar("layered", 1)
2864 .unwrap()
2865 .invoke(&[SqliteValue::Integer(7)])
2866 .unwrap(),
2867 SqliteValue::Integer(3_007),
2868 "a compatible application variadic must shadow an exact builtin"
2869 );
2870
2871 registry.register_aggregate(TaggedAggregate {
2872 name: "sum_like",
2873 num_args: 1,
2874 tag: 4_000,
2875 });
2876 registry.register_application_scalar_captured(
2877 "sum_like",
2878 FunctionArity::variadic(0, None),
2879 true,
2880 false,
2881 TaggedScalar {
2882 name: "sum_like",
2883 num_args: -1,
2884 tag: 5_000,
2885 },
2886 );
2887 assert!(registry.find_aggregate("sum_like", 1).is_none());
2888 assert!(registry.find_scalar("sum_like", 1).is_some());
2889
2890 let bounded_window_arity = FunctionArity::variadic(1, Some(2));
2891 registry.register_application_window_captured(
2892 "bounded_window",
2893 bounded_window_arity,
2894 TaggedWindow {
2895 name: "bounded_window",
2896 num_args: -1,
2897 tag: 6_000,
2898 },
2899 );
2900 assert_eq!(
2901 registry
2902 .find_aggregate("bounded_window", 2)
2903 .unwrap()
2904 .arity(),
2905 bounded_window_arity,
2906 "the aggregate bridge must preserve frozen variadic bounds"
2907 );
2908 assert_eq!(
2909 registry.aggregate_accepts_arg_count("bounded_window", 0),
2910 Some(false)
2911 );
2912 assert_eq!(
2913 registry.window_accepts_arg_count("bounded_window", 3),
2914 Some(false)
2915 );
2916 }
2917
2918 #[test]
2919 fn same_application_key_replaces_kind_and_window_retains_aggregate_form() {
2920 let mut registry = FunctionRegistry::new();
2921 let mut displaced = Vec::new();
2922 assert!(
2923 registry
2924 .register_application_scalar_captured(
2925 "cross_kind",
2926 FunctionArity::exact(1),
2927 true,
2928 false,
2929 TaggedScalar {
2930 name: "cross_kind",
2931 num_args: 1,
2932 tag: 70_000,
2933 },
2934 )
2935 .is_none()
2936 );
2937
2938 displaced.push(
2939 registry
2940 .register_application_aggregate_captured(
2941 "cross_kind",
2942 FunctionArity::exact(1),
2943 TaggedAggregate {
2944 name: "cross_kind",
2945 num_args: 1,
2946 tag: 80_000,
2947 },
2948 )
2949 .expect("aggregate must displace same-key scalar"),
2950 );
2951 assert_eq!(
2952 registry
2953 .resolve_application_function("cross_kind", 1)
2954 .map(ApplicationFunctionResolution::kind),
2955 Some(ApplicationFunctionKind::Aggregate)
2956 );
2957 assert!(registry.find_scalar("cross_kind", 1).is_none());
2958 assert!(registry.find_window("cross_kind", 1).is_none());
2959
2960 let (registered_window, old_aggregate) = registry.register_application_window_captured(
2961 "cross_kind",
2962 FunctionArity::exact(1),
2963 TaggedWindow {
2964 name: "cross_kind",
2965 num_args: 1,
2966 tag: 90_000,
2967 },
2968 );
2969 displaced.push(old_aggregate.expect("window must displace same-key aggregate"));
2970 assert_eq!(
2971 registry
2972 .resolve_application_function("cross_kind", 1)
2973 .map(ApplicationFunctionResolution::kind),
2974 Some(ApplicationFunctionKind::Window)
2975 );
2976 assert!(Arc::ptr_eq(
2977 ®istered_window,
2978 ®istry.find_window("cross_kind", 1).unwrap()
2979 ));
2980 let row_one = [SqliteValue::Integer(1)];
2981 let row_two = [SqliteValue::Integer(2)];
2982 let row_three = [SqliteValue::Integer(3)];
2983 assert_eq!(
2984 finalize_aggregate(
2985 registry.find_aggregate("cross_kind", 1).unwrap().as_ref(),
2986 &[&row_one, &row_two, &row_three],
2987 ),
2988 SqliteValue::Integer(90_006),
2989 "window registration must keep its ordinary aggregate form"
2990 );
2991
2992 displaced.push(
2993 registry
2994 .register_application_scalar_captured(
2995 "cross_kind",
2996 FunctionArity::exact(1),
2997 true,
2998 false,
2999 TaggedScalar {
3000 name: "cross_kind",
3001 num_args: 1,
3002 tag: 100_000,
3003 },
3004 )
3005 .expect("scalar must displace same-key window"),
3006 );
3007 assert_eq!(
3008 registry
3009 .resolve_application_function("cross_kind", 1)
3010 .map(ApplicationFunctionResolution::kind),
3011 Some(ApplicationFunctionKind::Scalar)
3012 );
3013 assert_eq!(registry.aggregate_accepts_arg_count("cross_kind", 1), None);
3014 assert_eq!(registry.window_accepts_arg_count("cross_kind", 1), None);
3015 assert!(registry.find_aggregate("cross_kind", 1).is_none());
3016 assert!(registry.find_window("cross_kind", 1).is_none());
3017 assert_eq!(displaced.len(), 3);
3018 }
3019
3020 #[test]
3021 fn test_registry_register_scalar() {
3022 let mut registry = FunctionRegistry::new();
3023 let previous = registry.register_scalar(Double);
3024 assert!(previous.is_none());
3025 assert!(registry.contains_scalar("double"));
3026 assert!(registry.contains_scalar("DOUBLE"));
3027 let f = registry
3028 .find_scalar(" Double ", 1)
3029 .expect("double registered");
3030 assert_eq!(
3031 f.invoke(&[SqliteValue::Integer(21)])
3032 .expect("invoke succeeds"),
3033 SqliteValue::Integer(42)
3034 );
3035 }
3036
3037 #[test]
3038 fn test_registry_case_insensitive_lookup() {
3039 let mut registry = FunctionRegistry::new();
3040 registry.register_scalar(Double);
3041
3042 assert!(registry.find_scalar("DOUBLE", 1).is_some());
3044 assert!(registry.find_scalar("Double", 1).is_some());
3045 assert!(registry.find_scalar(" double ", 1).is_some());
3046 }
3047
3048 #[test]
3049 fn test_registry_overwrite() {
3050 let mut registry = FunctionRegistry::new();
3051
3052 let prev = registry.register_scalar(Double);
3054 assert!(prev.is_none());
3055
3056 let prev = registry.register_scalar(Double);
3058 assert!(prev.is_some());
3059
3060 let f = registry.find_scalar("double", 1).unwrap();
3062 assert_eq!(
3063 f.invoke(&[SqliteValue::Integer(5)]).unwrap(),
3064 SqliteValue::Integer(10)
3065 );
3066 }
3067
3068 #[test]
3069 fn test_registry_variadic_fallback() {
3070 let mut registry = FunctionRegistry::new();
3071
3072 registry.register_scalar(VariadicConcat);
3074
3075 let too_few = registry
3076 .find_scalar("my_func", 0)
3077 .expect("known function with bad arity returns erroring scalar");
3078 assert_wrong_arg_count(too_few.as_ref(), &[], "my_func");
3079
3080 let f = registry
3082 .find_scalar("my_func", 3)
3083 .expect("variadic fallback");
3084 assert_eq!(
3085 f.invoke(&[
3086 SqliteValue::Text("a".into()),
3087 SqliteValue::Text("b".into()),
3088 SqliteValue::Text("c".into()),
3089 ])
3090 .unwrap(),
3091 SqliteValue::Text("abc".into())
3092 );
3093 let too_many = registry
3094 .find_scalar("my_func", 4)
3095 .expect("known function with bad arity returns erroring scalar");
3096 assert_wrong_arg_count(
3097 too_many.as_ref(),
3098 &[
3099 SqliteValue::Null,
3100 SqliteValue::Null,
3101 SqliteValue::Null,
3102 SqliteValue::Null,
3103 ],
3104 "my_func",
3105 );
3106 }
3107
3108 #[test]
3109 fn test_registry_exact_wrong_arity_returns_function_error() {
3110 let mut registry = FunctionRegistry::new();
3111 registry.register_scalar(Double);
3112
3113 let f = registry
3114 .find_scalar("double", 2)
3115 .expect("known function with wrong arity returns erroring scalar");
3116 assert_wrong_arg_count(
3117 f.as_ref(),
3118 &[SqliteValue::Integer(1), SqliteValue::Integer(2)],
3119 "double",
3120 );
3121 }
3122
3123 #[test]
3124 fn test_registry_exact_match_over_variadic() {
3125 let mut registry = FunctionRegistry::new();
3126
3127 registry.register_scalar(VariadicConcat);
3129 registry.register_scalar(TwoArgFunc);
3130
3131 let f = registry
3133 .find_scalar("my_func", 2)
3134 .expect("exact match found");
3135 assert_eq!(
3136 f.invoke(&[SqliteValue::Integer(10), SqliteValue::Integer(32)])
3137 .unwrap(),
3138 SqliteValue::Integer(42)
3139 );
3140
3141 let f = registry
3143 .find_scalar("my_func", 3)
3144 .expect("variadic fallback");
3145 assert_eq!(f.num_args(), -1);
3146 }
3147
3148 #[test]
3149 fn test_registry_not_found_returns_none() {
3150 let registry = FunctionRegistry::new();
3151 assert!(registry.find_scalar("nonexistent", 1).is_none());
3152 assert!(registry.find_aggregate("nonexistent", 1).is_none());
3153 assert!(registry.find_window("nonexistent", 1).is_none());
3154 }
3155
3156 #[test]
3157 fn test_registry_scalar_aggregate_arity_introspection_is_side_effect_free() {
3158 let mut registry = FunctionRegistry::new();
3159 registry.register_scalar(Double);
3160 registry.register_scalar(VariadicConcat);
3161 registry.register_aggregate(Product);
3162
3163 assert_eq!(registry.scalar_accepts_arg_count("double", 1), Some(true));
3164 assert_eq!(registry.scalar_accepts_arg_count("double", 2), Some(false));
3165 assert_eq!(registry.scalar_accepts_arg_count("my_func", 1), Some(true));
3166 assert_eq!(registry.scalar_accepts_arg_count("my_func", 3), Some(true));
3167 assert_eq!(registry.scalar_accepts_arg_count("my_func", 0), Some(false));
3168 assert_eq!(registry.scalar_accepts_arg_count("my_func", 4), Some(false));
3169 assert_eq!(registry.scalar_accepts_arg_count("missing_scalar", 1), None);
3170 assert_eq!(registry.scalar_is_deterministic("double", 1), Some(true));
3171 assert_eq!(registry.scalar_is_deterministic("my_func", 1), Some(true));
3172 assert_eq!(registry.scalar_is_deterministic("my_func", 0), None);
3173 assert_eq!(registry.scalar_is_deterministic("missing_scalar", 1), None);
3174
3175 assert_eq!(
3176 registry.aggregate_accepts_arg_count("product", 1),
3177 Some(true)
3178 );
3179 assert_eq!(
3180 registry.aggregate_accepts_arg_count("product", 0),
3181 Some(false)
3182 );
3183 assert_eq!(
3184 registry.aggregate_accepts_arg_count("missing_aggregate", 1),
3185 None
3186 );
3187
3188 registry
3189 .scalar_schema_safety
3190 .remove(&FunctionKey::new("double", 1));
3191 assert_eq!(
3192 registry.scalar_is_deterministic("double", 1),
3193 Some(false),
3194 "missing immutable metadata must fail closed"
3195 );
3196 }
3197
3198 #[test]
3199 fn scalar_query_constancy_is_frozen_cloned_and_fails_closed() {
3200 let mut registry = FunctionRegistry::new();
3201 registry.register_scalar(Double);
3202 registry.register_scalar_captured(
3203 "volatile_scalar",
3204 FunctionArity::exact(1),
3205 false,
3206 false,
3207 TaggedScalar {
3208 name: "ignored_by_captured_registration",
3209 num_args: 1,
3210 tag: 1,
3211 },
3212 );
3213 registry.register_conditionally_deterministic_scalar(TaggedScalar {
3214 name: "conditional_scalar",
3215 num_args: 1,
3216 tag: 2,
3217 });
3218 registry.register_slow_changing_scalar(TaggedScalar {
3219 name: "slow_scalar",
3220 num_args: 1,
3221 tag: 3,
3222 });
3223
3224 let resolved = registry.resolve_scalar("double", 1).unwrap();
3225 assert_eq!(resolved.query_constancy(), ScalarQueryConstancy::Constant);
3226 assert!(resolved.query_constancy().is_query_constant());
3227
3228 let resolved = registry.resolve_scalar("volatile_scalar", 1).unwrap();
3229 assert_eq!(resolved.query_constancy(), ScalarQueryConstancy::Volatile);
3230 assert!(!resolved.query_constancy().is_query_constant());
3231
3232 let conditional = registry.resolve_scalar("conditional_scalar", 1).unwrap();
3233 assert_eq!(
3234 conditional.schema_safety(),
3235 ScalarSchemaSafety::DateTimeConditional
3236 );
3237 assert_eq!(
3238 conditional.query_constancy(),
3239 ScalarQueryConstancy::SlowChanging
3240 );
3241
3242 let slow = registry.resolve_scalar("slow_scalar", 1).unwrap();
3243 assert_eq!(slow.schema_safety(), ScalarSchemaSafety::Never);
3244 assert_eq!(slow.query_constancy(), ScalarQueryConstancy::SlowChanging);
3245
3246 let wrong_arity = registry.resolve_scalar("double", 2).unwrap();
3247 assert_eq!(
3248 wrong_arity.query_constancy(),
3249 ScalarQueryConstancy::Volatile,
3250 "wrong-arity sentinels must never be treated as query constants"
3251 );
3252
3253 let mut cloned = FunctionRegistry::clone_from_arc(&Arc::new(registry));
3254 assert_eq!(
3255 cloned
3256 .resolve_scalar("slow_scalar", 1)
3257 .unwrap()
3258 .query_constancy(),
3259 ScalarQueryConstancy::SlowChanging,
3260 "registry snapshots must retain frozen query metadata"
3261 );
3262 cloned
3263 .scalar_query_constancy
3264 .remove(&FunctionKey::new("double", 1));
3265 assert_eq!(
3266 cloned
3267 .resolve_scalar("double", 1)
3268 .unwrap()
3269 .query_constancy(),
3270 ScalarQueryConstancy::Volatile,
3271 "missing immutable query metadata must fail closed"
3272 );
3273 }
3274
3275 #[test]
3276 fn application_shadowing_selects_matching_scalar_query_constancy() {
3277 let mut registry = FunctionRegistry::new();
3278 registry.register_slow_changing_scalar(TaggedScalar {
3279 name: "layered_constancy",
3280 num_args: 1,
3281 tag: 10,
3282 });
3283 registry.register_application_scalar_captured(
3284 "layered_constancy",
3285 FunctionArity::variadic(2, Some(3)),
3286 false,
3287 false,
3288 TaggedScalar {
3289 name: "layered_constancy",
3290 num_args: -1,
3291 tag: 20,
3292 },
3293 );
3294
3295 assert_eq!(
3296 registry
3297 .resolve_scalar("layered_constancy", 1)
3298 .unwrap()
3299 .query_constancy(),
3300 ScalarQueryConstancy::SlowChanging,
3301 "an incompatible application variadic must leave base metadata visible"
3302 );
3303 assert_eq!(
3304 registry
3305 .resolve_scalar("layered_constancy", 2)
3306 .unwrap()
3307 .query_constancy(),
3308 ScalarQueryConstancy::Volatile,
3309 "a matching non-deterministic application scalar must publish volatile metadata"
3310 );
3311
3312 registry.register_application_scalar_captured(
3313 "layered_constancy",
3314 FunctionArity::variadic(0, None),
3315 true,
3316 false,
3317 TaggedScalar {
3318 name: "layered_constancy",
3319 num_args: -1,
3320 tag: 30,
3321 },
3322 );
3323 assert_eq!(
3324 registry
3325 .resolve_scalar("layered_constancy", 1)
3326 .unwrap()
3327 .query_constancy(),
3328 ScalarQueryConstancy::Constant,
3329 "a compatible deterministic application scalar must shadow base metadata"
3330 );
3331
3332 let cloned = FunctionRegistry::clone_from_arc(&Arc::new(registry));
3333 assert_eq!(
3334 cloned
3335 .resolve_scalar("layered_constancy", 1)
3336 .unwrap()
3337 .query_constancy(),
3338 ScalarQueryConstancy::Constant,
3339 "registry snapshots must retain application query metadata"
3340 );
3341
3342 let mut shadowed = cloned;
3343 shadowed.register_application_aggregate_captured(
3344 "layered_constancy",
3345 FunctionArity::exact(1),
3346 TaggedAggregate {
3347 name: "layered_constancy",
3348 num_args: 1,
3349 tag: 40,
3350 },
3351 );
3352 assert!(
3353 shadowed.resolve_scalar("layered_constancy", 1).is_none(),
3354 "a matching application aggregate must shadow scalar metadata across kinds"
3355 );
3356 assert_eq!(
3357 shadowed
3358 .resolve_scalar("layered_constancy", 2)
3359 .unwrap()
3360 .query_constancy(),
3361 ScalarQueryConstancy::Constant,
3362 "an incompatible exact aggregate must leave the application variadic visible"
3363 );
3364 }
3365
3366 #[test]
3367 fn test_registry_register_and_resolve_aggregate() {
3368 let mut registry = FunctionRegistry::new();
3369 let previous = registry.register_aggregate(Product);
3370 assert!(previous.is_none());
3371 assert!(registry.contains_aggregate("product"));
3372 let f = registry
3373 .find_aggregate("PRODUCT", 1)
3374 .expect("product aggregate registered");
3375
3376 let mut state = f.initial_state();
3377 f.step(&mut state, &[SqliteValue::Integer(2)])
3378 .expect("step 1");
3379 f.step(&mut state, &[SqliteValue::Integer(3)])
3380 .expect("step 2");
3381 f.step(&mut state, &[SqliteValue::Integer(7)])
3382 .expect("step 3");
3383
3384 assert_eq!(
3385 f.finalize(state).expect("finalize succeeds"),
3386 SqliteValue::Integer(42)
3387 );
3388 }
3389
3390 #[test]
3391 fn test_registry_aggregate_type_erased() {
3392 let mut registry = FunctionRegistry::new();
3393 registry.register_aggregate(Product);
3394
3395 let f = registry
3397 .find_aggregate("product", 1)
3398 .expect("product found");
3399 let mut state = f.initial_state();
3400 f.step(&mut state, &[SqliteValue::Integer(6)]).unwrap();
3401 f.step(&mut state, &[SqliteValue::Integer(7)]).unwrap();
3402 assert_eq!(f.finalize(state).unwrap(), SqliteValue::Integer(42));
3403 assert_eq!(f.name(), "product");
3404 }
3405
3406 #[test]
3407 fn test_registry_aggregate_wrong_arity_returns_function_error() {
3408 let mut registry = FunctionRegistry::new();
3409 registry.register_aggregate(Product);
3410
3411 let f = registry
3412 .find_aggregate("product", 0)
3413 .expect("known aggregate with wrong arity returns erroring aggregate");
3414 assert_wrong_arg_count_aggregate(f.as_ref(), &[], "product");
3415 }
3416
3417 #[test]
3418 fn test_registry_register_and_resolve_window() {
3419 let mut registry = FunctionRegistry::new();
3420 let previous = registry.register_window(MovingSum);
3421 assert!(previous.is_none());
3422 assert!(registry.contains_window("moving_sum"));
3423 let f = registry
3424 .find_window("MOVING_SUM", 1)
3425 .expect("moving_sum window registered");
3426
3427 let mut state = f.initial_state();
3428 f.step(&mut state, &[SqliteValue::Integer(10)])
3429 .expect("step 1");
3430 f.step(&mut state, &[SqliteValue::Integer(20)])
3431 .expect("step 2");
3432 f.step(&mut state, &[SqliteValue::Integer(30)])
3433 .expect("step 3");
3434 assert_eq!(f.value(&state).expect("value"), SqliteValue::Integer(60));
3435
3436 f.inverse(&mut state, &[SqliteValue::Integer(10)])
3437 .expect("inverse 1");
3438 f.step(&mut state, &[SqliteValue::Integer(40)])
3439 .expect("step 4");
3440 assert_eq!(f.value(&state).expect("value"), SqliteValue::Integer(90));
3441 }
3442
3443 #[test]
3444 fn test_registry_window_wrong_arity_returns_function_error() {
3445 let mut registry = FunctionRegistry::new();
3446 registry.register_window(MovingSum);
3447
3448 let f = registry
3449 .find_window("moving_sum", 0)
3450 .expect("known window with wrong arity returns erroring window");
3451 assert_wrong_arg_count_window(f.as_ref(), &[], "moving_sum");
3452 }
3453
3454 #[test]
3455 fn test_registry_window_accepts_arg_count_reports_known_bad_arity() {
3456 let mut registry = FunctionRegistry::new();
3457 registry.register_window(MovingSum);
3458
3459 assert_eq!(
3460 registry.window_accepts_arg_count("moving_sum", 1),
3461 Some(true)
3462 );
3463 assert_eq!(
3464 registry.window_accepts_arg_count("moving_sum", 0),
3465 Some(false)
3466 );
3467 assert_eq!(registry.window_accepts_arg_count("missing_window", 1), None);
3468 }
3469
3470 #[test]
3471 fn test_registry_window_type_erased() {
3472 let mut registry = FunctionRegistry::new();
3473 registry.register_window(MovingSum);
3474
3475 let f = registry
3476 .find_window("moving_sum", 1)
3477 .expect("moving_sum found");
3478
3479 let mut state = f.initial_state();
3481 f.step(&mut state, &[SqliteValue::Integer(100)]).unwrap();
3482 assert_eq!(f.value(&state).unwrap(), SqliteValue::Integer(100));
3483
3484 f.inverse(&mut state, &[SqliteValue::Integer(100)]).unwrap();
3485 assert_eq!(f.value(&state).unwrap(), SqliteValue::Integer(0));
3486
3487 f.step(&mut state, &[SqliteValue::Integer(42)]).unwrap();
3488 assert_eq!(f.finalize(state).unwrap(), SqliteValue::Integer(42));
3489 }
3490
3491 #[test]
3492 fn test_function_key_equality() {
3493 let k1 = FunctionKey::new("ABS", 1);
3494 let k2 = FunctionKey::new("abs", 1);
3495 let k3 = FunctionKey::new("ABS", 2);
3496
3497 assert_eq!(k1, k2, "case-insensitive equality");
3498 assert_ne!(k1, k3, "different num_args");
3499 }
3500
3501 #[test]
3504 fn test_e2e_custom_collation_in_order_by() {
3505 use collation::{BinaryCollation, CollationFunction, NoCaseCollation, RtrimCollation};
3506
3507 struct ReverseAlpha;
3509
3510 impl CollationFunction for ReverseAlpha {
3511 fn name(&self) -> &str {
3512 "REVERSE_ALPHA"
3513 }
3514
3515 fn compare(&self, left: &[u8], right: &[u8]) -> std::cmp::Ordering {
3516 right.cmp(left)
3518 }
3519 }
3520
3521 let coll = ReverseAlpha;
3522 let mut data: Vec<&[u8]> = vec![b"banana", b"apple", b"cherry", b"date"];
3523 data.sort_by(|a, b| coll.compare(a, b));
3524
3525 let expected: Vec<&[u8]> = vec![b"date", b"cherry", b"banana", b"apple"];
3527 assert_eq!(data, expected);
3528 assert_eq!(coll.name(), "REVERSE_ALPHA");
3529
3530 let collations: Vec<Box<dyn CollationFunction>> = vec![
3532 Box::new(BinaryCollation),
3533 Box::new(NoCaseCollation),
3534 Box::new(RtrimCollation),
3535 Box::new(ReverseAlpha),
3536 ];
3537 assert_eq!(collations.len(), 4);
3538
3539 let mut binary_sorted = data.clone();
3541 binary_sorted.sort_by(|a, b| collations[0].compare(a, b));
3542 assert_eq!(binary_sorted[0], b"apple");
3543 }
3544
3545 #[test]
3546 fn test_e2e_authorizer_sandboxing() {
3547 use authorizer::{AuthAction, AuthResult, Authorizer};
3548
3549 struct SelectOnlyAuthorizer;
3551
3552 impl Authorizer for SelectOnlyAuthorizer {
3553 fn authorize(
3554 &self,
3555 action: AuthAction,
3556 _arg1: Option<&str>,
3557 arg2: Option<&str>,
3558 _db_name: Option<&str>,
3559 _trigger: Option<&str>,
3560 ) -> AuthResult {
3561 match action {
3562 AuthAction::Select | AuthAction::Read => {
3563 if action == AuthAction::Read && arg2 == Some("secret") {
3565 return AuthResult::Ignore;
3566 }
3567 AuthResult::Ok
3568 }
3569 AuthAction::Insert | AuthAction::Update | AuthAction::Delete => {
3570 AuthResult::Deny
3571 }
3572 _ => AuthResult::Ok,
3573 }
3574 }
3575 }
3576
3577 let auth = SelectOnlyAuthorizer;
3578
3579 assert_eq!(
3581 auth.authorize(AuthAction::Select, None, None, Some("main"), None),
3582 AuthResult::Ok,
3583 "SELECT must be allowed"
3584 );
3585
3586 assert_eq!(
3588 auth.authorize(AuthAction::Insert, Some("users"), None, Some("main"), None),
3589 AuthResult::Deny,
3590 "INSERT must be denied (compile-time auth error)"
3591 );
3592
3593 assert_eq!(
3595 auth.authorize(
3596 AuthAction::Update,
3597 Some("users"),
3598 Some("email"),
3599 Some("main"),
3600 None
3601 ),
3602 AuthResult::Deny,
3603 );
3604
3605 assert_eq!(
3607 auth.authorize(AuthAction::Delete, Some("users"), None, Some("main"), None),
3608 AuthResult::Deny,
3609 );
3610
3611 assert_eq!(
3613 auth.authorize(
3614 AuthAction::Read,
3615 Some("users"),
3616 Some("secret"),
3617 Some("main"),
3618 None
3619 ),
3620 AuthResult::Ignore,
3621 "Ignore must nullify column"
3622 );
3623
3624 assert_eq!(
3626 auth.authorize(
3627 AuthAction::Read,
3628 Some("users"),
3629 Some("name"),
3630 Some("main"),
3631 None
3632 ),
3633 AuthResult::Ok,
3634 );
3635 }
3636
3637 #[test]
3638 fn test_e2e_function_registry_resolution() {
3639 struct Abs1;
3641
3642 impl ScalarFunction for Abs1 {
3643 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
3644 Ok(SqliteValue::Integer(args[0].to_integer().abs()))
3645 }
3646
3647 fn num_args(&self) -> i32 {
3648 1
3649 }
3650
3651 fn name(&self) -> &str {
3652 "abs"
3653 }
3654 }
3655
3656 struct AbsVariadic;
3657
3658 impl ScalarFunction for AbsVariadic {
3659 fn invoke(&self, args: &[SqliteValue]) -> fsqlite_error::Result<SqliteValue> {
3660 let sum: i64 = args.iter().map(|a| a.to_integer().abs()).sum();
3662 Ok(SqliteValue::Integer(sum))
3663 }
3664
3665 fn num_args(&self) -> i32 {
3666 -1
3667 }
3668
3669 fn name(&self) -> &str {
3670 "abs"
3671 }
3672 }
3673
3674 let mut registry = FunctionRegistry::new();
3675 registry.register_scalar(Abs1);
3676 registry.register_scalar(AbsVariadic);
3677
3678 let f = registry.find_scalar("abs", 1).expect("abs(1) found");
3680 assert_eq!(f.num_args(), 1, "exact 1-arg match");
3681 assert_eq!(
3682 f.invoke(&[SqliteValue::Integer(-5)]).unwrap(),
3683 SqliteValue::Integer(5)
3684 );
3685
3686 let f = registry.find_scalar("abs", 2).expect("abs variadic found");
3688 assert_eq!(f.num_args(), -1, "variadic fallback for 2 args");
3689 assert_eq!(
3690 f.invoke(&[SqliteValue::Integer(-5), SqliteValue::Integer(-3)])
3691 .unwrap(),
3692 SqliteValue::Integer(8)
3693 );
3694
3695 assert!(registry.find_scalar("nonexistent", 1).is_none());
3697 }
3698
3699 #[test]
3700 fn test_authorizer_called_at_compile_time() {
3701 use authorizer::{AuthAction, AuthResult, Authorizer};
3702 use std::sync::Mutex;
3703
3704 struct TrackingAuthorizer {
3706 calls: Mutex<Vec<AuthAction>>,
3707 }
3708
3709 impl TrackingAuthorizer {
3710 fn new() -> Self {
3711 Self {
3712 calls: Mutex::new(Vec::new()),
3713 }
3714 }
3715 }
3716
3717 impl Authorizer for TrackingAuthorizer {
3718 fn authorize(
3719 &self,
3720 action: AuthAction,
3721 _arg1: Option<&str>,
3722 _arg2: Option<&str>,
3723 _db_name: Option<&str>,
3724 _trigger: Option<&str>,
3725 ) -> AuthResult {
3726 self.calls.lock().unwrap().push(action);
3727 AuthResult::Ok
3728 }
3729 }
3730
3731 let auth = TrackingAuthorizer::new();
3732
3733 auth.authorize(AuthAction::Select, None, None, Some("main"), None);
3745 auth.authorize(
3746 AuthAction::Read,
3747 Some("users"),
3748 Some("name"),
3749 Some("main"),
3750 None,
3751 );
3752 auth.authorize(
3753 AuthAction::Read,
3754 Some("users"),
3755 Some("email"),
3756 Some("main"),
3757 None,
3758 );
3759 auth.authorize(
3760 AuthAction::Read,
3761 Some("users"),
3762 Some("id"),
3763 Some("main"),
3764 None,
3765 );
3766
3767 let calls = auth.calls.lock().unwrap();
3768 assert_eq!(calls.len(), 4, "authorizer called 4 times during prepare");
3769 assert_eq!(calls[0], AuthAction::Select);
3770 assert_eq!(calls[1], AuthAction::Read);
3771 assert_eq!(calls[2], AuthAction::Read);
3772 assert_eq!(calls[3], AuthAction::Read);
3773 drop(calls);
3774
3775 let calls_after = auth.calls.lock().unwrap();
3779 assert_eq!(
3780 calls_after.len(),
3781 4,
3782 "authorizer must NOT be called during step/execution"
3783 );
3784 drop(calls_after);
3785 }
3786}