uqa_sql/routines/lifecycle/
binding.rs1use super::{
10 alter_routine_kind_matches, alter_routine_kind_name, ensure_routine_owner_as,
11 names::{routine_lookup_keys, RoutineNameCatalog},
12 routine_signature_label, wrong_routine_kind_error, RoutineDropResolution, RoutineDropTarget,
13};
14use crate::catalog::roles::identity::RoleSubject;
15use crate::{
16 ast::{AlterRoutineKind, DropFunctionItem, DropFunctionStmt},
17 catalog::roles::{role_inherits, RoleDefinition, RoleMembership, RoleMembershipKey},
18 routines::{routine_signature_types, SQLUserFunction},
19 type_resolution::canonical_routine_type_name,
20 SQLError,
21};
22use std::{
23 collections::{BTreeMap, BTreeSet},
24 sync::Arc,
25};
26
27pub fn resolve_sql_function_drop_targets(
28 catalog: &dyn RoutineNameCatalog,
29 stmt: &DropFunctionStmt,
30 registry: &BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
31 kind: &'static str,
32) -> Result<RoutineDropResolution, SQLError> {
33 let mut resolution = RoutineDropResolution {
34 targets: Vec::new(),
35 seen_targets: BTreeSet::new(),
36 notices: Vec::new(),
37 };
38 for item in &stmt.items {
39 let target =
40 resolve_sql_function_drop_target(catalog, registry, item, stmt.is_procedure, kind)?;
41 if let Some((key, position)) = target {
42 let function = ®istry[&key][position];
43 let target = RoutineDropTarget {
44 object_id: function.def.object_id,
45 name: key,
46 argument_types: routine_signature_types(&function.def),
47 is_procedure: function.def.is_procedure,
48 };
49 if resolution.seen_targets.insert(target.clone()) {
50 resolution.targets.push(target);
51 }
52 } else {
53 let spelled = match &item.arg_types {
54 Some(types) => format!("{}({})", item.name, types.join(", ")),
55 None => format!("{}()", item.name),
56 };
57 if stmt.if_exists {
58 resolution.notices.push((
59 "NOTICE",
60 format!("{kind} {spelled} does not exist, skipping"),
61 ));
62 continue;
63 }
64 let described = match &item.arg_types {
65 Some(_) => format!("{kind} {spelled} does not exist"),
66 None => format!("could not find a {kind} named \"{}\"", item.name),
67 };
68 return Err(SQLError::Routine {
69 sqlstate: "42883".into(),
70 message: described,
71 });
72 }
73 }
74 Ok(resolution)
75}
76
77pub fn resolve_sql_function_drop_target(
78 catalog: &dyn RoutineNameCatalog,
79 registry: &BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
80 item: &DropFunctionItem,
81 is_procedure: bool,
82 expected_kind: &str,
83) -> Result<Option<(String, usize)>, SQLError> {
84 let requested_types = item.arg_types.as_ref().map(|types| {
85 types
86 .iter()
87 .map(|type_name| canonical_routine_type_name(type_name))
88 .collect::<Vec<_>>()
89 });
90 for key in routine_lookup_keys(catalog, &item.name)? {
91 let Some(overloads) = registry.get(&key) else {
92 continue;
93 };
94 if let Some(types) = requested_types.as_ref() {
95 let Some((position, function)) = overloads
96 .iter()
97 .enumerate()
98 .find(|(_, function)| routine_signature_types(&function.def) == *types)
99 else {
100 continue;
101 };
102 if function.def.is_procedure != is_procedure {
103 return Err(wrong_routine_kind_error(
104 &function.def.name,
105 types,
106 function.def.is_procedure,
107 expected_kind,
108 ));
109 }
110 return Ok(Some((key, position)));
111 }
112
113 let positions = overloads
114 .iter()
115 .enumerate()
116 .filter(|(_, function)| function.def.is_procedure == is_procedure)
117 .map(|(position, _)| position)
118 .collect::<Vec<_>>();
119 match positions.as_slice() {
120 [] => {
121 if let Some(function) = overloads.first() {
122 return Err(wrong_routine_kind_error(
123 &function.def.name,
124 &routine_signature_types(&function.def),
125 function.def.is_procedure,
126 expected_kind,
127 ));
128 }
129 }
130 [position] => return Ok(Some((key, *position))),
131 _ => {
132 return Err(SQLError::Routine {
133 sqlstate: "42725".into(),
134 message: format!("{expected_kind} name \"{}\" is not unique", item.name),
135 });
136 }
137 }
138 }
139 Ok(None)
140}
141
142pub fn resolve_sql_routine_alter_target(
143 catalog: &dyn RoutineNameCatalog,
144 registry: &BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
145 requested_name: &str,
146 requested_types: Option<&[String]>,
147 kind: AlterRoutineKind,
148) -> Result<(String, usize), SQLError> {
149 let kind_name = alter_routine_kind_name(kind);
150 let keys = routine_lookup_keys(catalog, requested_name)?;
151 if let Some(types) = requested_types {
152 for key in keys {
153 let Some(overloads) = registry.get(&key) else {
154 continue;
155 };
156 let Some((position, function)) = overloads
157 .iter()
158 .enumerate()
159 .find(|(_, function)| routine_signature_types(&function.def) == types)
160 else {
161 continue;
162 };
163 if !alter_routine_kind_matches(kind, &function.def) {
164 return Err(wrong_routine_kind_error(
165 &function.def.name,
166 types,
167 function.def.is_procedure,
168 kind_name,
169 ));
170 }
171 return Ok((key, position));
172 }
173 return Err(SQLError::Routine {
174 sqlstate: "42883".into(),
175 message: format!(
176 "{kind_name} {} does not exist",
177 routine_signature_label(requested_name, types)
178 ),
179 });
180 }
181
182 let mut visible_signatures = std::collections::BTreeSet::new();
184 let mut candidates = Vec::new();
185 for key in keys {
186 let Some(overloads) = registry.get(&key) else {
187 continue;
188 };
189 for (position, function) in overloads.iter().enumerate() {
190 let signature = routine_signature_types(&function.def);
191 if visible_signatures.insert(signature)
192 && alter_routine_kind_matches(kind, &function.def)
193 {
194 candidates.push((key.clone(), position));
195 }
196 }
197 }
198 match candidates.as_slice() {
199 [(name, position)] => Ok((name.clone(), *position)),
200 [] => Err(SQLError::Routine {
201 sqlstate: "42883".into(),
202 message: format!("could not find a {kind_name} named \"{requested_name}\""),
203 }),
204 _ => Err(SQLError::Routine {
205 sqlstate: "42725".into(),
206 message: format!("{kind_name} name \"{requested_name}\" is not unique"),
207 }),
208 }
209}
210
211pub fn ensure_routine_drop_owners(
212 registry: &BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
213 targets: &[RoutineDropTarget],
214 current_user: &(impl RoleSubject + ?Sized),
215 roles: &BTreeMap<String, RoleDefinition>,
216 memberships: &BTreeMap<RoleMembershipKey, RoleMembership>,
217) -> Result<(), SQLError> {
218 for target in targets {
219 let definition = registry
220 .get(&target.name)
221 .and_then(|overloads| {
222 overloads.iter().find(|function| {
223 function.def.is_procedure == target.is_procedure
224 && routine_signature_types(&function.def) == target.argument_types
225 })
226 })
227 .map(|function| &function.def)
228 .ok_or_else(|| {
229 SQLError::Internal(format!(
230 "resolved {} {} disappeared before ownership validation",
231 target.kind(),
232 target.label()
233 ))
234 })?;
235 ensure_routine_owner_as(
236 definition,
237 role_inherits(
238 roles,
239 memberships,
240 current_user,
241 &crate::routines::security::bound_routine_owner(definition)?,
242 ),
243 )?;
244 }
245 Ok(())
246}