uqa_sql/catalog/node_tree/expressions/
routines.rs1use super::{ColumnType, RoutineIdentity, SQLError};
10use crate::ast::RoutineVariadicMode;
11use crate::type_resolution::{
12 function_resolution_error, match_routine_signature, rank_function_matches,
13 MatchedRoutineSignature, RankedFunctionMatch, RoutineCallDescriptor,
14 RoutineParameterDescriptor,
15};
16
17pub struct RoutineDeclaration {
18 pub oid: i64,
19 pub parameters: Vec<RoutineParameterDescriptor>,
20 pub return_type: String,
21}
22
23struct Candidate<'a> {
24 declaration: &'a RoutineDeclaration,
25 matched: MatchedRoutineSignature,
26 wildcard: Option<usize>,
27}
28
29impl RankedFunctionMatch for Candidate<'_> {
30 fn argument_types(&self) -> &[String] {
31 self.matched.argument_types()
32 }
33 fn raw_exact_matches(&self) -> usize {
34 self.matched.raw_exact_matches()
35 }
36 fn exact_matches(&self) -> usize {
37 self.matched.exact_matches()
38 }
39 fn preferred_matches(&self) -> usize {
40 self.matched.preferred_matches()
41 }
42 fn is_variadic_expansion(&self) -> bool {
43 self.matched.is_variadic_expansion()
44 }
45}
46
47pub fn select_routine(
49 name: &str,
50 declarations: &[RoutineDeclaration],
51 call: RoutineCallDescriptor<'_>,
52) -> Result<RoutineIdentity, SQLError> {
53 let mut candidates = Vec::new();
54 let mut failure = None;
55 for declaration in declarations {
56 match candidate(name, declaration, call) {
57 Ok(Some(candidate)) => candidates.push(candidate),
58 Ok(None) => {}
59 Err(error) => {
60 failure.get_or_insert(error);
61 }
62 }
63 }
64
65 if candidates.is_empty() {
66 if let Some(error) = failure {
67 return Err(error);
68 }
69 return Err(function_resolution_error(
70 "42883",
71 name,
72 call.argument_names,
73 call.argument_types,
74 "does not exist",
75 ));
76 }
77 if !rank_function_matches(&mut candidates, call.argument_types) || candidates.len() != 1 {
78 return Err(function_resolution_error(
79 "42725",
80 name,
81 call.argument_names,
82 call.argument_types,
83 "is not unique",
84 ));
85 }
86 let Candidate {
87 declaration,
88 matched,
89 wildcard,
90 } = candidates.pop().expect("one selected catalog routine");
91 let result_type = matched
92 .substitute_type(&declaration.return_type)
93 .ok_or_else(|| SQLError::Internal("unresolved catalog routine result type".into()))?;
94 let mut invocation = matched.invocation_binding(Some(&declaration.return_type));
95 if let Some(position) = wildcard {
96 invocation.parameter_types.truncate(position);
97 invocation.parameter_types.push("any".into());
98 for index in position..call.argument_types.len() {
99 invocation.argument_positions[index] = position;
100 invocation.argument_targets[index] = call.argument_types[index]
101 .as_ref()
102 .map_or_else(|| "unknown".into(), ColumnType::catalog_name);
103 }
104 invocation.variadic_mode = if call.explicit_variadic {
105 RoutineVariadicMode::Explicit {
106 parameter_index: position,
107 }
108 } else {
109 RoutineVariadicMode::Expanded {
110 parameter_index: position,
111 }
112 };
113 }
114 Ok(RoutineIdentity {
115 oid: declaration.oid,
116 invocation,
117 result_type,
118 })
119}
120
121fn candidate<'a>(
122 name: &str,
123 declaration: &'a RoutineDeclaration,
124 call: RoutineCallDescriptor<'_>,
125) -> Result<Option<Candidate<'a>>, SQLError> {
126 let wildcard = declaration
127 .parameters
128 .last()
129 .filter(|parameter| parameter.variadic && parameter.type_name == "any")
130 .map(|_| declaration.parameters.len() - 1);
131 let parameters;
132 let matching = if let Some(position) = wildcard {
133 if call.argument_types.len() <= position || call.argument_names.iter().any(Option::is_some)
134 {
135 return Ok(None);
136 }
137 if call.explicit_variadic
138 && (call.argument_types.len() != position + 1
139 || !matches!(call.argument_types[position], Some(ColumnType::Array(_))))
140 {
141 return Ok(None);
142 }
143 parameters = declaration.parameters[..position]
145 .iter()
146 .cloned()
147 .chain(call.argument_types[position..].iter().map(|ty| {
148 let ty = ty.clone().unwrap_or(ColumnType::Text);
149 RoutineParameterDescriptor {
150 name: None,
151 type_name: ty.catalog_name(),
152 column_type: Some(ty),
153 has_default: false,
154 default_type: None,
155 variadic: false,
156 }
157 }))
158 .collect::<Vec<_>>();
159 ¶meters
160 } else {
161 &declaration.parameters
162 };
163 let mut matching_call = call;
164 if wildcard.is_some() {
165 matching_call.explicit_variadic = false;
166 }
167 let matched = match_routine_signature(matching, matching_call).map_err(|error| {
168 crate::routines::resolution::static_signature_error(
169 crate::routines::resolution::RoutineCallKind::Function,
170 name,
171 error,
172 )
173 })?;
174 Ok(matched.map(|matched| Candidate {
175 declaration,
176 matched,
177 wildcard,
178 }))
179}