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