1mod combined_overloads;
10
11use super::{
12 declaration::RoutineTypeCatalog, routine_signature_types, SQLUserFunction, StaticFunctionMatch,
13};
14use crate::type_resolution::{
15 canonical_routine_type_name, match_routine_candidate, rank_function_matches,
16 BuiltinFunctionOverload, FunctionTypeResolver, MatchedRoutineSignature,
17 ResolvedFunctionOverload, RoutineCallDescriptor, RoutineParameterDescriptor,
18 RoutineSignatureMatchError,
19};
20use crate::{
21 ast::{
22 ColumnType, CreateFunction, FunctionBinding, FunctionParamMode, FunctionReturns,
23 RoutineInvocationBinding, RoutineVariadicMode,
24 },
25 catalog::domain::StoredDomain,
26 SQLError,
27};
28use std::{collections::BTreeMap, sync::Arc};
29use uqa_core::RelationIdentity;
30
31pub type RoutineTypeSnapshot = Arc<BTreeMap<String, StoredDomain>>;
33pub trait RoutineOverloadCatalog: RoutineTypeCatalog + Send + Sync {
34 fn routine_type_snapshot(&self) -> RoutineTypeSnapshot;
35 fn routine_search_path(&self) -> Vec<String>;
36 fn has_registered_scalar_function(&self, name: &str) -> bool;
37 fn lookup_sql_routine_candidates(
38 &self,
39 name: &str,
40 ) -> Result<Option<Vec<Arc<SQLUserFunction>>>, SQLError>;
41 fn lookup_bound_sql_routine_candidates_by_binding(
42 &self,
43 binding: &FunctionBinding,
44 ) -> Option<Vec<Arc<SQLUserFunction>>>;
45 fn lookup_bound_sql_functions_by_binding(
46 &self,
47 binding: &FunctionBinding,
48 ) -> Option<Vec<Arc<SQLUserFunction>>>;
49}
50pub struct RoutineOverloadContext<'a> {
51 pub catalog: &'a dyn RoutineOverloadCatalog,
52}
53
54impl FunctionTypeResolver for RoutineOverloadContext<'_> {
55 fn has_untyped_function(&self, name: &str) -> bool {
56 self.catalog.has_registered_scalar_function(name)
57 }
58
59 fn resolve_type_name(&self, name: &str) -> Result<Option<ColumnType>, SQLError> {
60 self.catalog
61 .resolve_catalog_column_type_name(name)
62 .map(Some)
63 }
64
65 fn require_type_usage(&self, ty: &ColumnType) -> Result<(), SQLError> {
66 self.catalog.require_type_usage(ty)
67 }
68
69 fn resolve_function_type(
70 &self,
71 name: &str,
72 binding: Option<&FunctionBinding>,
73 argument_names: &[Option<String>],
74 argument_types: &[Option<ColumnType>],
75 explicit_variadic: bool,
76 ) -> Result<Option<ColumnType>, SQLError> {
77 self.resolve_function_overload(
78 name,
79 binding,
80 argument_names,
81 argument_types,
82 explicit_variadic,
83 )
84 .map(|resolved| resolved.map(|resolved| resolved.return_type))
85 }
86
87 fn resolve_function_overload(
88 &self,
89 name: &str,
90 binding: Option<&FunctionBinding>,
91 argument_names: &[Option<String>],
92 argument_types: &[Option<ColumnType>],
93 explicit_variadic: bool,
94 ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
95 let Some(matched) = self.resolve_static_sql_routine_match(
96 name,
97 binding,
98 argument_names,
99 argument_types,
100 explicit_variadic,
101 RoutineCallKind::Function,
102 )?
103 else {
104 return Ok(None);
105 };
106 let function = &matched.function;
107 Ok(Some(ResolvedFunctionOverload {
108 binding: matched.binding(),
109 return_type: static_function_return_type(
110 self,
111 name,
112 &function.def,
113 Some(&matched.invocation),
114 )?,
115 exact_matches: matched.exact_matches,
116 known_arguments: argument_types.iter().flatten().count(),
117 preferred_matches: matched.preferred_matches,
118 precedes_pg_catalog: self.user_function_precedes_pg_catalog(&function.def.name),
119 }))
120 }
121
122 fn is_scalar_function_binding(&self, binding: &FunctionBinding) -> Result<bool, SQLError> {
123 if binding.builtin {
124 return Ok(false);
125 }
126 let function = self
127 .catalog
128 .lookup_bound_sql_functions_by_binding(binding)
129 .and_then(|overloads| {
130 overloads.into_iter().find(|function| {
131 !function.def.is_procedure
132 && routine_signature_types(&function.def) == binding.argument_types
133 })
134 });
135 Ok(function.is_some_and(|function| !function.def.returns_set()))
136 }
137
138 fn resolve_function_overload_with_builtins(
139 &self,
140 name: &str,
141 binding: Option<&FunctionBinding>,
142 argument_names: &[Option<String>],
143 argument_types: &[Option<ColumnType>],
144 explicit_variadic: bool,
145 builtins: &[BuiltinFunctionOverload],
146 ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
147 combined_overloads::resolve(
148 self,
149 name,
150 binding,
151 argument_names,
152 argument_types,
153 explicit_variadic,
154 builtins,
155 )
156 .map(Some)
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum RoutineCallKind {
162 Function,
163 Procedure,
164}
165
166impl RoutineCallKind {
167 fn is_procedure(self) -> bool {
168 self == Self::Procedure
169 }
170
171 fn name(self) -> &'static str {
172 match self {
173 Self::Function => "function",
174 Self::Procedure => "procedure",
175 }
176 }
177}
178
179impl RoutineOverloadContext<'_> {
180 pub(super) fn user_function_precedes_pg_catalog(&self, name: &str) -> bool {
181 let Ok((Some(schema), _)) = RelationIdentity::parse_reference(name) else {
182 return false;
183 };
184 let search_path = self.catalog.routine_search_path();
185 let Some(user_position) = search_path.iter().position(|entry| entry == &schema) else {
186 return false;
187 };
188 search_path
189 .iter()
190 .position(|entry| entry == "pg_catalog")
191 .is_some_and(|catalog_position| user_position < catalog_position)
192 }
193
194 pub fn resolve_static_sql_function(
195 &self,
196 name: &str,
197 binding: Option<&FunctionBinding>,
198 argument_names: &[Option<String>],
199 argument_types: &[Option<ColumnType>],
200 explicit_variadic: bool,
201 ) -> Result<Option<Arc<SQLUserFunction>>, SQLError> {
202 self.resolve_static_sql_function_match(
203 name,
204 binding,
205 argument_names,
206 argument_types,
207 explicit_variadic,
208 )
209 .map(|matched| matched.map(|matched| matched.function))
210 }
211
212 pub fn resolve_static_sql_function_match(
213 &self,
214 name: &str,
215 binding: Option<&FunctionBinding>,
216 argument_names: &[Option<String>],
217 argument_types: &[Option<ColumnType>],
218 explicit_variadic: bool,
219 ) -> Result<Option<StaticFunctionMatch>, SQLError> {
220 self.resolve_static_sql_routine_match(
221 name,
222 binding,
223 argument_names,
224 argument_types,
225 explicit_variadic,
226 RoutineCallKind::Function,
227 )
228 }
229
230 pub fn resolve_table_function_overload_with_builtins(
231 &self,
232 name: &str,
233 binding: Option<&FunctionBinding>,
234 argument_names: &[Option<String>],
235 argument_types: &[Option<ColumnType>],
236 explicit_variadic: bool,
237 builtins: &[BuiltinFunctionOverload],
238 ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
239 combined_overloads::resolve_table(
240 self,
241 name,
242 binding,
243 argument_names,
244 argument_types,
245 explicit_variadic,
246 builtins,
247 )
248 .map(Some)
249 }
250
251 pub fn resolve_static_sql_routine_match(
252 &self,
253 name: &str,
254 binding: Option<&FunctionBinding>,
255 argument_names: &[Option<String>],
256 argument_types: &[Option<ColumnType>],
257 explicit_variadic: bool,
258 kind: RoutineCallKind,
259 ) -> Result<Option<StaticFunctionMatch>, SQLError> {
260 if let Some(binding) = binding {
261 if binding.builtin {
262 return Ok(None);
263 }
264 let function = self
265 .catalog
266 .lookup_bound_sql_routine_candidates_by_binding(binding)
267 .and_then(|overloads| {
268 overloads.into_iter().find(|function| {
269 function.def.is_procedure == kind.is_procedure()
270 && routine_signature_types(&function.def) == binding.argument_types
271 })
272 })
273 .ok_or_else(|| static_bound_routine_error(kind, binding))?;
274 let matched = if let Some(invocation) = &binding.invocation {
275 let invocation_is_explicit = matches!(
276 invocation.variadic_mode,
277 RoutineVariadicMode::Explicit { .. }
278 );
279 if invocation.argument_positions.len() != argument_types.len()
280 || invocation_is_explicit != explicit_variadic
281 {
282 return Err(static_bound_routine_error(kind, binding));
283 }
284 StaticFunctionMatch {
285 function,
286 default_error: None,
287 invocation: invocation.clone(),
288 argument_types: invocation.argument_targets.clone(),
289 raw_exact_matches: 0,
290 exact_matches: 0,
291 preferred_matches: 0,
292 variadic_expansion: matches!(
293 invocation.variadic_mode,
294 RoutineVariadicMode::Expanded { .. }
295 ),
296 }
297 } else {
298 static_routine_match(
299 &self.catalog.routine_type_snapshot(),
300 function,
301 argument_names,
302 argument_types,
303 explicit_variadic,
304 kind,
305 )
306 .map_err(|error| static_signature_error(kind, name, error))?
307 .ok_or_else(|| static_bound_routine_error(kind, binding))?
308 };
309 ensure_routine_kind(
310 name,
311 argument_names,
312 argument_types,
313 kind,
314 &matched.function.def,
315 )?;
316 check_selected_default_types(self.catalog, kind, name, &matched)?;
317 return Ok(Some(matched));
318 }
319 let Some(overloads) = self.catalog.lookup_sql_routine_candidates(name)? else {
320 return Ok(None);
321 };
322 resolve_static_routine_overload(
323 self.catalog,
324 name,
325 overloads,
326 argument_names,
327 argument_types,
328 explicit_variadic,
329 kind,
330 )
331 .map(Some)
332 }
333}
334
335fn resolve_static_routine_overload(
336 catalog: &dyn RoutineOverloadCatalog,
337 name: &str,
338 overloads: Vec<Arc<SQLUserFunction>>,
339 argument_names: &[Option<String>],
340 argument_types: &[Option<ColumnType>],
341 explicit_variadic: bool,
342 kind: RoutineCallKind,
343) -> Result<StaticFunctionMatch, SQLError> {
344 let snapshot = catalog.routine_type_snapshot();
345 let mut candidates = Vec::new();
346 let mut match_error = None;
347 for function in overloads {
348 match static_routine_match(
349 &snapshot,
350 function,
351 argument_names,
352 argument_types,
353 explicit_variadic,
354 kind,
355 ) {
356 Ok(Some(candidate)) => candidates.push(candidate),
357 Ok(None) => {}
358 Err(error) => {
359 match_error.get_or_insert(error);
360 }
361 }
362 }
363 retain_earliest_effective_signatures(&mut candidates);
364 if candidates.is_empty() {
365 if let Some(error) = match_error {
366 return Err(static_signature_error(kind, name, error));
367 }
368 return Err(static_routine_resolution_error(
369 kind,
370 "42883",
371 name,
372 argument_types,
373 "does not exist",
374 ));
375 }
376
377 if !rank_function_matches(&mut candidates, argument_types) || candidates.len() != 1 {
378 return Err(static_routine_resolution_error(
379 kind,
380 "42725",
381 name,
382 argument_types,
383 "is not unique",
384 ));
385 }
386 let matched = candidates
387 .pop()
388 .ok_or_else(|| SQLError::Internal("resolved routine candidate disappeared".into()))?;
389 ensure_routine_kind(
390 name,
391 argument_names,
392 argument_types,
393 kind,
394 &matched.function.def,
395 )?;
396 check_selected_default_types(catalog, kind, name, &matched)?;
397 Ok(matched)
398}
399
400pub(super) fn retain_earliest_effective_signatures(candidates: &mut Vec<StaticFunctionMatch>) {
401 let mut visible = Vec::<(Vec<String>, String)>::new();
402 candidates.retain(|candidate| {
403 let schema = RelationIdentity::parse_reference(&candidate.function.def.name)
404 .ok()
405 .and_then(|(schema, _)| schema)
406 .unwrap_or_default();
407 if let Some((_, first_schema)) = visible
408 .iter()
409 .find(|(signature, _)| signature == &candidate.argument_types)
410 {
411 return first_schema == &schema;
412 }
413 visible.push((candidate.argument_types.clone(), schema));
414 true
415 });
416}
417
418fn static_routine_match(
419 catalog: &RoutineTypeSnapshot,
420 function: Arc<SQLUserFunction>,
421 argument_names: &[Option<String>],
422 argument_types: &[Option<ColumnType>],
423 explicit_variadic: bool,
424 kind: RoutineCallKind,
425) -> Result<Option<StaticFunctionMatch>, RoutineSignatureMatchError> {
426 let parameter_indices = routine_call_parameter_indices(&function.def, kind);
427 let parameters = parameter_indices
428 .iter()
429 .map(|index| &function.def.params[*index])
430 .collect::<Vec<_>>();
431 let Some(matched) = match_static_function_signature(
432 catalog,
433 ¶meters,
434 argument_names,
435 argument_types,
436 explicit_variadic,
437 )?
438 else {
439 return Ok(None);
440 };
441 let invocation = routine_invocation_binding(&function.def, ¶meter_indices, &matched);
442 Ok(Some(StaticFunctionMatch {
443 function,
444 argument_types: matched.argument_signature,
445 default_error: matched.default_error,
446 raw_exact_matches: matched.raw_exact_matches,
447 exact_matches: matched.exact_matches,
448 preferred_matches: matched.preferred_matches,
449 variadic_expansion: matches!(
450 matched.variadic_mode,
451 crate::type_resolution::RoutineVariadicMode::Pack
452 ),
453 invocation: Box::new(invocation),
454 }))
455}
456
457pub(super) fn static_function_match(
458 catalog: &RoutineTypeSnapshot,
459 function: Arc<SQLUserFunction>,
460 argument_names: &[Option<String>],
461 argument_types: &[Option<ColumnType>],
462 explicit_variadic: bool,
463) -> Result<Option<StaticFunctionMatch>, RoutineSignatureMatchError> {
464 static_routine_match(
465 catalog,
466 function,
467 argument_names,
468 argument_types,
469 explicit_variadic,
470 RoutineCallKind::Function,
471 )
472}
473
474fn match_static_function_signature(
475 catalog: &RoutineTypeSnapshot,
476 signature: &[&crate::ast::FunctionParam],
477 argument_names: &[Option<String>],
478 argument_types: &[Option<ColumnType>],
479 explicit_variadic: bool,
480) -> Result<Option<MatchedRoutineSignature>, RoutineSignatureMatchError> {
481 let parameters = signature
482 .iter()
483 .map(|parameter| RoutineParameterDescriptor {
484 name: Some(parameter.name.clone()),
485 type_name: canonical_routine_type_name(¶meter.type_name),
486 column_type: declared_parameter_type(catalog, ¶meter.type_name),
487 has_default: parameter.default.is_some(),
488 default_type: parameter.default_type.clone(),
489 variadic: parameter.mode == FunctionParamMode::Variadic,
490 })
491 .collect::<Vec<_>>();
492 match_routine_candidate(
493 ¶meters,
494 RoutineCallDescriptor {
495 argument_names,
496 argument_types,
497 explicit_variadic,
498 },
499 )
500}
501
502fn declared_parameter_type(catalog: &RoutineTypeSnapshot, type_name: &str) -> Option<ColumnType> {
503 if let Some(element) = type_name.strip_suffix("[]") {
504 return declared_parameter_type(catalog, element).map(|ty| ColumnType::Array(Box::new(ty)));
505 }
506 if let Some(identity) = crate::ast::UserTypeIdentity::parse(type_name) {
508 return (identity.kind == crate::ast::UserTypeKind::Domain)
509 .then(|| {
510 catalog
511 .values()
512 .find(|domain| domain.oid == identity.oid)
513 .map(StoredDomain::column_type)
514 .or_else(|| crate::catalog::system_catalog_domain(identity.oid))
515 })
516 .flatten();
517 }
518 ColumnType::from_sql_name(type_name).ok().or_else(|| {
519 catalog.values().map(StoredDomain::column_type).find(|ty| {
520 canonical_routine_type_name(&ty.sql_name()) == canonical_routine_type_name(type_name)
521 })
522 })
523}
524
525fn routine_call_parameter_indices(def: &CreateFunction, kind: RoutineCallKind) -> Vec<usize> {
526 def.params
527 .iter()
528 .enumerate()
529 .filter_map(|(index, parameter)| {
530 let participates = if kind == RoutineCallKind::Procedure && !def.is_procedure {
531 true
532 } else {
533 match parameter.mode {
534 FunctionParamMode::In
535 | FunctionParamMode::InOut
536 | FunctionParamMode::Variadic => true,
537 FunctionParamMode::Out => def.is_procedure,
538 FunctionParamMode::Table => false,
539 }
540 };
541 participates.then_some(index)
542 })
543 .collect()
544}
545
546fn routine_invocation_binding(
547 def: &CreateFunction,
548 parameter_indices: &[usize],
549 matched: &MatchedRoutineSignature,
550) -> RoutineInvocationBinding {
551 let parameter_types = def
552 .params
553 .iter()
554 .enumerate()
555 .map(|(definition_index, parameter)| {
556 parameter_indices
557 .iter()
558 .position(|index| *index == definition_index)
559 .and_then(|call_index| matched.parameter_types.get(call_index).cloned())
560 .or_else(|| matched.substitute_type_name(¶meter.type_name))
561 .unwrap_or_else(|| canonical_routine_type_name(¶meter.type_name))
562 })
563 .collect::<Vec<_>>();
564 let argument_positions = matched
565 .argument_positions
566 .iter()
567 .map(|call_index| parameter_indices[*call_index])
568 .collect();
569 let variadic_mode = match &matched.variadic_plan {
570 crate::type_resolution::RoutineVariadicPlan::Pack {
571 parameter_index, ..
572 } => RoutineVariadicMode::Expanded {
573 parameter_index: parameter_indices[*parameter_index],
574 },
575 crate::type_resolution::RoutineVariadicPlan::PassThrough {
576 parameter_index, ..
577 } => RoutineVariadicMode::Explicit {
578 parameter_index: parameter_indices[*parameter_index],
579 },
580 crate::type_resolution::RoutineVariadicPlan::None
581 | crate::type_resolution::RoutineVariadicPlan::Default { .. } => RoutineVariadicMode::None,
582 };
583 let output_indices = def
584 .params
585 .iter()
586 .enumerate()
587 .filter_map(|(index, parameter)| {
588 matches!(
589 parameter.mode,
590 FunctionParamMode::Out | FunctionParamMode::InOut | FunctionParamMode::Table
591 )
592 .then_some(index)
593 })
594 .collect::<Vec<_>>();
595 let return_type = match &def.returns {
596 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name } => matched
597 .substitute_type_name(type_name)
598 .or_else(|| Some(canonical_routine_type_name(type_name))),
599 FunctionReturns::Table => Some("record".into()),
600 FunctionReturns::None => match output_indices.as_slice() {
601 [] => None,
602 [index] => parameter_types.get(*index).cloned(),
603 _ => Some("record".into()),
604 },
605 };
606 RoutineInvocationBinding {
607 argument_positions,
608 argument_targets: matched.argument_targets.clone(),
609 argument_sources: matched.argument_sources.clone(),
610 parameter_types,
611 return_type,
612 variadic_mode,
613 }
614}
615
616pub(super) fn static_signature_error(
617 _kind: RoutineCallKind,
618 _name: &str,
619 error: RoutineSignatureMatchError,
620) -> SQLError {
621 let sqlstate = error.sqlstate().to_string();
622 let message = match error {
623 RoutineSignatureMatchError::InvalidVariadicSignature { reason } => reason,
624 RoutineSignatureMatchError::IndeterminatePolymorphicType { .. } => {
625 "could not determine polymorphic type because input has type unknown".into()
626 }
627 _ => unreachable!("default consistency is checked after candidate selection"),
628 };
629 SQLError::Routine { sqlstate, message }
630}
631
632pub(super) fn static_function_return_type(
633 resolver: &RoutineOverloadContext<'_>,
634 name: &str,
635 def: &CreateFunction,
636 invocation: Option<&RoutineInvocationBinding>,
637) -> Result<ColumnType, SQLError> {
638 let invocation_return = invocation.and_then(|invocation| invocation.return_type.as_deref());
639 let declared_return = match &def.returns {
640 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name } => {
641 Some(type_name.as_str())
642 }
643 FunctionReturns::Table | FunctionReturns::None => None,
644 };
645 if invocation_return
646 .or(declared_return)
647 .is_some_and(|type_name| canonical_routine_type_name(type_name) == "trigger")
648 {
649 return Err(SQLError::Routine {
650 sqlstate: "0A000".into(),
651 message: "trigger functions can only be called as triggers".into(),
652 });
653 }
654 if matches!(def.returns, FunctionReturns::Table) || def.output_params().len() > 1 {
655 return Ok(ColumnType::Record);
656 }
657 if let Some(type_name) = invocation_return {
658 return resolver
659 .catalog
660 .resolve_catalog_column_type(type_name)
661 .or_else(|| ColumnType::from_sql_name(type_name).ok())
662 .ok_or_else(|| {
663 SQLError::TypeMismatch(format!(
664 "function `{name}` has unresolved return type `{type_name}`"
665 ))
666 });
667 }
668 let type_name = match &def.returns {
669 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name } => type_name,
670 FunctionReturns::None => {
671 let outputs = def.output_params();
672 if outputs.len() > 1 {
673 return Ok(ColumnType::Record);
674 }
675 &outputs
676 .first()
677 .ok_or_else(|| {
678 SQLError::TypeMismatch(format!("function `{name}` does not return a value"))
679 })?
680 .type_name
681 }
682 FunctionReturns::Table => unreachable!("table result handled above"),
683 };
684 resolver
685 .catalog
686 .resolve_catalog_column_type(type_name)
687 .or_else(|| ColumnType::from_sql_name(type_name).ok())
688 .ok_or_else(|| SQLError::TypeMismatch(format!("unknown type `{type_name}`")))
689}
690
691fn ensure_routine_kind(
692 name: &str,
693 argument_names: &[Option<String>],
694 argument_types: &[Option<ColumnType>],
695 expected: RoutineCallKind,
696 definition: &CreateFunction,
697) -> Result<(), SQLError> {
698 if definition.is_procedure == expected.is_procedure() {
699 return Ok(());
700 }
701 let arguments = static_routine_argument_types(argument_names, argument_types);
702 let suffix = if definition.is_procedure {
703 "is a procedure"
704 } else {
705 "is not a procedure"
706 };
707 Err(SQLError::Diagnostic {
708 sqlstate: "42809".into(),
709 message: format!("{name}({arguments}) {suffix}"),
710 detail: None,
711 hint: Some(if definition.is_procedure {
712 "To call a procedure, use CALL.".into()
713 } else {
714 "To call a function, use SELECT.".into()
715 }),
716 })
717}
718
719fn static_bound_routine_error(kind: RoutineCallKind, binding: &FunctionBinding) -> SQLError {
720 SQLError::Routine {
721 sqlstate: "42883".into(),
722 message: format!(
723 "bound {} {}({}) does not exist",
724 kind.name(),
725 binding.name,
726 binding.argument_types.join(", ")
727 ),
728 }
729}
730
731fn static_routine_resolution_error(
732 kind: RoutineCallKind,
733 sqlstate: &str,
734 name: &str,
735 argument_types: &[Option<ColumnType>],
736 suffix: &str,
737) -> SQLError {
738 let arguments = static_routine_argument_types(&[], argument_types);
739 let hint = if sqlstate == "42725" {
740 format!(
741 "Could not choose a best candidate {}. You might need to add explicit type casts.",
742 kind.name()
743 )
744 } else {
745 format!(
746 "No {} matches the given name and argument types. You might need to add explicit type casts.",
747 kind.name()
748 )
749 };
750 SQLError::Diagnostic {
751 sqlstate: sqlstate.into(),
752 message: format!("{} {name}({arguments}) {suffix}", kind.name()),
753 detail: None,
754 hint: Some(hint),
755 }
756}
757
758fn static_routine_argument_types(
759 argument_names: &[Option<String>],
760 argument_types: &[Option<ColumnType>],
761) -> String {
762 argument_types
763 .iter()
764 .enumerate()
765 .map(|(index, ty)| {
766 let ty = ty
767 .as_ref()
768 .map_or_else(|| "unknown".into(), ColumnType::regtype_name);
769 argument_names
770 .get(index)
771 .and_then(Option::as_ref)
772 .map_or_else(|| ty.clone(), |name| format!("{name} => {ty}"))
773 })
774 .collect::<Vec<_>>()
775 .join(", ")
776}
777
778pub(super) fn check_selected_default_types(
781 catalog: &dyn RoutineTypeCatalog,
782 _kind: RoutineCallKind,
783 _name: &str,
784 matched: &StaticFunctionMatch,
785) -> Result<(), SQLError> {
786 if let Some(error) = &matched.default_error {
787 return Err(default_type_error(catalog, error)?);
788 }
789 if matched
792 .invocation
793 .parameter_types
794 .iter()
795 .any(|ty| ty == "anyarray")
796 && matched.invocation.return_type.as_deref().is_some_and(|ty| {
797 matches!(
798 canonical_routine_type_name(ty).as_str(),
799 "anyelement" | "anynonarray" | "anyenum"
800 )
801 })
802 {
803 return Err(default_type_error(
804 catalog,
805 &RoutineSignatureMatchError::IndeterminateArrayElement,
806 )?);
807 }
808 Ok(())
809}
810
811fn default_type_error(
812 catalog: &dyn RoutineTypeCatalog,
813 error: &RoutineSignatureMatchError,
814) -> Result<SQLError, SQLError> {
815 use RoutineSignatureMatchError as E;
816 let format = |actual: &crate::ast::RoutineDefaultType| match actual {
817 crate::ast::RoutineDefaultType::Concrete(ty) => catalog.format_type(ty),
818 crate::ast::RoutineDefaultType::Polymorphic(name) => Ok(name.clone()),
819 };
820 let mut detail = None;
821 let message = match error {
822 E::InconsistentDefault {
823 declared,
824 first,
825 second,
826 } => {
827 detail = Some(format!("{} versus {}", format(first)?, format(second)?));
828 format!("arguments declared \"{declared}\" are not all alike")
829 }
830 E::InconsistentPolymorphicTypes {
831 first_declared,
832 second_declared,
833 first,
834 second,
835 } => {
836 detail = Some(format!("{} versus {}", format(first)?, format(second)?));
837 format!("argument declared {first_declared} is not consistent with argument declared {second_declared}")
838 }
839 E::InvalidPolymorphicActual { declared, actual } => {
840 let kind = if declared.ends_with("multirange") {
841 "a multirange type"
842 } else if declared.ends_with("range") {
843 "a range type"
844 } else {
845 "an array"
846 };
847 format!(
848 "argument declared {declared} is not {kind} but type {}",
849 format(actual)?
850 )
851 }
852 E::IncompatibleDefaultTypes { first, second } => format!(
853 "argument types {} and {} cannot be matched",
854 catalog.format_type(first)?,
855 catalog.format_type(second)?
856 ),
857 E::IncompatibleRangeSubtype {
858 declared,
859 range,
860 common,
861 } => format!(
862 "{declared} type {} does not match anycompatible type {}",
863 catalog.format_type(range)?,
864 catalog.format_type(common)?
865 ),
866 E::InvalidPolymorphicElement { declared, actual } => {
867 let shape = if declared == "anyenum" {
868 "not an enum type"
869 } else {
870 "an array type"
871 };
872 format!(
873 "type matched to {declared} is {shape}: {}",
874 catalog.format_type(actual)?
875 )
876 }
877 E::IndeterminatePolymorphicArgument { declared } => format!(
878 "could not determine polymorphic type {declared} because input has type unknown"
879 ),
880 E::IndeterminateArrayElement => {
881 "cannot determine element type of \"anyarray\" argument".into()
882 }
883 E::IndeterminatePolymorphicType { .. } => {
884 "could not determine polymorphic type because input has type unknown".into()
885 }
886 E::InvalidVariadicSignature { reason } => reason.clone(),
887 };
888 Ok(SQLError::Diagnostic {
889 sqlstate: error.sqlstate().into(),
890 message,
891 detail,
892 hint: None,
893 })
894}