uqa_sql/catalog/
resolution.rs1use crate::catalog::roles::RoleReference;
10use crate::SQLError;
11
12#[derive(Clone)]
14pub struct RelationNameResolution {
15 pub search_path: Vec<String>,
16 pub temporary_schema: String,
17 pub temporary_namespace_allocated: bool,
18 pub current_user: RoleReference,
19 pub lookup_mode: RelationLookupMode,
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum RelationLookupMode {
25 Dynamic,
26 Bound,
27}
28
29impl RelationNameResolution {
30 pub fn search_path(&self) -> &[String] {
31 &self.search_path
32 }
33
34 pub fn search_path_contains(&self, schema: &str) -> bool {
35 self.search_path.iter().any(|candidate| candidate == schema)
36 }
37
38 pub fn current_user(&self) -> &RoleReference {
39 &self.current_user
40 }
41
42 pub fn lookup_mode(&self) -> RelationLookupMode {
43 self.lookup_mode
44 }
45
46 pub fn qualified_schema(&self, name: &str) -> Result<Option<(String, String)>, SQLError> {
47 let (schema, _) = uqa_core::RelationIdentity::parse_reference(name).map_err(|error| {
48 SQLError::Internal(format!("resolve catalog relation `{name}`: {error}"))
49 })?;
50 Ok(schema.map(|schema| {
51 let resolved = if schema == "pg_temp" {
52 self.temporary_schema.clone()
53 } else {
54 schema.clone()
55 };
56 (schema, resolved)
57 }))
58 }
59
60 pub fn set_lookup_mode(&mut self, lookup_mode: RelationLookupMode) -> RelationLookupMode {
61 std::mem::replace(&mut self.lookup_mode, lookup_mode)
62 }
63
64 pub fn raw_relation_lookup_candidates(
65 &self,
66 name: &str,
67 ) -> Result<Vec<uqa_core::RelationIdentity>, SQLError> {
68 let (schema, relation) =
69 uqa_core::RelationIdentity::parse_reference(name).map_err(|error| {
70 SQLError::Internal(format!("resolve catalog relation `{name}`: {error}"))
71 })?;
72 if let Some(schema) = schema {
73 let schema = if schema == "pg_temp" {
74 self.temporary_schema.clone()
75 } else {
76 schema
77 };
78 return Ok(vec![uqa_core::RelationIdentity::new(schema, relation)]);
79 }
80 Ok(candidates::unqualified_candidates(
81 &self.temporary_schema,
82 &self.search_path,
83 &relation,
84 ))
85 }
86}
87
88#[derive(Clone, Debug, Eq, PartialEq)]
90pub enum RelationResolution {
91 Found(String, &'static str),
92 MissingRelation,
93 MissingSchema(String),
94}
95
96impl RelationResolution {
97 pub fn into_found(self) -> Option<(String, &'static str)> {
99 match self {
100 Self::Found(name, kind) => Some((name, kind)),
101 Self::MissingRelation | Self::MissingSchema(_) => None,
102 }
103 }
104}
105
106pub fn missing_relation_notice(name: &str) -> Result<String, crate::SQLError> {
107 let (_, local_name) =
108 uqa_core::RelationIdentity::parse_reference(name).map_err(crate::SQLError::Internal)?;
109 Ok(format!(
110 "relation \"{local_name}\" does not exist, skipping"
111 ))
112}
113
114pub fn resolve_relation_rename_source(
116 resolution: RelationResolution,
117 name: &str,
118 if_exists: bool,
119 notice: &mut dyn FnMut(&str),
120) -> Result<Option<(String, &'static str)>, crate::SQLError> {
121 match resolution {
122 RelationResolution::Found(canonical, kind) => Ok(Some((canonical, kind))),
123 RelationResolution::MissingSchema(_) | RelationResolution::MissingRelation if if_exists => {
124 notice(&missing_relation_notice(name)?);
125 Ok(None)
126 }
127 RelationResolution::MissingSchema(schema) => Err(crate::SQLError::Routine {
128 sqlstate: "3F000".into(),
129 message: format!("schema \"{schema}\" does not exist"),
130 }),
131 RelationResolution::MissingRelation => Err(crate::SQLError::Routine {
132 sqlstate: "42P01".into(),
133 message: format!("relation \"{name}\" does not exist"),
134 }),
135 }
136}
137
138pub mod candidates;
139
140pub mod creation;