uqa_sql/routines/lifecycle/
rename.rs1use super::RoutineRegistry;
10use crate::{
11 ast::{CreateFunction, FunctionBinding, FunctionBody, RenameRoutineStmt},
12 routines::{routine_signature_types, SQLUserFunction},
13 SQLError,
14};
15use std::{collections::BTreeMap, sync::Arc};
16use uqa_core::RelationIdentity;
17
18pub struct RoutineRenameTarget {
19 pub old_name: String,
20 pub new_name: String,
21 pub position: usize,
22 pub binding: FunctionBinding,
23}
24
25pub fn routine_rename_identity(name: &str) -> Result<RelationIdentity, SQLError> {
26 RelationIdentity::from_legacy_name(name)
27 .map_err(|error| SQLError::Internal(format!("decode routine identity `{name}`: {error}")))
28}
29
30pub fn finish_routine_rename_target(
31 stmt: &RenameRoutineStmt,
32 old_name: String,
33 position: usize,
34 definition: &CreateFunction,
35 old_identity: &RelationIdentity,
36 registry: &RoutineRegistry,
37) -> Result<RoutineRenameTarget, SQLError> {
38 let signature = routine_signature_types(definition);
39 let new_name = routine_rename_destination(stmt, old_identity, &signature, registry)?;
40 let object_id = definition.object_id.ok_or_else(|| {
41 SQLError::Internal(format!(
42 "routine `{old_name}` has no catalog object identity"
43 ))
44 })?;
45 Ok(RoutineRenameTarget {
46 old_name: old_name.clone(),
47 new_name,
48 position,
49 binding: FunctionBinding {
50 object_id: Some(object_id),
51 name: old_name,
52 argument_types: signature,
53 builtin: false,
54 dispatch: None,
55 invocation: None,
56 resolution_error: None,
57 },
58 })
59}
60
61fn routine_rename_destination(
62 stmt: &RenameRoutineStmt,
63 old_identity: &RelationIdentity,
64 signature: &[String],
65 registry: &BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
66) -> Result<String, SQLError> {
67 let (new_schema, new_local_name) =
68 RelationIdentity::parse_reference(&stmt.new_name).map_err(|error| SQLError::Routine {
69 sqlstate: "42602".into(),
70 message: format!("invalid routine name `{}`: {error}", stmt.new_name),
71 })?;
72 if new_schema.is_some() {
73 return Err(SQLError::Routine {
74 sqlstate: "42601".into(),
75 message: "ALTER ROUTINE RENAME TO requires an unqualified new name".into(),
76 });
77 }
78 let new_name = RelationIdentity::new(&old_identity.schema, new_local_name).qualified_name();
79 if registry.get(&new_name).is_some_and(|overloads| {
80 overloads
81 .iter()
82 .any(|function| routine_signature_types(&function.def) == signature)
83 }) {
84 return Err(SQLError::Routine {
85 sqlstate: "42723".into(),
86 message: format!(
87 "function \"{}\" already exists with same argument types",
88 RelationIdentity::from_legacy_name(&new_name)
89 .map_or_else(|_| new_name.clone(), |identity| identity.name)
90 ),
91 });
92 }
93 Ok(new_name)
94}
95
96pub fn move_routine_registry_entry(
97 mut registry: BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
98 target: &RoutineRenameTarget,
99) -> Result<BTreeMap<String, Vec<Arc<SQLUserFunction>>>, SQLError> {
100 let old_overloads = registry.get_mut(&target.old_name).ok_or_else(|| {
101 SQLError::Internal(format!(
102 "resolved ALTER routine registry entry `{}` disappeared before rename",
103 target.old_name
104 ))
105 })?;
106 let renamed = old_overloads.remove(target.position);
107 if old_overloads.is_empty() {
108 registry.remove(&target.old_name);
109 }
110 let mut renamed_definition = renamed.def.clone();
111 renamed_definition.name.clone_from(&target.new_name);
112 let new_overloads = registry.entry(target.new_name.clone()).or_default();
113 new_overloads.push(Arc::new(SQLUserFunction {
114 def: renamed_definition,
115 compiled: renamed.compiled.clone(),
116 }));
117 new_overloads.sort_by(|left, right| {
118 routine_signature_types(&left.def)
119 .cmp(&routine_signature_types(&right.def))
120 .then_with(|| left.def.is_procedure.cmp(&right.def.is_procedure))
121 });
122 Ok(registry)
123}
124
125pub fn rewrite_routine_owned_dependency_identity(
126 def: &mut CreateFunction,
127 target: &FunctionBinding,
128 new_name: &str,
129) -> Result<bool, SQLError> {
130 let mut changed = false;
131 for default in def
132 .params
133 .iter_mut()
134 .filter_map(|parameter| parameter.default.as_mut())
135 {
136 changed |= crate::catalog::stored_ast::rewrite_expression_routine_identity(
137 default, target, new_name,
138 )?;
139 }
140 if let FunctionBody::Statements(statements) = &mut def.body {
141 for statement in statements {
142 changed |= crate::catalog::stored_ast::rewrite_statement_routine_identity(
143 statement, target, new_name,
144 )?;
145 }
146 }
147 Ok(changed)
148}