uqa_sql/binding/
portals.rs1pub mod dependencies;
10pub mod relations;
11
12#[cfg(test)]
13mod tests;
14
15use crate::{plan::QueryPlan, routines::RoutineResolution, SQLError};
16use std::collections::BTreeSet;
17use uqa_core::RelationIdentity;
18
19pub trait PortalRelationCatalog {
20 fn try_resolve_table_name(&self, name: &str) -> Result<Option<String>, String>;
21 fn hierarchy_scan_tables(
22 &self,
23 table: &str,
24 include_descendants: bool,
25 ) -> Result<Vec<String>, SQLError>;
26 fn view_plan(&self, name: &str) -> Result<Option<QueryPlan>, SQLError>;
27 fn try_resolve_visible_relation_kind(
28 &self,
29 name: &str,
30 ) -> Result<Option<(String, &'static str)>, SQLError>;
31 fn resolve_age_label_relation_name(&self, name: &str) -> Result<Option<String>, SQLError>;
32 fn try_resolve_sequence_reference(&self, name: &str) -> Result<Option<String>, String>;
33}
34
35pub trait PortalTransitionRelations {
36 fn active_transition_relation_names(&self) -> BTreeSet<String>;
37}
38
39pub struct PortalBindingContext<'a> {
40 pub catalog: &'a dyn PortalRelationCatalog,
41 pub routines: &'a dyn RoutineResolution,
42 pub transitions: &'a dyn PortalTransitionRelations,
43}
44
45pub struct SessionPortalTableDependencies {
46 pub tables: Option<std::collections::BTreeSet<RelationIdentity>>,
47 pub graphs: Option<std::collections::BTreeSet<String>>,
48 pub graph_catalog: bool,
49}
50
51impl SessionPortalTableDependencies {
52 pub fn empty() -> Self {
53 Self {
54 tables: Some(std::collections::BTreeSet::new()),
55 graphs: Some(std::collections::BTreeSet::new()),
56 graph_catalog: false,
57 }
58 }
59
60 pub fn all() -> Self {
61 Self {
62 tables: None,
63 graphs: None,
64 graph_catalog: true,
65 }
66 }
67
68 pub fn is_all(&self) -> bool {
69 self.tables.is_none() && self.graphs.is_none()
70 }
71
72 pub fn includes(&self, relation: &RelationIdentity) -> bool {
73 self.tables
74 .as_ref()
75 .is_none_or(|tables| tables.contains(relation))
76 }
77
78 pub fn insert(&mut self, relation: RelationIdentity) {
79 if let Some(relations) = &mut self.tables {
80 relations.insert(relation);
81 }
82 }
83
84 pub fn insert_graph(&mut self, graph: String) {
85 self.graph_catalog = true;
86 if let Some(graphs) = &mut self.graphs {
87 graphs.insert(graph);
88 }
89 }
90}
91
92pub fn prepare_query(
93 inputs: &PortalBindingContext<'_>,
94 query: &mut QueryPlan,
95) -> Result<SessionPortalTableDependencies, SQLError> {
96 relations::bind_session_portal_query_relations(
97 inputs,
98 query,
99 &std::collections::BTreeSet::new(),
100 )?;
101 super::view_dependencies::bind_query_plan_sequence_references(query, &mut |reference| {
102 inputs
103 .catalog
104 .try_resolve_sequence_reference(reference)
105 .map_err(|error| {
106 SQLError::Internal(format!(
107 "bind cursor sequence `{reference}` at DECLARE: {error}"
108 ))
109 })
110 .and_then(|bound| {
111 bound.ok_or_else(|| SQLError::Routine {
112 sqlstate: "42P01".into(),
113 message: format!("relation \"{reference}\" does not exist"),
114 })
115 })
116 })?;
117 let table_dependencies = dependencies::session_portal_table_dependencies(inputs, query)?;
118 Ok(table_dependencies)
119}