Skip to main content

uqa_sql/routines/
lifecycle.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Routine removal identities, dependency results, and declaration diagnostics.
8
9pub mod binding;
10pub mod dependencies;
11pub mod diagnostics;
12pub mod lookup;
13pub mod names;
14pub mod relations;
15pub mod rename;
16pub mod restoration;
17pub mod rewrites;
18
19use super::SQLUserFunction;
20use crate::{
21    ast::{AlterRoutineKind, CreateFunction, FunctionBinding},
22    SQLError,
23};
24use std::{
25    collections::{BTreeMap, BTreeSet},
26    sync::Arc,
27};
28use uqa_core::RelationIdentity;
29
30pub type RoutineRegistry = BTreeMap<String, Vec<Arc<SQLUserFunction>>>;
31
32pub struct SQLFunctionDropPlan {
33    pub domains: BTreeSet<u32>,
34    pub targets: Vec<RoutineDropTarget>,
35    pub dependents: RoutineObjectDependents,
36    pub notices: Vec<(&'static str, String)>,
37}
38
39#[derive(Default)]
40pub struct RoutineDropResolution {
41    pub targets: Vec<RoutineDropTarget>,
42    pub seen_targets: BTreeSet<RoutineDropTarget>,
43    pub notices: Vec<(&'static str, String)>,
44}
45
46pub struct RoutineObjectDependents {
47    pub indexes: Vec<RelationIdentity>,
48    pub views: Vec<String>,
49    pub columns: Vec<(String, String, bool)>,
50    pub defaults: Vec<(String, String, bool)>,
51    pub checks: Vec<(String, String, bool)>,
52    pub triggers: Vec<(String, String)>,
53    pub rules: Vec<(String, String)>,
54}
55
56#[derive(Default)]
57pub struct RoutineSchemaDependents {
58    pub columns: Vec<(String, String, bool)>,
59    pub defaults: Vec<(String, String, bool)>,
60    pub checks: Vec<(String, String, bool)>,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
64pub struct RoutineDropTarget {
65    pub object_id: Option<[u8; 16]>,
66    pub name: String,
67    pub argument_types: Vec<String>,
68    pub is_procedure: bool,
69}
70
71impl RoutineDropTarget {
72    pub fn kind(&self) -> &'static str {
73        if self.is_procedure {
74            "procedure"
75        } else {
76            "function"
77        }
78    }
79
80    pub fn label(&self) -> String {
81        routine_signature_label(&self.name, &self.argument_types)
82    }
83
84    pub fn binding(&self) -> FunctionBinding {
85        FunctionBinding {
86            object_id: self.object_id,
87            name: self.name.clone(),
88            argument_types: self.argument_types.clone(),
89            builtin: false,
90            dispatch: None,
91            invocation: None,
92            resolution_error: None,
93        }
94    }
95}
96
97pub fn routine_signature_label(name: &str, types: &[String]) -> String {
98    let display_types = types
99        .iter()
100        .map(|type_name| {
101            crate::ast::ColumnType::from_sql_name(type_name)
102                .map_or_else(|_| type_name.clone(), |column_type| column_type.sql_name())
103        })
104        .collect::<Vec<_>>();
105    format!("{name}({})", display_types.join(", "))
106}
107
108pub fn wrong_routine_kind_error(
109    name: &str,
110    types: &[String],
111    actual_is_procedure: bool,
112    expected_kind: &str,
113) -> SQLError {
114    let actual_kind = if actual_is_procedure {
115        "procedure"
116    } else {
117        "function"
118    };
119    SQLError::Routine {
120        sqlstate: "42809".into(),
121        message: format!(
122            "{} is a {actual_kind}, not a {expected_kind}",
123            routine_signature_label(name, types)
124        ),
125    }
126}
127
128pub fn alter_routine_kind_name(kind: AlterRoutineKind) -> &'static str {
129    match kind {
130        AlterRoutineKind::Function => "function",
131        AlterRoutineKind::Procedure => "procedure",
132        AlterRoutineKind::Routine => "routine",
133    }
134}
135
136pub fn alter_routine_kind_matches(kind: AlterRoutineKind, def: &CreateFunction) -> bool {
137    match kind {
138        AlterRoutineKind::Function => !def.is_procedure,
139        AlterRoutineKind::Procedure => def.is_procedure,
140        AlterRoutineKind::Routine => true,
141    }
142}
143
144pub fn ensure_routine_owner_as(
145    definition: &CreateFunction,
146    current_user_has_owner_privileges: bool,
147) -> Result<(), SQLError> {
148    if current_user_has_owner_privileges {
149        Ok(())
150    } else {
151        Err(SQLError::Routine {
152            sqlstate: "42501".into(),
153            message: format!(
154                "must be owner of {} {}",
155                if definition.is_procedure {
156                    "procedure"
157                } else {
158                    "function"
159                },
160                definition.name
161            ),
162        })
163    }
164}