1mod query;
10pub use query::{
11 bind_stored_query_relations, resolve_loaded_query_sequence, StoredQueryBindingContext,
12 StoredQueryNamespace, StoredQuerySequences,
13};
14
15use crate::{
16 ast::{Expr, Statement},
17 catalog::{
18 events::RuleDependencies,
19 resolution::{RelationLookupMode, RelationResolution},
20 stored_ast::StoredAstVisitor,
21 },
22 SQLError,
23};
24use std::collections::BTreeSet;
25use uqa_core::RelationIdentity;
26
27pub trait StoredRelationCatalog {
28 fn resolve_age_label_relation_name(&self, reference: &str) -> Result<Option<String>, SQLError>;
29 fn resolve_visible_relation_kind(
30 &self,
31 reference: &str,
32 ) -> Result<RelationResolution, SQLError>;
33 fn resolve_loaded_visible_relation_kind(
34 &self,
35 reference: &str,
36 ) -> Result<RelationResolution, SQLError>;
37 fn resolve_bound_relation_kind(&self, reference: &str) -> Result<RelationResolution, SQLError>;
38}
39
40fn bind_catalog_relation_reference(
41 catalog: &dyn StoredRelationCatalog,
42 reference: &mut String,
43 lookup_mode: RelationLookupMode,
44 loaded_catalog: bool,
45 context: &str,
46 dependencies: &mut BTreeSet<RelationIdentity>,
47) -> Result<(), SQLError> {
48 if lookup_mode == RelationLookupMode::Dynamic {
49 if let Some(canonical) = catalog.resolve_age_label_relation_name(reference)? {
50 let relation = RelationIdentity::from_legacy_name(&canonical).map_err(|error| {
51 SQLError::Internal(format!("decode bound rule source `{canonical}`: {error}"))
52 })?;
53 *reference = canonical;
54 dependencies.insert(relation);
55 return Ok(());
56 }
57 }
58 let resolution = match (lookup_mode, loaded_catalog) {
59 (RelationLookupMode::Dynamic, true) => {
60 catalog.resolve_loaded_visible_relation_kind(reference)?
61 }
62 (RelationLookupMode::Dynamic, false) => catalog.resolve_visible_relation_kind(reference)?,
63 (RelationLookupMode::Bound, _) => catalog.resolve_bound_relation_kind(reference)?,
64 };
65 let canonical = match resolution {
66 RelationResolution::Found(
67 canonical,
68 "table" | "view" | "materialized view" | "foreign table" | "sequence",
69 ) => canonical,
70 RelationResolution::Found(canonical, "composite type") => {
71 return Err(query::composite_relation_error(&canonical));
72 }
73 RelationResolution::Found(canonical, kind) => {
74 return Err(SQLError::Routine {
75 sqlstate: "42809".into(),
76 message: format!(
77 "{context} source \"{canonical}\" is a {kind}, not a row relation"
78 ),
79 });
80 }
81 RelationResolution::MissingSchema(schema) => {
82 return Err(SQLError::Routine {
83 sqlstate: "3F000".into(),
84 message: format!("schema \"{schema}\" does not exist"),
85 });
86 }
87 RelationResolution::MissingRelation => {
88 return Err(SQLError::UnknownTable(reference.clone()));
89 }
90 };
91 let relation = RelationIdentity::from_legacy_name(&canonical).map_err(|error| {
92 SQLError::Internal(format!("decode bound rule source `{canonical}`: {error}"))
93 })?;
94 *reference = canonical;
95 if crate::catalog::VirtualRelation::at(&relation.schema, &relation.name).is_none() {
96 dependencies.insert(relation);
97 }
98 Ok(())
99}
100
101pub fn bind_rule_action_relation_dependencies(
102 catalog: &dyn StoredRelationCatalog,
103 statement: &mut Statement,
104 lookup_mode: RelationLookupMode,
105) -> Result<RuleDependencies, SQLError> {
106 let mut dependencies = BTreeSet::new();
107 let mut bind = |reference: &mut String| {
108 bind_catalog_relation_reference(
109 catalog,
110 reference,
111 lookup_mode,
112 false,
113 "CREATE RULE",
114 &mut dependencies,
115 )
116 };
117 let mut ignore_routine = |_: &mut String,
118 _: Option<&mut Option<crate::ast::FunctionBinding>>|
119 -> Result<(), SQLError> { Ok(()) };
120 StoredAstVisitor {
121 source: None,
122 merge: None,
123 expression: None,
124 projection: None,
125 ty: None,
126 relation: &mut bind,
127 routine: &mut ignore_routine,
128 }
129 .bind_statement(statement)?;
130 Ok(RuleDependencies {
131 relations: dependencies,
132 columns: BTreeSet::new(),
133 routines: BTreeSet::new(),
134 })
135}
136
137pub fn bind_rule_condition_relation_dependencies(
138 catalog: &dyn StoredRelationCatalog,
139 expression: &mut Expr,
140 lookup_mode: RelationLookupMode,
141) -> Result<RuleDependencies, SQLError> {
142 let mut dependencies = BTreeSet::new();
143 let mut bind = |reference: &mut String| {
144 bind_catalog_relation_reference(
145 catalog,
146 reference,
147 lookup_mode,
148 false,
149 "CREATE RULE",
150 &mut dependencies,
151 )
152 };
153 let mut ignore_routine = |_: &mut String,
154 _: Option<&mut Option<crate::ast::FunctionBinding>>|
155 -> Result<(), SQLError> { Ok(()) };
156 StoredAstVisitor {
157 source: None,
158 merge: None,
159 expression: None,
160 projection: None,
161 ty: None,
162 relation: &mut bind,
163 routine: &mut ignore_routine,
164 }
165 .bind_expr(expression, &BTreeSet::new())?;
166 Ok(RuleDependencies {
167 relations: dependencies,
168 columns: BTreeSet::new(),
169 routines: BTreeSet::new(),
170 })
171}
172
173pub fn bind_stored_statement_relations(
174 catalog: &dyn StoredRelationCatalog,
175 statement: &mut Statement,
176 lookup_mode: RelationLookupMode,
177 loaded_catalog: bool,
178 context: &str,
179) -> Result<bool, SQLError> {
180 let mut dependencies = BTreeSet::new();
181 let mut changed = false;
182 let mut bind = |reference: &mut String| {
183 let previous = reference.clone();
184 bind_catalog_relation_reference(
185 catalog,
186 reference,
187 lookup_mode,
188 loaded_catalog,
189 context,
190 &mut dependencies,
191 )?;
192 changed |= reference != &previous;
193 Ok(())
194 };
195 let mut ignore_routine = |_: &mut String,
196 _: Option<&mut Option<crate::ast::FunctionBinding>>|
197 -> Result<(), SQLError> { Ok(()) };
198 StoredAstVisitor {
199 source: None,
200 merge: None,
201 expression: None,
202 projection: None,
203 ty: None,
204 relation: &mut bind,
205 routine: &mut ignore_routine,
206 }
207 .bind_statement(statement)?;
208 match statement {
209 Statement::Insert(insert) => {
210 changed |= !insert.target_relation_bound;
211 insert.target_relation_bound = true;
212 }
213 Statement::Update(update) => {
214 changed |= !update.target_relation_bound;
215 update.target_relation_bound = true;
216 }
217 Statement::Delete(delete) => {
218 changed |= !delete.target_relation_bound;
219 delete.target_relation_bound = true;
220 }
221 _ => {}
222 }
223 Ok(changed)
224}