1use crate::PlannerError;
13use crate::ast::expr::Literal;
14use crate::ast::span::Span;
15use crate::planner::typed_expr::{TypedExpr, TypedExprKind};
16use crate::planner::types::ResolvedType;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Arity {
20 Exact(usize),
21 Range(usize, usize),
22 Variadic(usize),
23}
24
25impl Arity {
26 pub fn accepts(self, count: usize) -> bool {
27 match self {
28 Self::Exact(n) => count == n,
29 Self::Range(min, max) => (min..=max).contains(&count),
30 Self::Variadic(min) => count >= min,
31 }
32 }
33
34 pub fn describe(self) -> String {
35 match self {
36 Self::Exact(n) => n.to_string(),
37 Self::Range(min, max) => format!("{min}..={max}"),
38 Self::Variadic(min) => format!("{min} or more"),
39 }
40 }
41
42 pub fn validate(self, name: &str, count: usize, _span: Span) -> Result<(), PlannerError> {
43 if self.accepts(count) {
44 Ok(())
45 } else {
46 Err(PlannerError::invalid_expression(format!(
47 "function '{name}' expects {} argument(s), got {count}",
48 self.describe()
49 )))
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
55pub enum ReturnRule {
56 Fixed(ResolvedType),
57 FromArgs(fn(&[ResolvedType]) -> Result<ResolvedType, PlannerError>),
58 FromTypedArgs(fn(&[TypedExpr]) -> Result<ResolvedType, PlannerError>),
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct FnMeta {
63 pub deterministic: bool,
64 pub volatile: bool,
65 pub side_effecting: bool,
66 pub foldable: bool,
67 pub cacheable: bool,
68 pub reorderable: bool,
69}
70
71pub const PURE_META: FnMeta = FnMeta {
72 deterministic: true,
73 volatile: false,
74 side_effecting: false,
75 foldable: true,
76 cacheable: true,
77 reorderable: true,
78};
79
80pub const RANDOM_META: FnMeta = FnMeta {
81 deterministic: false,
82 volatile: true,
83 side_effecting: false,
84 foldable: false,
85 cacheable: false,
86 reorderable: true,
87};
88
89pub const STATEMENT_STABLE_META: FnMeta = FnMeta {
91 deterministic: false,
92 volatile: false,
93 side_effecting: false,
94 foldable: false,
95 cacheable: false,
96 reorderable: true,
97};
98
99pub const SYSTEM_META: FnMeta = FnMeta {
100 deterministic: false,
101 volatile: true,
102 side_effecting: false,
103 foldable: false,
104 cacheable: false,
105 reorderable: false,
106};
107
108pub const SYSTEM_SIDE_EFFECT_META: FnMeta = FnMeta {
109 deterministic: false,
110 volatile: true,
111 side_effecting: true,
112 foldable: false,
113 cacheable: false,
114 reorderable: false,
115};
116
117#[derive(Debug, Clone)]
118pub struct ScalarSignature {
119 pub name: &'static str,
120 pub arity: Arity,
121 pub check: fn(&[TypedExpr]) -> Result<(), PlannerError>,
122 pub ret: ReturnRule,
123 pub meta: FnMeta,
124}
125
126fn is_numeric(ty: &ResolvedType) -> bool {
127 matches!(
128 ty,
129 ResolvedType::Integer
130 | ResolvedType::BigInt
131 | ResolvedType::Float
132 | ResolvedType::Double
133 | ResolvedType::Null
134 )
135}
136
137pub fn check_numeric(args: &[TypedExpr]) -> Result<(), PlannerError> {
138 for arg in args {
139 if !is_numeric(&arg.resolved_type) {
140 return Err(PlannerError::type_mismatch(
141 "Numeric",
142 arg.resolved_type.type_name(),
143 arg.span,
144 ));
145 }
146 }
147 Ok(())
148}
149
150pub fn check_text(args: &[TypedExpr]) -> Result<(), PlannerError> {
151 for arg in args {
152 if !matches!(arg.resolved_type, ResolvedType::Text | ResolvedType::Null) {
153 return Err(PlannerError::type_mismatch(
154 "Text",
155 arg.resolved_type.type_name(),
156 arg.span,
157 ));
158 }
159 }
160 Ok(())
161}
162
163fn check_json_object(args: &[TypedExpr]) -> Result<(), PlannerError> {
164 if !args.len().is_multiple_of(2) {
165 return Err(PlannerError::invalid_expression(
166 "JSON_OBJECT expects label/value pairs".to_string(),
167 ));
168 }
169 for label in args.iter().step_by(2) {
170 if !matches!(label.resolved_type, ResolvedType::Text | ResolvedType::Null) {
171 return Err(PlannerError::type_mismatch(
172 "Text",
173 label.resolved_type.type_name(),
174 label.span,
175 ));
176 }
177 }
178 Ok(())
179}
180
181fn check_json_input(args: &[TypedExpr]) -> Result<(), PlannerError> {
182 for arg in args {
183 if !matches!(
184 arg.resolved_type,
185 ResolvedType::Text | ResolvedType::Json | ResolvedType::Null
186 ) {
187 return Err(PlannerError::type_mismatch(
188 "JSON or Text",
189 arg.resolved_type.type_name(),
190 arg.span,
191 ));
192 }
193 }
194 Ok(())
195}
196
197fn common_nested_type(
198 types: impl IntoIterator<Item = ResolvedType>,
199) -> Result<ResolvedType, PlannerError> {
200 let mut common = ResolvedType::Null;
201 for data_type in types {
202 common = match (&common, &data_type) {
203 (ResolvedType::Null, _) => data_type,
204 (_, ResolvedType::Null) => common,
205 (left, right) if left == right => common,
206 (ResolvedType::Integer, ResolvedType::BigInt)
207 | (ResolvedType::BigInt, ResolvedType::Integer) => ResolvedType::BigInt,
208 (
209 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Float,
210 ResolvedType::Double,
211 )
212 | (
213 ResolvedType::Double,
214 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Float,
215 ) => ResolvedType::Double,
216 (ResolvedType::Integer, ResolvedType::Float)
217 | (ResolvedType::Float, ResolvedType::Integer) => ResolvedType::Float,
218 (ResolvedType::BigInt, ResolvedType::Float)
219 | (ResolvedType::Float, ResolvedType::BigInt) => ResolvedType::Double,
220 _ => {
221 return Err(PlannerError::invalid_expression(
222 "nested values require a common element type",
223 ));
224 }
225 };
226 }
227 Ok(common)
228}
229
230fn return_array_value(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
231 Ok(ResolvedType::Array(Box::new(common_nested_type(
232 args.to_vec(),
233 )?)))
234}
235
236fn return_integer_array(_args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
237 Ok(ResolvedType::Array(Box::new(ResolvedType::Integer)))
238}
239
240fn return_text_array(_args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
241 Ok(ResolvedType::Array(Box::new(ResolvedType::Text)))
242}
243
244fn array_element(data_type: &ResolvedType) -> Result<ResolvedType, PlannerError> {
245 match data_type {
246 ResolvedType::Array(element) => Ok((**element).clone()),
247 ResolvedType::Null => Ok(ResolvedType::Null),
248 other => Err(PlannerError::invalid_expression(format!(
249 "expected ARRAY, found {other}"
250 ))),
251 }
252}
253
254fn check_array_first(args: &[TypedExpr]) -> Result<(), PlannerError> {
255 array_element(&args[0].resolved_type).map(|_| ())
256}
257
258fn check_array_append(args: &[TypedExpr]) -> Result<(), PlannerError> {
259 common_nested_type([
260 array_element(&args[0].resolved_type)?,
261 args[1].resolved_type.clone(),
262 ])
263 .map(|_| ())
264}
265
266fn check_array_prepend(args: &[TypedExpr]) -> Result<(), PlannerError> {
267 common_nested_type([
268 args[0].resolved_type.clone(),
269 array_element(&args[1].resolved_type)?,
270 ])
271 .map(|_| ())
272}
273
274fn check_array_replace(args: &[TypedExpr]) -> Result<(), PlannerError> {
275 common_nested_type([
276 array_element(&args[0].resolved_type)?,
277 args[1].resolved_type.clone(),
278 args[2].resolved_type.clone(),
279 ])
280 .map(|_| ())
281}
282
283fn check_array_slice(args: &[TypedExpr]) -> Result<(), PlannerError> {
284 check_array_first(args)?;
285 for arg in &args[1..] {
286 if !matches!(
287 arg.resolved_type,
288 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
289 ) {
290 return Err(PlannerError::invalid_expression(
291 "ARRAY slice bounds must be INTEGER",
292 ));
293 }
294 }
295 Ok(())
296}
297
298fn check_array_pair(args: &[TypedExpr]) -> Result<(), PlannerError> {
299 let left = array_element(&args[0].resolved_type)?;
300 let right = array_element(&args[1].resolved_type)?;
301 common_nested_type([left, right]).map(|_| ())
302}
303
304fn return_array_first(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
305 Ok(ResolvedType::Array(Box::new(array_element(&args[0])?)))
306}
307
308fn return_array_append(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
309 Ok(ResolvedType::Array(Box::new(common_nested_type([
310 array_element(&args[0])?,
311 args[1].clone(),
312 ])?)))
313}
314
315fn return_array_prepend(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
316 Ok(ResolvedType::Array(Box::new(common_nested_type([
317 args[0].clone(),
318 array_element(&args[1])?,
319 ])?)))
320}
321
322fn return_array_cat(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
323 Ok(ResolvedType::Array(Box::new(common_nested_type([
324 array_element(&args[0])?,
325 array_element(&args[1])?,
326 ])?)))
327}
328
329fn check_map(args: &[TypedExpr]) -> Result<(), PlannerError> {
330 array_element(&args[0].resolved_type)?;
331 array_element(&args[1].resolved_type)?;
332 Ok(())
333}
334
335fn check_array_text_tail(args: &[TypedExpr]) -> Result<(), PlannerError> {
336 check_array_first(args)?;
337 check_text(&args[1..])
338}
339
340fn return_map(args: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
341 Ok(ResolvedType::Map {
342 key: Box::new(array_element(&args[0])?),
343 value: Box::new(array_element(&args[1])?),
344 })
345}
346
347fn check_struct_pack(args: &[TypedExpr]) -> Result<(), PlannerError> {
348 if !args.len().is_multiple_of(2) {
349 return Err(PlannerError::invalid_expression(
350 "STRUCT_PACK expects name/value pairs",
351 ));
352 }
353 for name in args.iter().step_by(2) {
354 if !matches!(name.kind, TypedExprKind::Literal(Literal::String(_))) {
355 return Err(PlannerError::invalid_expression(
356 "STRUCT_PACK field names must be string literals",
357 ));
358 }
359 }
360 Ok(())
361}
362
363fn return_struct(args: &[TypedExpr]) -> Result<ResolvedType, PlannerError> {
364 Ok(ResolvedType::Struct(
365 args.as_chunks::<2>()
366 .0
367 .iter()
368 .map(|pair| {
369 let TypedExprKind::Literal(Literal::String(name)) = &pair[0].kind else {
370 unreachable!("checked by check_struct_pack")
371 };
372 (name.clone(), pair[1].resolved_type.clone())
373 })
374 .collect(),
375 ))
376}
377
378fn check_subscript(args: &[TypedExpr]) -> Result<(), PlannerError> {
379 match &args[0].resolved_type {
380 ResolvedType::Array(_) => {
381 if matches!(
382 args[1].resolved_type,
383 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
384 ) {
385 Ok(())
386 } else {
387 Err(PlannerError::invalid_expression(
388 "ARRAY subscript must be INTEGER",
389 ))
390 }
391 }
392 ResolvedType::Map { key, .. } if args[1].resolved_type.can_cast_to(key) => Ok(()),
393 ResolvedType::Struct(_) if matches!(args[1].resolved_type, ResolvedType::Text) => Ok(()),
394 ResolvedType::Null => Ok(()),
395 other => Err(PlannerError::invalid_expression(format!(
396 "cannot subscript {other}"
397 ))),
398 }
399}
400
401fn return_subscript(args: &[TypedExpr]) -> Result<ResolvedType, PlannerError> {
402 match &args[0].resolved_type {
403 ResolvedType::Array(element) => Ok((**element).clone()),
404 ResolvedType::Map { value, .. } => Ok((**value).clone()),
405 ResolvedType::Struct(fields) => {
406 let TypedExprKind::Literal(Literal::String(name)) = &args[1].kind else {
407 return Ok(ResolvedType::Null);
408 };
409 fields
410 .iter()
411 .find(|(field, _)| field == name)
412 .map(|(_, data_type)| data_type.clone())
413 .ok_or_else(|| {
414 PlannerError::invalid_expression(format!("unknown struct field '{name}'"))
415 })
416 }
417 ResolvedType::Null => Ok(ResolvedType::Null),
418 _ => unreachable!("checked by check_subscript"),
419 }
420}
421
422fn check_json_selector(args: &[TypedExpr]) -> Result<(), PlannerError> {
423 check_json_input(&args[..1])?;
424 if matches!(
425 args[1].resolved_type,
426 ResolvedType::Text | ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
427 ) {
428 Ok(())
429 } else {
430 Err(PlannerError::type_mismatch(
431 "Text or Integer",
432 args[1].resolved_type.type_name(),
433 args[1].span,
434 ))
435 }
436}
437
438fn check_json_path(args: &[TypedExpr]) -> Result<(), PlannerError> {
439 check_json_input(&args[..1])?;
440 check_text(&args[1..])
441}
442
443fn check_jsonb_update(args: &[TypedExpr]) -> Result<(), PlannerError> {
444 if args.len() < 3 || args.len().is_multiple_of(2) {
445 return Err(PlannerError::invalid_expression(
446 "JSON update expects JSON followed by path/value pairs".to_string(),
447 ));
448 }
449 check_json_input(&args[..1])?;
450 for path in args[1..].iter().step_by(2) {
451 check_text(std::slice::from_ref(path))?;
452 }
453 Ok(())
454}
455
456fn check_json_update(args: &[TypedExpr]) -> Result<(), PlannerError> {
457 if args.len() < 3 || args.len().is_multiple_of(2) {
458 return Err(PlannerError::invalid_expression(
459 "JSON update expects JSON followed by path/value pairs".to_string(),
460 ));
461 }
462 check_text(&args[..1])?;
463 for path in args[1..].iter().step_by(2) {
464 check_text(std::slice::from_ref(path))?;
465 }
466 Ok(())
467}
468
469pub fn check_text_or_blob(args: &[TypedExpr]) -> Result<(), PlannerError> {
470 for arg in args {
471 if !matches!(
472 arg.resolved_type,
473 ResolvedType::Text | ResolvedType::Blob | ResolvedType::Null
474 ) {
475 return Err(PlannerError::type_mismatch(
476 "Text or Blob",
477 arg.resolved_type.type_name(),
478 arg.span,
479 ));
480 }
481 }
482 Ok(())
483}
484
485fn check_timestamp(args: &[TypedExpr]) -> Result<(), PlannerError> {
486 for arg in args {
487 if !matches!(
488 arg.resolved_type,
489 ResolvedType::Timestamp | ResolvedType::Null
490 ) {
491 return Err(PlannerError::type_mismatch(
492 "Timestamp",
493 arg.resolved_type.type_name(),
494 arg.span,
495 ));
496 }
497 }
498 Ok(())
499}
500
501fn check_text_timestamp(args: &[TypedExpr]) -> Result<(), PlannerError> {
502 check_text(&args[..1])?;
503 check_timestamp(&args[1..])
504}
505
506fn check_timestamp_text(args: &[TypedExpr]) -> Result<(), PlannerError> {
507 check_timestamp(&args[..1])?;
508 check_text(&args[1..])
509}
510
511fn check_to_timestamp(args: &[TypedExpr]) -> Result<(), PlannerError> {
512 if args.len() == 1 {
513 let arg = &args[0];
514 if is_numeric(&arg.resolved_type)
515 || matches!(arg.resolved_type, ResolvedType::Text | ResolvedType::Null)
516 {
517 return Ok(());
518 }
519 return Err(PlannerError::type_mismatch(
520 "Numeric or Text",
521 arg.resolved_type.type_name(),
522 arg.span,
523 ));
524 }
525 check_text(args)
526}
527
528fn check_temporal_input(args: &[TypedExpr]) -> Result<(), PlannerError> {
529 for arg in args {
530 if !matches!(
531 arg.resolved_type,
532 ResolvedType::Text
533 | ResolvedType::Date
534 | ResolvedType::Time
535 | ResolvedType::Timestamp
536 | ResolvedType::Null
537 ) {
538 return Err(PlannerError::type_mismatch(
539 "Temporal or Text",
540 arg.resolved_type.type_name(),
541 arg.span,
542 ));
543 }
544 }
545 Ok(())
546}
547
548fn check_datetime(args: &[TypedExpr]) -> Result<(), PlannerError> {
549 check_temporal_input(&args[..1])?;
550 check_text(&args[1..])
551}
552
553fn check_temporal_interval(args: &[TypedExpr]) -> Result<(), PlannerError> {
554 let temporal = &args[0];
555 if !matches!(
556 temporal.resolved_type,
557 ResolvedType::Date | ResolvedType::Time | ResolvedType::Timestamp | ResolvedType::Null
558 ) {
559 return Err(PlannerError::type_mismatch(
560 "Date, Time, or Timestamp",
561 temporal.resolved_type.type_name(),
562 temporal.span,
563 ));
564 }
565 let interval = &args[1];
566 if matches!(
567 interval.resolved_type,
568 ResolvedType::Interval | ResolvedType::Null
569 ) {
570 Ok(())
571 } else {
572 Err(PlannerError::type_mismatch(
573 "Interval",
574 interval.resolved_type.type_name(),
575 interval.span,
576 ))
577 }
578}
579
580fn check_age(args: &[TypedExpr]) -> Result<(), PlannerError> {
581 for arg in args {
582 if !matches!(
583 arg.resolved_type,
584 ResolvedType::Date | ResolvedType::Timestamp | ResolvedType::Null
585 ) {
586 return Err(PlannerError::type_mismatch(
587 "Date or Timestamp",
588 arg.resolved_type.type_name(),
589 arg.span,
590 ));
591 }
592 }
593 if args.len() == 2
594 && args[0].resolved_type != ResolvedType::Null
595 && args[1].resolved_type != ResolvedType::Null
596 && args[0].resolved_type != args[1].resolved_type
597 {
598 return Err(PlannerError::type_mismatch(
599 args[0].resolved_type.type_name(),
600 args[1].resolved_type.type_name(),
601 args[1].span,
602 ));
603 }
604 Ok(())
605}
606
607pub fn check_bigint(args: &[TypedExpr]) -> Result<(), PlannerError> {
608 for arg in args {
609 if !matches!(arg.resolved_type, ResolvedType::BigInt | ResolvedType::Null) {
610 return Err(PlannerError::type_mismatch(
611 "BigInt",
612 arg.resolved_type.type_name(),
613 arg.span,
614 ));
615 }
616 }
617 Ok(())
618}
619
620fn check_integer(args: &[TypedExpr]) -> Result<(), PlannerError> {
621 for arg in args {
622 if !matches!(
623 arg.resolved_type,
624 ResolvedType::Integer | ResolvedType::BigInt | ResolvedType::Null
625 ) {
626 return Err(PlannerError::type_mismatch(
627 "Integer",
628 arg.resolved_type.type_name(),
629 arg.span,
630 ));
631 }
632 }
633 Ok(())
634}
635
636pub fn check_blob_text(args: &[TypedExpr]) -> Result<(), PlannerError> {
637 if let Some(first) = args.first()
638 && !matches!(first.resolved_type, ResolvedType::Blob | ResolvedType::Null)
639 {
640 return Err(PlannerError::type_mismatch(
641 "Blob",
642 first.resolved_type.type_name(),
643 first.span,
644 ));
645 }
646 check_text(&args[1..])
647}
648
649pub fn check_any(args: &[TypedExpr]) -> Result<(), PlannerError> {
650 if args.is_empty() {
651 return Err(PlannerError::invalid_expression(
652 "at least one argument is required",
653 ));
654 }
655 Ok(())
656}
657
658pub fn check_no_args(_args: &[TypedExpr]) -> Result<(), PlannerError> {
659 Ok(())
660}
661
662fn check_vector_one(args: &[TypedExpr]) -> Result<(), PlannerError> {
663 match args.first().map(|arg| &arg.resolved_type) {
664 Some(ResolvedType::Vector { .. } | ResolvedType::Null) => Ok(()),
665 Some(ty) => Err(PlannerError::type_mismatch(
666 "Vector",
667 ty.type_name(),
668 args[0].span,
669 )),
670 None => Ok(()),
671 }
672}
673
674fn check_vector_triplet(args: &[TypedExpr]) -> Result<(), PlannerError> {
675 let first = match &args[0].resolved_type {
676 ResolvedType::Vector { dimension, .. } => *dimension,
677 ty => {
678 return Err(PlannerError::type_mismatch(
679 "Vector",
680 ty.type_name(),
681 args[0].span,
682 ));
683 }
684 };
685 let second = match &args[1].resolved_type {
686 ResolvedType::Vector { dimension, .. } => *dimension,
687 ty => {
688 return Err(PlannerError::type_mismatch(
689 "Vector",
690 ty.type_name(),
691 args[1].span,
692 ));
693 }
694 };
695 if first != second {
696 return Err(PlannerError::vector_dimension_mismatch(
697 first,
698 second,
699 args[1].span,
700 ));
701 }
702 match &args[2].resolved_type {
703 ResolvedType::Text => {
704 if let TypedExprKind::Literal(Literal::String(value)) = &args[2].kind
705 && !matches!(
706 value.to_ascii_lowercase().as_str(),
707 "cosine" | "l2" | "inner"
708 )
709 {
710 return Err(PlannerError::invalid_metric(value, args[2].span));
711 }
712 Ok(())
713 }
714 ty => Err(PlannerError::type_mismatch(
715 "Text (metric)",
716 ty.type_name(),
717 args[2].span,
718 )),
719 }
720}
721
722fn numeric_return(types: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
723 let mut result = ResolvedType::Null;
724 for ty in types {
725 if matches!(ty, ResolvedType::Null) {
726 continue;
727 }
728 if !is_numeric(ty) {
729 return Err(PlannerError::type_mismatch(
730 "Numeric",
731 ty.type_name(),
732 Span::default(),
733 ));
734 }
735 result = match (&result, ty) {
736 (ResolvedType::Null, _) => ty.clone(),
737 (ResolvedType::Double, _) | (_, ResolvedType::Double) => ResolvedType::Double,
738 (ResolvedType::Float, ResolvedType::BigInt)
739 | (ResolvedType::BigInt, ResolvedType::Float) => ResolvedType::Double,
740 (ResolvedType::Float, _) | (_, ResolvedType::Float) => ResolvedType::Float,
741 (ResolvedType::BigInt, _) | (_, ResolvedType::BigInt) => ResolvedType::BigInt,
742 _ => ResolvedType::Integer,
743 };
744 }
745 Ok(result)
746}
747
748fn return_arg0(types: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
749 Ok(types.first().cloned().unwrap_or(ResolvedType::Null))
750}
751
752fn return_first_non_null(types: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
753 Ok(types
754 .iter()
755 .find(|ty| !matches!(ty, ResolvedType::Null))
756 .cloned()
757 .unwrap_or(ResolvedType::Null))
758}
759
760fn return_numeric(types: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
761 numeric_return(types)
762}
763
764fn return_arg0_numeric(types: &[ResolvedType]) -> Result<ResolvedType, PlannerError> {
765 return_arg0(types)
766}
767
768const fn sig(
769 name: &'static str,
770 arity: Arity,
771 check: fn(&[TypedExpr]) -> Result<(), PlannerError>,
772 ret: ReturnRule,
773) -> ScalarSignature {
774 ScalarSignature {
775 name,
776 arity,
777 check,
778 ret,
779 meta: PURE_META,
780 }
781}
782
783const fn sig_meta(
784 name: &'static str,
785 arity: Arity,
786 check: fn(&[TypedExpr]) -> Result<(), PlannerError>,
787 ret: ReturnRule,
788 meta: FnMeta,
789) -> ScalarSignature {
790 ScalarSignature {
791 name,
792 arity,
793 check,
794 ret,
795 meta,
796 }
797}
798
799static SIGNATURES: &[ScalarSignature] = &[
800 sig(
801 "to_tsvector",
802 Arity::Range(1, 2),
803 check_text,
804 ReturnRule::Fixed(ResolvedType::Text),
805 ),
806 sig(
807 "to_tsquery",
808 Arity::Range(1, 2),
809 check_text,
810 ReturnRule::Fixed(ResolvedType::Text),
811 ),
812 sig(
813 "plainto_tsquery",
814 Arity::Range(1, 2),
815 check_text,
816 ReturnRule::Fixed(ResolvedType::Text),
817 ),
818 sig(
819 "websearch_to_tsquery",
820 Arity::Range(1, 2),
821 check_text,
822 ReturnRule::Fixed(ResolvedType::Text),
823 ),
824 sig(
825 "ts_rank",
826 Arity::Exact(2),
827 check_text,
828 ReturnRule::Fixed(ResolvedType::Double),
829 ),
830 sig(
831 "ts_headline",
832 Arity::Range(2, 3),
833 check_text,
834 ReturnRule::Fixed(ResolvedType::Text),
835 ),
836 sig(
837 "array_value",
838 Arity::Variadic(0),
839 check_no_args,
840 ReturnRule::FromArgs(return_array_value),
841 ),
842 sig(
843 "list_value",
844 Arity::Variadic(0),
845 check_no_args,
846 ReturnRule::FromArgs(return_array_value),
847 ),
848 sig(
849 "array_append",
850 Arity::Exact(2),
851 check_array_append,
852 ReturnRule::FromArgs(return_array_append),
853 ),
854 sig(
855 "array_prepend",
856 Arity::Exact(2),
857 check_array_prepend,
858 ReturnRule::FromArgs(return_array_prepend),
859 ),
860 sig(
861 "array_cat",
862 Arity::Exact(2),
863 check_array_pair,
864 ReturnRule::FromArgs(return_array_cat),
865 ),
866 sig(
867 "array_remove",
868 Arity::Exact(2),
869 check_array_append,
870 ReturnRule::FromArgs(return_array_first),
871 ),
872 sig(
873 "array_replace",
874 Arity::Exact(3),
875 check_array_replace,
876 ReturnRule::FromArgs(return_array_first),
877 ),
878 sig(
879 "array_length",
880 Arity::Exact(1),
881 check_array_first,
882 ReturnRule::Fixed(ResolvedType::Integer),
883 ),
884 sig(
885 "array_position",
886 Arity::Exact(2),
887 check_array_append,
888 ReturnRule::Fixed(ResolvedType::Integer),
889 ),
890 sig(
891 "array_positions",
892 Arity::Exact(2),
893 check_array_append,
894 ReturnRule::FromArgs(return_integer_array),
895 ),
896 sig(
897 "string_to_array",
898 Arity::Range(2, 3),
899 check_text,
900 ReturnRule::FromArgs(return_text_array),
901 ),
902 sig(
903 "array_to_string",
904 Arity::Range(2, 3),
905 check_array_text_tail,
906 ReturnRule::Fixed(ResolvedType::Text),
907 ),
908 sig(
909 "map",
910 Arity::Exact(2),
911 check_map,
912 ReturnRule::FromArgs(return_map),
913 ),
914 sig(
915 "struct_pack",
916 Arity::Variadic(0),
917 check_struct_pack,
918 ReturnRule::FromTypedArgs(return_struct),
919 ),
920 sig(
921 "array_subscript",
922 Arity::Exact(2),
923 check_subscript,
924 ReturnRule::FromTypedArgs(return_subscript),
925 ),
926 sig(
927 "array_slice",
928 Arity::Exact(3),
929 check_array_slice,
930 ReturnRule::FromArgs(return_array_first),
931 ),
932 sig(
933 "jsonb_extract",
934 Arity::Exact(2),
935 check_json_selector,
936 ReturnRule::Fixed(ResolvedType::Json),
937 ),
938 sig(
939 "jsonb_extract_text",
940 Arity::Exact(2),
941 check_json_selector,
942 ReturnRule::Fixed(ResolvedType::Text),
943 ),
944 sig(
945 "jsonb_extract_path",
946 Arity::Exact(2),
947 check_json_path,
948 ReturnRule::Fixed(ResolvedType::Json),
949 ),
950 sig(
951 "jsonb_extract_path_text",
952 Arity::Exact(2),
953 check_json_path,
954 ReturnRule::Fixed(ResolvedType::Text),
955 ),
956 sig(
957 "jsonb_set",
958 Arity::Variadic(3),
959 check_jsonb_update,
960 ReturnRule::Fixed(ResolvedType::Json),
961 ),
962 sig(
963 "jsonb_insert",
964 Arity::Variadic(3),
965 check_jsonb_update,
966 ReturnRule::Fixed(ResolvedType::Json),
967 ),
968 sig(
969 "jsonb_build_object",
970 Arity::Variadic(0),
971 check_json_object,
972 ReturnRule::Fixed(ResolvedType::Json),
973 ),
974 sig(
975 "jsonb_build_array",
976 Arity::Variadic(0),
977 check_no_args,
978 ReturnRule::Fixed(ResolvedType::Json),
979 ),
980 sig(
981 "json",
982 Arity::Exact(1),
983 check_text,
984 ReturnRule::Fixed(ResolvedType::Text),
985 ),
986 sig(
987 "json_valid",
988 Arity::Exact(1),
989 check_text,
990 ReturnRule::Fixed(ResolvedType::Boolean),
991 ),
992 sig(
993 "json_type",
994 Arity::Range(1, 2),
995 check_text,
996 ReturnRule::Fixed(ResolvedType::Text),
997 ),
998 sig(
999 "json_extract",
1000 Arity::Variadic(2),
1001 check_text,
1002 ReturnRule::Fixed(ResolvedType::Text),
1003 ),
1004 sig(
1005 "json_object",
1006 Arity::Variadic(0),
1007 check_json_object,
1008 ReturnRule::Fixed(ResolvedType::Text),
1009 ),
1010 sig(
1011 "json_array",
1012 Arity::Variadic(0),
1013 check_no_args,
1014 ReturnRule::Fixed(ResolvedType::Text),
1015 ),
1016 sig(
1017 "json_insert",
1018 Arity::Variadic(3),
1019 check_json_update,
1020 ReturnRule::Fixed(ResolvedType::Text),
1021 ),
1022 sig(
1023 "json_replace",
1024 Arity::Variadic(3),
1025 check_json_update,
1026 ReturnRule::Fixed(ResolvedType::Text),
1027 ),
1028 sig(
1029 "json_set",
1030 Arity::Variadic(3),
1031 check_json_update,
1032 ReturnRule::Fixed(ResolvedType::Text),
1033 ),
1034 sig(
1035 "json_remove",
1036 Arity::Variadic(2),
1037 check_text,
1038 ReturnRule::Fixed(ResolvedType::Text),
1039 ),
1040 sig(
1041 "json_array_length",
1042 Arity::Range(1, 2),
1043 check_text,
1044 ReturnRule::Fixed(ResolvedType::Integer),
1045 ),
1046 sig(
1047 "vector_similarity",
1048 Arity::Exact(3),
1049 check_vector_triplet,
1050 ReturnRule::Fixed(ResolvedType::Double),
1051 ),
1052 sig(
1053 "vector_distance",
1054 Arity::Exact(3),
1055 check_vector_triplet,
1056 ReturnRule::Fixed(ResolvedType::Double),
1057 ),
1058 sig(
1059 "vector_dims",
1060 Arity::Exact(1),
1061 check_vector_one,
1062 ReturnRule::Fixed(ResolvedType::Integer),
1063 ),
1064 sig(
1065 "vector_norm",
1066 Arity::Exact(1),
1067 check_vector_one,
1068 ReturnRule::Fixed(ResolvedType::Double),
1069 ),
1070 sig(
1071 "abs",
1072 Arity::Exact(1),
1073 check_numeric,
1074 ReturnRule::FromArgs(return_arg0_numeric),
1075 ),
1076 sig(
1077 "sign",
1078 Arity::Exact(1),
1079 check_numeric,
1080 ReturnRule::Fixed(ResolvedType::Integer),
1081 ),
1082 sig(
1083 "round",
1084 Arity::Range(1, 2),
1085 check_numeric,
1086 ReturnRule::FromArgs(return_arg0_numeric),
1087 ),
1088 sig(
1089 "floor",
1090 Arity::Exact(1),
1091 check_numeric,
1092 ReturnRule::FromArgs(return_arg0_numeric),
1093 ),
1094 sig(
1095 "ceil",
1096 Arity::Exact(1),
1097 check_numeric,
1098 ReturnRule::FromArgs(return_arg0_numeric),
1099 ),
1100 sig(
1101 "ceiling",
1102 Arity::Exact(1),
1103 check_numeric,
1104 ReturnRule::FromArgs(return_arg0_numeric),
1105 ),
1106 sig(
1107 "trunc",
1108 Arity::Range(1, 2),
1109 check_numeric,
1110 ReturnRule::FromArgs(return_arg0_numeric),
1111 ),
1112 sig(
1113 "mod",
1114 Arity::Exact(2),
1115 check_numeric,
1116 ReturnRule::FromArgs(return_numeric),
1117 ),
1118 sig(
1119 "power",
1120 Arity::Exact(2),
1121 check_numeric,
1122 ReturnRule::Fixed(ResolvedType::Double),
1123 ),
1124 sig(
1125 "pow",
1126 Arity::Exact(2),
1127 check_numeric,
1128 ReturnRule::Fixed(ResolvedType::Double),
1129 ),
1130 sig(
1131 "sqrt",
1132 Arity::Exact(1),
1133 check_numeric,
1134 ReturnRule::Fixed(ResolvedType::Double),
1135 ),
1136 sig(
1137 "exp",
1138 Arity::Exact(1),
1139 check_numeric,
1140 ReturnRule::Fixed(ResolvedType::Double),
1141 ),
1142 sig(
1143 "ln",
1144 Arity::Exact(1),
1145 check_numeric,
1146 ReturnRule::Fixed(ResolvedType::Double),
1147 ),
1148 sig(
1149 "log",
1150 Arity::Range(1, 2),
1151 check_numeric,
1152 ReturnRule::Fixed(ResolvedType::Double),
1153 ),
1154 sig(
1155 "log10",
1156 Arity::Exact(1),
1157 check_numeric,
1158 ReturnRule::Fixed(ResolvedType::Double),
1159 ),
1160 sig(
1161 "cbrt",
1162 Arity::Exact(1),
1163 check_numeric,
1164 ReturnRule::Fixed(ResolvedType::Double),
1165 ),
1166 sig(
1167 "cot",
1168 Arity::Exact(1),
1169 check_numeric,
1170 ReturnRule::Fixed(ResolvedType::Double),
1171 ),
1172 sig(
1173 "log2",
1174 Arity::Exact(1),
1175 check_numeric,
1176 ReturnRule::Fixed(ResolvedType::Double),
1177 ),
1178 sig(
1179 "acosh",
1180 Arity::Exact(1),
1181 check_numeric,
1182 ReturnRule::Fixed(ResolvedType::Double),
1183 ),
1184 sig(
1185 "asinh",
1186 Arity::Exact(1),
1187 check_numeric,
1188 ReturnRule::Fixed(ResolvedType::Double),
1189 ),
1190 sig(
1191 "atanh",
1192 Arity::Exact(1),
1193 check_numeric,
1194 ReturnRule::Fixed(ResolvedType::Double),
1195 ),
1196 sig(
1197 "cosh",
1198 Arity::Exact(1),
1199 check_numeric,
1200 ReturnRule::Fixed(ResolvedType::Double),
1201 ),
1202 sig(
1203 "sinh",
1204 Arity::Exact(1),
1205 check_numeric,
1206 ReturnRule::Fixed(ResolvedType::Double),
1207 ),
1208 sig(
1209 "tanh",
1210 Arity::Exact(1),
1211 check_numeric,
1212 ReturnRule::Fixed(ResolvedType::Double),
1213 ),
1214 sig(
1215 "isnan",
1216 Arity::Exact(1),
1217 check_numeric,
1218 ReturnRule::Fixed(ResolvedType::Boolean),
1219 ),
1220 sig_meta(
1221 "random",
1222 Arity::Exact(0),
1223 check_numeric,
1224 ReturnRule::Fixed(ResolvedType::Double),
1225 RANDOM_META,
1226 ),
1227 sig_meta(
1228 "now",
1229 Arity::Range(0, 1),
1230 check_numeric,
1231 ReturnRule::Fixed(ResolvedType::Timestamp),
1232 STATEMENT_STABLE_META,
1233 ),
1234 sig_meta(
1235 "current_timestamp",
1236 Arity::Range(0, 1),
1237 check_numeric,
1238 ReturnRule::Fixed(ResolvedType::Timestamp),
1239 STATEMENT_STABLE_META,
1240 ),
1241 sig_meta(
1242 "current_date",
1243 Arity::Exact(0),
1244 check_numeric,
1245 ReturnRule::Fixed(ResolvedType::Date),
1246 STATEMENT_STABLE_META,
1247 ),
1248 sig_meta(
1249 "current_time",
1250 Arity::Exact(0),
1251 check_numeric,
1252 ReturnRule::Fixed(ResolvedType::Time),
1253 STATEMENT_STABLE_META,
1254 ),
1255 sig(
1256 "make_date",
1257 Arity::Exact(3),
1258 check_numeric,
1259 ReturnRule::Fixed(ResolvedType::Date),
1260 ),
1261 sig(
1262 "make_time",
1263 Arity::Exact(3),
1264 check_numeric,
1265 ReturnRule::Fixed(ResolvedType::Time),
1266 ),
1267 sig(
1268 "make_timestamp",
1269 Arity::Exact(6),
1270 check_numeric,
1271 ReturnRule::Fixed(ResolvedType::Timestamp),
1272 ),
1273 sig(
1274 "make_interval",
1275 Arity::Range(0, 7),
1276 check_numeric,
1277 ReturnRule::Fixed(ResolvedType::Interval),
1278 ),
1279 sig(
1280 "date",
1281 Arity::Exact(1),
1282 check_temporal_input,
1283 ReturnRule::Fixed(ResolvedType::Date),
1284 ),
1285 sig(
1286 "time",
1287 Arity::Exact(1),
1288 check_temporal_input,
1289 ReturnRule::Fixed(ResolvedType::Time),
1290 ),
1291 sig(
1292 "datetime",
1293 Arity::Variadic(1),
1294 check_datetime,
1295 ReturnRule::Fixed(ResolvedType::Timestamp),
1296 ),
1297 sig(
1298 "to_date",
1299 Arity::Exact(2),
1300 check_text,
1301 ReturnRule::Fixed(ResolvedType::Date),
1302 ),
1303 sig(
1304 "age",
1305 Arity::Range(1, 2),
1306 check_age,
1307 ReturnRule::Fixed(ResolvedType::Interval),
1308 ),
1309 sig(
1310 "date_add",
1311 Arity::Exact(2),
1312 check_temporal_interval,
1313 ReturnRule::FromArgs(return_arg0),
1314 ),
1315 sig(
1316 "date_sub",
1317 Arity::Exact(2),
1318 check_temporal_interval,
1319 ReturnRule::FromArgs(return_arg0),
1320 ),
1321 sig(
1322 "extract",
1323 Arity::Exact(2),
1324 check_text_timestamp,
1325 ReturnRule::Fixed(ResolvedType::Double),
1326 ),
1327 sig(
1328 "date_part",
1329 Arity::Exact(2),
1330 check_text_timestamp,
1331 ReturnRule::Fixed(ResolvedType::Double),
1332 ),
1333 sig(
1334 "date_trunc",
1335 Arity::Exact(2),
1336 check_text_timestamp,
1337 ReturnRule::Fixed(ResolvedType::Timestamp),
1338 ),
1339 sig(
1340 "to_char",
1341 Arity::Exact(2),
1342 check_timestamp_text,
1343 ReturnRule::Fixed(ResolvedType::Text),
1344 ),
1345 sig(
1346 "to_timestamp",
1347 Arity::Range(1, 2),
1348 check_to_timestamp,
1349 ReturnRule::Fixed(ResolvedType::Timestamp),
1350 ),
1351 sig(
1352 "strftime",
1353 Arity::Exact(2),
1354 check_text_timestamp,
1355 ReturnRule::Fixed(ResolvedType::Text),
1356 ),
1357 sig(
1358 "julianday",
1359 Arity::Exact(1),
1360 check_timestamp,
1361 ReturnRule::Fixed(ResolvedType::Double),
1362 ),
1363 sig(
1364 "unixepoch",
1365 Arity::Exact(1),
1366 check_timestamp,
1367 ReturnRule::Fixed(ResolvedType::BigInt),
1368 ),
1369 sig(
1370 "sin",
1371 Arity::Exact(1),
1372 check_numeric,
1373 ReturnRule::Fixed(ResolvedType::Double),
1374 ),
1375 sig(
1376 "cos",
1377 Arity::Exact(1),
1378 check_numeric,
1379 ReturnRule::Fixed(ResolvedType::Double),
1380 ),
1381 sig(
1382 "tan",
1383 Arity::Exact(1),
1384 check_numeric,
1385 ReturnRule::Fixed(ResolvedType::Double),
1386 ),
1387 sig(
1388 "asin",
1389 Arity::Exact(1),
1390 check_numeric,
1391 ReturnRule::Fixed(ResolvedType::Double),
1392 ),
1393 sig(
1394 "acos",
1395 Arity::Exact(1),
1396 check_numeric,
1397 ReturnRule::Fixed(ResolvedType::Double),
1398 ),
1399 sig(
1400 "atan",
1401 Arity::Exact(1),
1402 check_numeric,
1403 ReturnRule::Fixed(ResolvedType::Double),
1404 ),
1405 sig(
1406 "atan2",
1407 Arity::Exact(2),
1408 check_numeric,
1409 ReturnRule::Fixed(ResolvedType::Double),
1410 ),
1411 sig(
1412 "degrees",
1413 Arity::Exact(1),
1414 check_numeric,
1415 ReturnRule::Fixed(ResolvedType::Double),
1416 ),
1417 sig(
1418 "radians",
1419 Arity::Exact(1),
1420 check_numeric,
1421 ReturnRule::Fixed(ResolvedType::Double),
1422 ),
1423 sig(
1424 "pi",
1425 Arity::Exact(0),
1426 check_numeric,
1427 ReturnRule::Fixed(ResolvedType::Double),
1428 ),
1429 sig(
1430 "sha256",
1431 Arity::Exact(1),
1432 check_text_or_blob,
1433 ReturnRule::Fixed(ResolvedType::Blob),
1434 ),
1435 sig(
1436 "md5",
1437 Arity::Exact(1),
1438 check_text_or_blob,
1439 ReturnRule::Fixed(ResolvedType::Text),
1440 ),
1441 sig(
1442 "simhash",
1443 Arity::Exact(1),
1444 check_text,
1445 ReturnRule::Fixed(ResolvedType::BigInt),
1446 ),
1447 sig(
1448 "hamming_distance",
1449 Arity::Exact(2),
1450 check_bigint,
1451 ReturnRule::Fixed(ResolvedType::Integer),
1452 ),
1453 sig_meta(
1454 "gen_random_uuid",
1455 Arity::Exact(0),
1456 check_no_args,
1457 ReturnRule::Fixed(ResolvedType::Text),
1458 RANDOM_META,
1459 ),
1460 sig_meta(
1461 "uuidv7",
1462 Arity::Exact(0),
1463 check_no_args,
1464 ReturnRule::Fixed(ResolvedType::Text),
1465 RANDOM_META,
1466 ),
1467 sig(
1468 "hex",
1469 Arity::Exact(1),
1470 check_text_or_blob,
1471 ReturnRule::Fixed(ResolvedType::Text),
1472 ),
1473 sig(
1474 "unhex",
1475 Arity::Exact(1),
1476 check_text,
1477 ReturnRule::Fixed(ResolvedType::Blob),
1478 ),
1479 sig(
1480 "encode",
1481 Arity::Exact(2),
1482 check_blob_text,
1483 ReturnRule::Fixed(ResolvedType::Text),
1484 ),
1485 sig(
1486 "decode",
1487 Arity::Exact(2),
1488 check_text,
1489 ReturnRule::Fixed(ResolvedType::Blob),
1490 ),
1491 sig(
1492 "length",
1493 Arity::Exact(1),
1494 check_text_or_blob,
1495 ReturnRule::Fixed(ResolvedType::Integer),
1496 ),
1497 sig(
1498 "char_length",
1499 Arity::Exact(1),
1500 check_text,
1501 ReturnRule::Fixed(ResolvedType::Integer),
1502 ),
1503 sig(
1504 "octet_length",
1505 Arity::Exact(1),
1506 check_text_or_blob,
1507 ReturnRule::Fixed(ResolvedType::Integer),
1508 ),
1509 sig(
1510 "ascii",
1511 Arity::Exact(1),
1512 check_text,
1513 ReturnRule::Fixed(ResolvedType::Integer),
1514 ),
1515 sig(
1516 "chr",
1517 Arity::Exact(1),
1518 check_integer,
1519 ReturnRule::Fixed(ResolvedType::Text),
1520 ),
1521 sig(
1522 "bit_length",
1523 Arity::Exact(1),
1524 check_text_or_blob,
1525 ReturnRule::Fixed(ResolvedType::Integer),
1526 ),
1527 sig(
1528 "starts_with",
1529 Arity::Exact(2),
1530 check_text,
1531 ReturnRule::Fixed(ResolvedType::Boolean),
1532 ),
1533 sig(
1534 "ends_with",
1535 Arity::Exact(2),
1536 check_text,
1537 ReturnRule::Fixed(ResolvedType::Boolean),
1538 ),
1539 sig(
1540 "translate",
1541 Arity::Exact(3),
1542 check_text,
1543 ReturnRule::Fixed(ResolvedType::Text),
1544 ),
1545 sig(
1546 "levenshtein",
1547 Arity::Exact(2),
1548 check_text,
1549 ReturnRule::Fixed(ResolvedType::Integer),
1550 ),
1551 sig(
1552 "upper",
1553 Arity::Exact(1),
1554 check_text,
1555 ReturnRule::Fixed(ResolvedType::Text),
1556 ),
1557 sig(
1558 "lower",
1559 Arity::Exact(1),
1560 check_text,
1561 ReturnRule::Fixed(ResolvedType::Text),
1562 ),
1563 sig(
1564 "initcap",
1565 Arity::Exact(1),
1566 check_text,
1567 ReturnRule::Fixed(ResolvedType::Text),
1568 ),
1569 sig(
1570 "substr",
1571 Arity::Range(2, 3),
1572 check_numeric_or_text_substr,
1573 ReturnRule::Fixed(ResolvedType::Text),
1574 ),
1575 sig(
1576 "left",
1577 Arity::Exact(2),
1578 check_text_numeric,
1579 ReturnRule::Fixed(ResolvedType::Text),
1580 ),
1581 sig(
1582 "right",
1583 Arity::Exact(2),
1584 check_text_numeric,
1585 ReturnRule::Fixed(ResolvedType::Text),
1586 ),
1587 sig(
1588 "trim",
1589 Arity::Range(1, 2),
1590 check_text,
1591 ReturnRule::Fixed(ResolvedType::Text),
1592 ),
1593 sig(
1594 "ltrim",
1595 Arity::Range(1, 2),
1596 check_text,
1597 ReturnRule::Fixed(ResolvedType::Text),
1598 ),
1599 sig(
1600 "rtrim",
1601 Arity::Range(1, 2),
1602 check_text,
1603 ReturnRule::Fixed(ResolvedType::Text),
1604 ),
1605 sig(
1606 "replace",
1607 Arity::Exact(3),
1608 check_text,
1609 ReturnRule::Fixed(ResolvedType::Text),
1610 ),
1611 sig(
1612 "instr",
1613 Arity::Exact(2),
1614 check_text,
1615 ReturnRule::Fixed(ResolvedType::Integer),
1616 ),
1617 sig(
1618 "strpos",
1619 Arity::Exact(2),
1620 check_text,
1621 ReturnRule::Fixed(ResolvedType::Integer),
1622 ),
1623 sig(
1624 "concat",
1625 Arity::Variadic(0),
1626 check_text,
1627 ReturnRule::Fixed(ResolvedType::Text),
1628 ),
1629 sig(
1630 "concat_ws",
1631 Arity::Variadic(1),
1632 check_text,
1633 ReturnRule::Fixed(ResolvedType::Text),
1634 ),
1635 sig(
1636 "repeat",
1637 Arity::Exact(2),
1638 check_text_numeric,
1639 ReturnRule::Fixed(ResolvedType::Text),
1640 ),
1641 sig(
1642 "reverse",
1643 Arity::Exact(1),
1644 check_text,
1645 ReturnRule::Fixed(ResolvedType::Text),
1646 ),
1647 sig(
1648 "lpad",
1649 Arity::Range(2, 3),
1650 check_pad,
1651 ReturnRule::Fixed(ResolvedType::Text),
1652 ),
1653 sig(
1654 "rpad",
1655 Arity::Range(2, 3),
1656 check_pad,
1657 ReturnRule::Fixed(ResolvedType::Text),
1658 ),
1659 sig(
1660 "split_part",
1661 Arity::Exact(3),
1662 check_text_text_numeric,
1663 ReturnRule::Fixed(ResolvedType::Text),
1664 ),
1665 sig(
1666 "regexp_replace",
1667 Arity::Exact(3),
1668 check_text,
1669 ReturnRule::Fixed(ResolvedType::Text),
1670 ),
1671 sig(
1672 "regexp_match",
1673 Arity::Exact(2),
1674 check_text,
1675 ReturnRule::Fixed(ResolvedType::Text),
1676 ),
1677 sig(
1678 "regexp_matches",
1679 Arity::Range(2, 3),
1680 check_text,
1681 ReturnRule::Fixed(ResolvedType::Text),
1682 ),
1683 sig(
1684 "regexp_like",
1685 Arity::Range(2, 3),
1686 check_text,
1687 ReturnRule::Fixed(ResolvedType::Boolean),
1688 ),
1689 sig(
1690 "coalesce",
1691 Arity::Variadic(1),
1692 check_compatible,
1693 ReturnRule::FromArgs(return_first_non_null),
1694 ),
1695 sig(
1696 "nullif",
1697 Arity::Exact(2),
1698 check_compatible,
1699 ReturnRule::FromArgs(return_first_non_null),
1700 ),
1701 sig(
1702 "ifnull",
1703 Arity::Exact(2),
1704 check_compatible,
1705 ReturnRule::FromArgs(return_first_non_null),
1706 ),
1707 sig(
1708 "iif",
1709 Arity::Exact(3),
1710 check_iif,
1711 ReturnRule::FromArgs(return_first_non_null),
1712 ),
1713 sig(
1714 "greatest",
1715 Arity::Variadic(1),
1716 check_numeric,
1717 ReturnRule::FromArgs(return_numeric),
1718 ),
1719 sig(
1720 "least",
1721 Arity::Variadic(1),
1722 check_numeric,
1723 ReturnRule::FromArgs(return_numeric),
1724 ),
1725 sig(
1726 "typeof",
1727 Arity::Exact(1),
1728 check_any,
1729 ReturnRule::Fixed(ResolvedType::Text),
1730 ),
1731 sig(
1732 "pg_typeof",
1733 Arity::Exact(1),
1734 check_any,
1735 ReturnRule::Fixed(ResolvedType::Text),
1736 ),
1737 sig(
1738 "quote",
1739 Arity::Exact(1),
1740 check_any,
1741 ReturnRule::Fixed(ResolvedType::Text),
1742 ),
1743 sig_meta(
1744 "memory_stats",
1745 Arity::Exact(0),
1746 check_no_args,
1747 ReturnRule::Fixed(ResolvedType::Text),
1748 SYSTEM_META,
1749 ),
1750 sig_meta(
1751 "io_stats",
1752 Arity::Exact(0),
1753 check_no_args,
1754 ReturnRule::Fixed(ResolvedType::Text),
1755 SYSTEM_META,
1756 ),
1757 sig_meta(
1758 "clear_cache",
1759 Arity::Exact(0),
1760 check_no_args,
1761 ReturnRule::Fixed(ResolvedType::BigInt),
1762 SYSTEM_SIDE_EFFECT_META,
1763 ),
1764];
1765
1766fn check_numeric_or_text_substr(args: &[TypedExpr]) -> Result<(), PlannerError> {
1767 if args.is_empty() {
1768 return Ok(());
1769 }
1770 check_text(&args[..1])?;
1771 check_numeric(&args[1..])
1772}
1773
1774fn check_text_numeric(args: &[TypedExpr]) -> Result<(), PlannerError> {
1775 if !args.is_empty() {
1776 check_text(&args[..1])?;
1777 }
1778 if args.len() >= 2 {
1779 check_numeric(&args[1..2])?;
1780 }
1781 Ok(())
1782}
1783
1784fn check_pad(args: &[TypedExpr]) -> Result<(), PlannerError> {
1785 check_text_numeric(&args[..2])?;
1786 if args.len() == 3 {
1787 check_text(&args[2..3])?;
1788 }
1789 Ok(())
1790}
1791
1792fn check_text_text_numeric(args: &[TypedExpr]) -> Result<(), PlannerError> {
1793 check_text(&args[..2])?;
1794 check_numeric(&args[2..3])
1795}
1796
1797fn check_compatible(args: &[TypedExpr]) -> Result<(), PlannerError> {
1798 let mut expected: Option<&ResolvedType> = None;
1799 for arg in args {
1800 if matches!(arg.resolved_type, ResolvedType::Null) {
1801 continue;
1802 }
1803 if let Some(first) = expected {
1804 if !(is_numeric(first) && is_numeric(&arg.resolved_type)) && first != &arg.resolved_type
1805 {
1806 return Err(PlannerError::type_mismatch(
1807 first.type_name(),
1808 arg.resolved_type.type_name(),
1809 arg.span,
1810 ));
1811 }
1812 } else {
1813 expected = Some(&arg.resolved_type);
1814 }
1815 }
1816 Ok(())
1817}
1818
1819fn check_iif(args: &[TypedExpr]) -> Result<(), PlannerError> {
1820 if !matches!(
1821 args.first().map(|a| &a.resolved_type),
1822 Some(ResolvedType::Boolean | ResolvedType::Null)
1823 ) {
1824 let arg = args
1825 .first()
1826 .expect("arity is validated before type checking");
1827 return Err(PlannerError::type_mismatch(
1828 "Boolean",
1829 arg.resolved_type.type_name(),
1830 arg.span,
1831 ));
1832 }
1833 check_compatible(&args[1..])
1834}
1835
1836pub fn signatures() -> &'static [ScalarSignature] {
1837 SIGNATURES
1838}
1839
1840pub fn signature(name: &str) -> Option<&'static ScalarSignature> {
1841 let lower = name.to_ascii_lowercase();
1842 SIGNATURES.iter().find(|sig| sig.name == lower)
1843}
1844
1845pub fn is_numeric_type(ty: &ResolvedType) -> bool {
1846 is_numeric(ty)
1847}
1848
1849#[cfg(test)]
1850mod tests {
1851 use super::*;
1852
1853 #[test]
1854 fn names_are_normalized_and_unique() {
1855 let mut names: Vec<_> = signatures().iter().map(|s| s.name).collect();
1856 names.sort_unstable();
1857 names.dedup();
1858 assert_eq!(names.len(), signatures().len());
1859 assert!(
1860 signatures()
1861 .iter()
1862 .all(|s| s.name == s.name.to_ascii_lowercase())
1863 );
1864 }
1865
1866 #[test]
1867 fn random_metadata_is_volatile() {
1868 let random = signature("RANDOM").unwrap();
1869 assert!(!random.meta.deterministic);
1870 assert!(random.meta.volatile);
1871 assert!(!random.meta.foldable);
1872 assert!(!random.meta.cacheable);
1873 }
1874
1875 #[test]
1876 fn scalar_names_do_not_overlap_aggregate_names() {
1877 let aggregates = [
1878 "count",
1879 "sum",
1880 "total",
1881 "avg",
1882 "min",
1883 "max",
1884 "group_concat",
1885 "string_agg",
1886 ];
1887 assert!(aggregates.iter().all(|name| signature(name).is_none()));
1888 }
1889}