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