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