uqa_sql/routines/lifecycle/
names.rs1use crate::catalog::roles::RoleReference;
10use crate::{catalog::security::BoundSchemaSecurity, SQLError};
11use uqa_core::RelationIdentity;
12
13pub struct RoutineCatalogIdentity {
15 pub oid: u32,
16 pub relation: RelationIdentity,
17 pub argument_types: Vec<i64>,
18 pub kind: char,
19}
20
21pub trait RoutineNameCatalog {
22 fn schema_security(&self, schema: &str) -> Option<BoundSchemaSecurity>;
23 fn current_role(&self) -> RoleReference;
24 fn search_path(&self) -> Vec<String>;
25 fn require_schema_usage(&self, schema: &str, role: &RoleReference) -> Result<(), SQLError>;
26 fn schema_has_usage(&self, schema: &str, role: &RoleReference) -> bool;
27 fn routine_type_display(&self, type_name: &str) -> String;
29 fn routine_identity_display(&self, oid: u32) -> Result<String, SQLError>;
31}
32
33pub fn routine_lookup_keys(
34 catalog: &dyn RoutineNameCatalog,
35 name: &str,
36) -> Result<Vec<String>, SQLError> {
37 let (schema, local_name) =
38 RelationIdentity::parse_reference(name).map_err(|error| SQLError::Routine {
39 sqlstate: "42602".into(),
40 message: format!("invalid routine name `{name}`: {error}"),
41 })?;
42 if let Some(schema) = schema {
43 if catalog.schema_security(&schema).is_none() {
44 return Err(SQLError::Routine {
45 sqlstate: "3F000".into(),
46 message: format!("schema \"{schema}\" does not exist"),
47 });
48 }
49 catalog.require_schema_usage(&schema, &catalog.current_role())?;
50 return Ok(vec![
51 RelationIdentity::new(schema, local_name).qualified_name()
52 ]);
53 }
54 let current_user = catalog.current_role();
55 let mut search_path = catalog.search_path();
56 if !search_path.iter().any(|schema| schema == "pg_catalog") {
57 search_path.insert(0, "pg_catalog".into());
58 }
59 Ok(search_path
60 .into_iter()
61 .filter(|schema| {
62 catalog.schema_security(schema).is_some()
63 && catalog.schema_has_usage(schema, ¤t_user)
64 })
65 .map(|schema| RelationIdentity::new(schema, &local_name).qualified_name())
66 .collect())
67}
68
69pub fn routine_lookup_keys_with_builtins(
71 catalog: &dyn RoutineNameCatalog,
72 name: &str,
73) -> Result<Vec<String>, SQLError> {
74 routine_lookup_keys(catalog, name)
75}
76
77pub fn exact_function(
79 catalog: &dyn RoutineNameCatalog,
80 identities: &[RoutineCatalogIdentity],
81 name: &str,
82 argument_types: &[i64],
83 display_types: &[String],
84) -> Result<usize, SQLError> {
85 for key in routine_lookup_keys_with_builtins(catalog, name)? {
86 if let Some(index) = identities.iter().position(|identity| {
87 identity.relation.qualified_name() == key
88 && identity.kind != 'p'
89 && identity.argument_types == argument_types
90 }) {
91 return Ok(index);
92 }
93 }
94 Err(SQLError::Routine {
95 sqlstate: "42883".into(),
96 message: format!(
97 "function {} does not exist",
98 super::routine_signature_display(catalog, name, display_types)
99 ),
100 })
101}
102
103pub fn routine_lookup_keys_for_analysis(
105 catalog: &dyn RoutineNameCatalog,
106 name: &str,
107) -> Result<Option<Vec<String>>, SQLError> {
108 match routine_lookup_keys(catalog, name) {
109 Err(error) if crate::routines::is_routine_namespace_lookup_error(&error) => Ok(None),
110 result => result.map(Some),
111 }
112}
113
114#[cfg(test)]
115mod tests;