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 search_path = catalog.search_path();
56 Ok(search_path
57 .into_iter()
58 .filter(|schema| {
59 catalog.schema_security(schema).is_some()
60 && catalog.schema_has_usage(schema, ¤t_user)
61 })
62 .map(|schema| RelationIdentity::new(schema, &local_name).qualified_name())
63 .collect())
64}
65
66pub fn routine_lookup_keys_with_builtins(
68 catalog: &dyn RoutineNameCatalog,
69 name: &str,
70) -> Result<Vec<String>, SQLError> {
71 let mut keys = routine_lookup_keys(catalog, name)?;
72 let (schema, local) = RelationIdentity::parse_reference(name)
73 .map_err(|error| SQLError::Internal(error.to_string()))?;
74 if schema.is_none()
75 && catalog.schema_has_usage("pg_catalog", &catalog.current_role())
76 && !catalog
77 .search_path()
78 .iter()
79 .any(|schema| schema == "pg_catalog")
80 {
81 keys.insert(
82 0,
83 RelationIdentity::new("pg_catalog", local).qualified_name(),
84 );
85 }
86 Ok(keys)
87}
88
89pub fn exact_function(
91 catalog: &dyn RoutineNameCatalog,
92 identities: &[RoutineCatalogIdentity],
93 name: &str,
94 argument_types: &[i64],
95 display_types: &[String],
96) -> Result<usize, SQLError> {
97 for key in routine_lookup_keys_with_builtins(catalog, name)? {
98 if let Some(index) = identities.iter().position(|identity| {
99 identity.relation.qualified_name() == key
100 && identity.kind != 'p'
101 && identity.argument_types == argument_types
102 }) {
103 return Ok(index);
104 }
105 }
106 Err(SQLError::Routine {
107 sqlstate: "42883".into(),
108 message: format!(
109 "function {} does not exist",
110 super::routine_signature_display(catalog, name, display_types)
111 ),
112 })
113}
114
115pub fn routine_lookup_keys_for_analysis(
117 catalog: &dyn RoutineNameCatalog,
118 name: &str,
119) -> Result<Option<Vec<String>>, SQLError> {
120 match routine_lookup_keys(catalog, name) {
121 Err(error) if crate::routines::is_routine_namespace_lookup_error(&error) => Ok(None),
122 result => result.map(Some),
123 }
124}
125
126#[cfg(test)]
127mod tests;