1#![allow(clippy::unnecessary_literal_bound)]
22
23use fsqlite_error::Result;
24use fsqlite_types::SqliteValue;
25
26pub const JSON_SUBTYPE: u32 = 74;
42
43pub trait ScalarFunction: Send + Sync {
44 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue>;
46
47 fn invoke_with_arg_subtypes(
56 &self,
57 args: &[SqliteValue],
58 _arg_subtypes: &[u32],
59 ) -> Result<SqliteValue> {
60 self.invoke(args)
61 }
62
63 fn result_subtype(&self) -> Option<u32> {
69 None
70 }
71
72 fn is_deterministic(&self) -> bool {
77 true
78 }
79
80 fn num_args(&self) -> i32;
84
85 fn min_args(&self) -> i32 {
91 self.num_args().max(0)
92 }
93
94 fn max_args(&self) -> Option<i32> {
97 (self.num_args() >= 0).then(|| self.num_args())
98 }
99
100 fn accepts_arg_count(&self, num_args: i32) -> bool {
102 num_args >= self.min_args() && self.max_args().is_none_or(|max| num_args <= max)
103 }
104
105 fn name(&self) -> &str;
107}
108
109#[cfg(test)]
110mod tests {
111 use std::sync::Arc;
112
113 use fsqlite_error::FrankenError;
114
115 use super::*;
116
117 struct AddOne;
120
121 impl ScalarFunction for AddOne {
122 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
123 match &args[0] {
124 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(i + 1)),
125 SqliteValue::Float(f) => Ok(SqliteValue::Float(f + 1.0)),
126 SqliteValue::Null => Ok(SqliteValue::Null),
127 SqliteValue::Text(s) => {
128 let n: i64 = s.parse().unwrap_or(0);
129 Ok(SqliteValue::Integer(n + 1))
130 }
131 SqliteValue::Blob(_) => Ok(SqliteValue::Integer(1)),
132 }
133 }
134
135 fn num_args(&self) -> i32 {
136 1
137 }
138
139 fn name(&self) -> &str {
140 "add_one"
141 }
142 }
143
144 struct NonDeterministic;
147
148 impl ScalarFunction for NonDeterministic {
149 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
150 Ok(SqliteValue::Integer(42))
151 }
152
153 fn is_deterministic(&self) -> bool {
154 false
155 }
156
157 fn num_args(&self) -> i32 {
158 0
159 }
160
161 fn name(&self) -> &str {
162 "random_ish"
163 }
164 }
165
166 struct Concat;
169
170 impl ScalarFunction for Concat {
171 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
172 let mut result = String::new();
173 for arg in args {
174 result.push_str(&arg.to_text());
175 }
176 Ok(SqliteValue::Text(result.into()))
177 }
178
179 fn num_args(&self) -> i32 {
180 -1
181 }
182
183 fn name(&self) -> &str {
184 "concat"
185 }
186 }
187
188 struct SafeAbs;
191
192 impl ScalarFunction for SafeAbs {
193 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
194 match &args[0] {
195 SqliteValue::Integer(i) => {
196 if *i == i64::MIN {
197 return Err(FrankenError::function_error("abs(i64::MIN) would overflow"));
198 }
199 Ok(SqliteValue::Integer(i.abs()))
200 }
201 _ => Ok(args[0].clone()),
202 }
203 }
204
205 fn num_args(&self) -> i32 {
206 1
207 }
208
209 fn name(&self) -> &str {
210 "safe_abs"
211 }
212 }
213
214 struct BigResult;
217
218 impl ScalarFunction for BigResult {
219 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
220 Err(FrankenError::TooBig)
221 }
222
223 fn num_args(&self) -> i32 {
224 0
225 }
226
227 fn name(&self) -> &str {
228 "big_result"
229 }
230 }
231
232 #[test]
235 fn test_scalar_function_invoke_basic() {
236 let f = AddOne;
237 assert_eq!(
239 f.invoke(&[SqliteValue::Integer(41)]).unwrap(),
240 SqliteValue::Integer(42)
241 );
242 assert_eq!(
244 f.invoke(&[SqliteValue::Float(1.5)]).unwrap(),
245 SqliteValue::Float(2.5)
246 );
247 assert!(f.invoke(&[SqliteValue::Null]).unwrap().is_null());
249 assert_eq!(
251 f.invoke(&[SqliteValue::Text("99".into())]).unwrap(),
252 SqliteValue::Integer(100)
253 );
254 }
255
256 #[test]
257 fn test_scalar_function_deterministic_flag() {
258 let det = AddOne;
259 assert!(det.is_deterministic());
260
261 let non_det = NonDeterministic;
262 assert!(!non_det.is_deterministic());
263 }
264
265 #[test]
266 fn test_scalar_function_variadic() {
267 let f = Concat;
268 assert_eq!(f.num_args(), -1);
269 assert_eq!(f.min_args(), 0);
270 assert_eq!(f.max_args(), None);
271 assert!(f.accepts_arg_count(0));
272 assert!(f.accepts_arg_count(3));
273
274 assert_eq!(f.invoke(&[]).unwrap(), SqliteValue::Text("".into()));
276
277 assert_eq!(
279 f.invoke(&[SqliteValue::Text("hello".into())]).unwrap(),
280 SqliteValue::Text("hello".into())
281 );
282
283 assert_eq!(
285 f.invoke(&[
286 SqliteValue::Text("a".into()),
287 SqliteValue::Text("b".into()),
288 SqliteValue::Text("c".into()),
289 ])
290 .unwrap(),
291 SqliteValue::Text("abc".into())
292 );
293 }
294
295 #[test]
296 fn test_scalar_function_error_domain() {
297 let f = SafeAbs;
298 let err = f.invoke(&[SqliteValue::Integer(i64::MIN)]).unwrap_err();
299 assert!(
300 matches!(err, FrankenError::FunctionError(ref msg) if msg.contains("overflow")),
301 "expected FunctionError, got {err:?}"
302 );
303 }
304
305 #[test]
306 fn test_scalar_function_too_big_error() {
307 let f = BigResult;
308 let err = f.invoke(&[]).unwrap_err();
309 assert!(matches!(err, FrankenError::TooBig));
310 }
311
312 #[test]
313 fn test_scalar_send_sync() {
314 fn assert_send_sync<T: Send + Sync>() {}
315 assert_send_sync::<AddOne>();
316
317 let f: Arc<dyn ScalarFunction> = Arc::new(AddOne);
319 let f2 = Arc::clone(&f);
320 let handle = std::thread::spawn(move || f2.invoke(&[SqliteValue::Integer(0)]));
321 let _ = f.invoke(&[SqliteValue::Integer(1)]);
322 handle.join().unwrap().unwrap();
323 }
324}