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