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