Skip to main content

uqa_sql/routines/
resolution.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Static routine signature matching, invocation binding, and declared return-type resolution.
8
9mod 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
31/// The original immutable domain allocation captured for one signature-matching pass.
32pub 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        &parameters,
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, &parameter_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(&parameter.type_name),
486            column_type: declared_parameter_type(catalog, &parameter.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        &parameters,
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    // Enum parameters match by identity alone; a domain parameter also needs its base type.
507    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(&parameter.type_name))
561                .unwrap_or_else(|| canonical_routine_type_name(&parameter.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
778/// Defaults do not exclude or rank an overload. Their polymorphic consistency is
779/// checked only after the ordinary supplied-argument rules choose one candidate.
780pub(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    // An actual anyarray supplies no element identity. Its sole-input exception
790    // permits an anyarray result, but cannot specialize an anyelement result.
791    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}