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, kind) => {
71 return Err(SQLError::Routine {
72 sqlstate: "42809".into(),
73 message: format!(
74 "{context} source \"{canonical}\" is a {kind}, not a row relation"
75 ),
76 });
77 }
78 RelationResolution::MissingSchema(schema) => {
79 return Err(SQLError::Routine {
80 sqlstate: "3F000".into(),
81 message: format!("schema \"{schema}\" does not exist"),
82 });
83 }
84 RelationResolution::MissingRelation => {
85 return Err(SQLError::UnknownTable(reference.clone()));
86 }
87 };
88 let relation = RelationIdentity::from_legacy_name(&canonical).map_err(|error| {
89 SQLError::Internal(format!("decode bound rule source `{canonical}`: {error}"))
90 })?;
91 *reference = canonical;
92 if crate::catalog::VirtualRelation::at(&relation.schema, &relation.name).is_none() {
93 dependencies.insert(relation);
94 }
95 Ok(())
96}
97
98pub fn bind_rule_action_relation_dependencies(
99 catalog: &dyn StoredRelationCatalog,
100 statement: &mut Statement,
101 lookup_mode: RelationLookupMode,
102) -> Result<RuleDependencies, SQLError> {
103 let mut dependencies = BTreeSet::new();
104 let mut bind = |reference: &mut String| {
105 bind_catalog_relation_reference(
106 catalog,
107 reference,
108 lookup_mode,
109 false,
110 "CREATE RULE",
111 &mut dependencies,
112 )
113 };
114 let mut ignore_routine = |_: &mut String,
115 _: Option<&mut Option<crate::ast::FunctionBinding>>|
116 -> Result<(), SQLError> { Ok(()) };
117 StoredAstVisitor {
118 source: None,
119 merge: None,
120 expression: None,
121 ty: None,
122 relation: &mut bind,
123 routine: &mut ignore_routine,
124 }
125 .bind_statement(statement)?;
126 Ok(RuleDependencies {
127 relations: dependencies,
128 columns: BTreeSet::new(),
129 routines: BTreeSet::new(),
130 })
131}
132
133pub fn bind_rule_condition_relation_dependencies(
134 catalog: &dyn StoredRelationCatalog,
135 expression: &mut Expr,
136 lookup_mode: RelationLookupMode,
137) -> Result<RuleDependencies, SQLError> {
138 let mut dependencies = BTreeSet::new();
139 let mut bind = |reference: &mut String| {
140 bind_catalog_relation_reference(
141 catalog,
142 reference,
143 lookup_mode,
144 false,
145 "CREATE RULE",
146 &mut dependencies,
147 )
148 };
149 let mut ignore_routine = |_: &mut String,
150 _: Option<&mut Option<crate::ast::FunctionBinding>>|
151 -> Result<(), SQLError> { Ok(()) };
152 StoredAstVisitor {
153 source: None,
154 merge: None,
155 expression: None,
156 ty: None,
157 relation: &mut bind,
158 routine: &mut ignore_routine,
159 }
160 .bind_expr(expression, &BTreeSet::new())?;
161 Ok(RuleDependencies {
162 relations: dependencies,
163 columns: BTreeSet::new(),
164 routines: BTreeSet::new(),
165 })
166}
167
168pub fn bind_stored_statement_relations(
169 catalog: &dyn StoredRelationCatalog,
170 statement: &mut Statement,
171 lookup_mode: RelationLookupMode,
172 loaded_catalog: bool,
173 context: &str,
174) -> Result<bool, SQLError> {
175 let mut dependencies = BTreeSet::new();
176 let mut changed = false;
177 let mut bind = |reference: &mut String| {
178 let previous = reference.clone();
179 bind_catalog_relation_reference(
180 catalog,
181 reference,
182 lookup_mode,
183 loaded_catalog,
184 context,
185 &mut dependencies,
186 )?;
187 changed |= reference != &previous;
188 Ok(())
189 };
190 let mut ignore_routine = |_: &mut String,
191 _: Option<&mut Option<crate::ast::FunctionBinding>>|
192 -> Result<(), SQLError> { Ok(()) };
193 StoredAstVisitor {
194 source: None,
195 merge: None,
196 expression: None,
197 ty: None,
198 relation: &mut bind,
199 routine: &mut ignore_routine,
200 }
201 .bind_statement(statement)?;
202 match statement {
203 Statement::Insert(insert) => {
204 changed |= !insert.target_relation_bound;
205 insert.target_relation_bound = true;
206 }
207 Statement::Update(update) => {
208 changed |= !update.target_relation_bound;
209 update.target_relation_bound = true;
210 }
211 Statement::Delete(delete) => {
212 changed |= !delete.target_relation_bound;
213 delete.target_relation_bound = true;
214 }
215 _ => {}
216 }
217 Ok(changed)
218}