uqa_sql/binding/stored_relations/
query.rs1use super::StoredRelationCatalog;
10use crate::{
11 binding::view_dependencies::{bind_query_plan_relations, bind_query_plan_sequence_references},
12 plan::QueryPlan,
13 SQLError,
14};
15use std::collections::BTreeSet;
16use uqa_core::RelationIdentity;
17
18pub trait StoredQuerySequences {
19 fn query_sequence(&self, reference: &str) -> Result<String, String>;
20 fn loaded_query_sequence(&self, reference: &str) -> Result<String, String>;
21}
22pub struct StoredQueryNamespace {
24 pub temporary_schema: String,
25 pub transition_relations: BTreeSet<String>,
26}
27pub struct StoredQueryBindingContext<'a> {
28 pub relations: &'a dyn StoredRelationCatalog,
29 pub sequences: &'a dyn StoredQuerySequences,
30 pub temporary_schema: &'a str,
31 pub transition_relations: &'a BTreeSet<String>,
32}
33
34pub fn resolve_loaded_query_sequence(
35 reference: &str,
36 candidates: Vec<RelationIdentity>,
37 mut contains: impl FnMut(&RelationIdentity) -> bool,
38) -> Result<String, String> {
39 candidates
40 .into_iter()
41 .find(|candidate| contains(candidate))
42 .map(|candidate| candidate.qualified_name())
43 .ok_or_else(|| format!("Sequence `{reference}` does not exist"))
44}
45
46pub fn bind_stored_query_relations(
47 catalog: &StoredQueryBindingContext<'_>,
48 plan: &mut QueryPlan,
49 context: &str,
50 reject_transition_relations: bool,
51 loaded_catalog: bool,
52) -> Result<bool, SQLError> {
53 let mut uses_temporary_relation = false;
54 bind_query_plan_relations(plan, &std::collections::BTreeSet::new(), &mut |reference| {
55 if let Some(canonical) = catalog
56 .relations
57 .resolve_age_label_relation_name(reference)?
58 {
59 return Ok(canonical);
60 }
61 if RelationIdentity::parse_reference(reference)
62 .ok()
63 .is_some_and(|(schema, relation)| {
64 schema.is_none() && catalog.transition_relations.contains(&relation)
65 })
66 {
67 if reject_transition_relations {
68 return Err(SQLError::Routine {
69 sqlstate: "0A000".into(),
70 message: "transition tables cannot be referenced in a view definition".into(),
71 });
72 }
73 return Ok(reference.to_string());
74 }
75 let resolved = if loaded_catalog {
76 catalog
77 .relations
78 .resolve_loaded_visible_relation_kind(reference)?
79 .into_found()
80 } else {
81 catalog
82 .relations
83 .resolve_visible_relation_kind(reference)?
84 .into_found()
85 };
86 match resolved {
87 Some((canonical, "table" | "view" | "materialized view" | "foreign table")) => {
88 uses_temporary_relation |= RelationIdentity::from_legacy_name(&canonical)
89 .is_ok_and(|relation| relation.schema == catalog.temporary_schema);
90 Ok(canonical)
91 }
92 Some((canonical, kind)) => Err(SQLError::Routine {
93 sqlstate: "42809".into(),
94 message: format!(
95 "{context} source \"{canonical}\" is a {kind}, not a row relation"
96 ),
97 }),
98 None => Err(SQLError::UnknownTable(reference.to_string())),
99 }
100 })?;
101 bind_query_plan_sequence_references(plan, &mut |reference| {
102 let resolved = if loaded_catalog {
103 catalog.sequences.loaded_query_sequence(reference)
104 } else {
105 catalog.sequences.query_sequence(reference)
106 };
107 resolved.map_err(|error| {
108 SQLError::Unsupported(format!(
109 "{context} sequence reference `{reference}`: {error}"
110 ))
111 })
112 })?;
113 Ok(uses_temporary_relation)
114}