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