1mod ctes;
10use super::{RuleDependencies, RuleRoutineDependency};
11use crate::{
12 plan::{QueryPlan, RelationalPlan, SourcePlan},
13 SQLError,
14};
15use ctes::{collect_cte_relation_dependencies, collect_cte_source_routine_dependencies};
16use std::collections::BTreeSet;
17use uqa_core::RelationIdentity;
18
19pub fn collect_query_relation_dependencies(
20 query: &QueryPlan,
21 dependencies: &mut RuleDependencies,
22 inherited_ctes: &BTreeSet<String>,
23) -> Result<(), SQLError> {
24 let mut visible_ctes = inherited_ctes.clone();
25 let recursive = query.ctes.iter().any(|cte| cte.recursive).then(|| {
26 query
27 .ctes
28 .iter()
29 .map(|cte| cte.name.clone())
30 .collect::<BTreeSet<_>>()
31 });
32 for cte in &query.ctes {
33 let body_scope = recursive.as_ref().map_or_else(
34 || visible_ctes.clone(),
35 |recursive| inherited_ctes.union(recursive).cloned().collect(),
36 );
37 collect_cte_relation_dependencies(&cte.body, dependencies, &body_scope)?;
38 visible_ctes.insert(cte.name.clone());
39 }
40 match &query.root {
41 RelationalPlan::QueryBlock(block) => {
42 if let Some(source) = &block.from {
43 collect_source_relation_dependencies(source, dependencies, &visible_ctes)?;
44 }
45 for subquery in &block.subqueries {
46 collect_query_relation_dependencies(subquery, dependencies, &visible_ctes)?;
47 }
48 }
49 RelationalPlan::SetOp {
50 left,
51 right,
52 subqueries,
53 ..
54 } => {
55 collect_query_relation_dependencies(left, dependencies, &visible_ctes)?;
56 collect_query_relation_dependencies(right, dependencies, &visible_ctes)?;
57 for subquery in subqueries {
58 collect_query_relation_dependencies(subquery, dependencies, &visible_ctes)?;
59 }
60 }
61 RelationalPlan::Values { subqueries, .. } => {
62 for subquery in subqueries {
63 collect_query_relation_dependencies(subquery, dependencies, &visible_ctes)?;
64 }
65 }
66 }
67 Ok(())
68}
69
70pub fn collect_expression_routine_dependencies(
71 expression: &crate::plan::ExpressionPlan,
72 dependencies: &mut RuleDependencies,
73) {
74 let mut scalar = expression.scalar.clone();
75 crate::plan::rewrite_scalar_expression(&mut scalar, &mut |expression| {
76 if let crate::ScalarExpr::Func {
77 binding: Some(binding),
78 ..
79 } = expression
80 {
81 insert_routine_dependency(binding, dependencies);
82 }
83 });
84 for query in &expression.subqueries {
85 collect_query_routine_dependencies(query, dependencies);
86 }
87}
88
89pub fn collect_query_routine_dependencies(query: &QueryPlan, dependencies: &mut RuleDependencies) {
90 let mut scalar_plan = query.clone();
91 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
92 if let crate::ScalarExpr::Func {
93 binding: Some(binding),
94 ..
95 } = expression
96 {
97 insert_routine_dependency(binding, dependencies);
98 }
99 });
100 for cte in &query.ctes {
101 collect_cte_source_routine_dependencies(&cte.body, dependencies);
102 }
103 collect_relational_source_routine_dependencies(&query.root, dependencies);
104}
105
106fn collect_query_source_routine_dependencies(
107 query: &QueryPlan,
108 dependencies: &mut RuleDependencies,
109) {
110 for cte in &query.ctes {
111 collect_cte_source_routine_dependencies(&cte.body, dependencies);
112 }
113 collect_relational_source_routine_dependencies(&query.root, dependencies);
114}
115
116fn collect_relational_source_routine_dependencies(
117 plan: &RelationalPlan,
118 dependencies: &mut RuleDependencies,
119) {
120 match plan {
121 RelationalPlan::QueryBlock(block) => {
122 if let Some(source) = &block.from {
123 collect_source_routine_dependencies(source, dependencies);
124 }
125 for subquery in &block.subqueries {
126 collect_query_source_routine_dependencies(subquery, dependencies);
127 }
128 }
129 RelationalPlan::SetOp {
130 left,
131 right,
132 subqueries,
133 ..
134 } => {
135 collect_query_source_routine_dependencies(left, dependencies);
136 collect_query_source_routine_dependencies(right, dependencies);
137 for subquery in subqueries {
138 collect_query_source_routine_dependencies(subquery, dependencies);
139 }
140 }
141 RelationalPlan::Values { subqueries, .. } => {
142 for subquery in subqueries {
143 collect_query_source_routine_dependencies(subquery, dependencies);
144 }
145 }
146 }
147}
148
149fn collect_source_routine_dependencies(source: &SourcePlan, dependencies: &mut RuleDependencies) {
150 match source {
151 SourcePlan::Table { .. } | SourcePlan::Values { .. } => {}
152 SourcePlan::Join { left, right, .. } => {
153 collect_source_routine_dependencies(left, dependencies);
154 collect_source_routine_dependencies(right, dependencies);
155 }
156 SourcePlan::Subquery { body, .. } => {
157 collect_query_source_routine_dependencies(body, dependencies);
158 }
159 SourcePlan::Function { binding, .. } => {
160 if let Some(binding) = binding {
161 insert_routine_dependency(binding, dependencies);
162 }
163 }
164 SourcePlan::FunctionGroup { functions, .. } => {
165 for function in functions {
166 if let Some(binding) = &function.binding {
167 insert_routine_dependency(binding, dependencies);
168 }
169 }
170 }
171 }
172}
173
174fn insert_routine_dependency(
175 binding: &crate::ast::FunctionBinding,
176 dependencies: &mut RuleDependencies,
177) {
178 if !binding.builtin {
179 dependencies.routines.insert(RuleRoutineDependency {
180 object_id: binding.object_id,
181 name: binding.name.clone(),
182 argument_types: binding.argument_types.clone(),
183 });
184 }
185}
186
187fn collect_source_relation_dependencies(
188 source: &SourcePlan,
189 dependencies: &mut RuleDependencies,
190 visible_ctes: &BTreeSet<String>,
191) -> Result<(), SQLError> {
192 match source {
193 SourcePlan::Table { name, .. } => {
194 if crate::binding::view_dependencies::canonical_virtual_relation_reference(name)
195 .is_some()
196 {
197 return Ok(());
198 }
199 let (schema, relation) = RelationIdentity::parse_reference(name).map_err(|error| {
200 SQLError::Internal(format!("decode stored rule dependency `{name}`: {error}"))
201 })?;
202 if schema.is_none() && visible_ctes.contains(&relation) {
203 return Ok(());
204 }
205 let schema = schema.ok_or_else(|| {
206 SQLError::Internal(format!(
207 "stored rule relation dependency `{name}` is not catalog-bound"
208 ))
209 })?;
210 dependencies
211 .relations
212 .insert(RelationIdentity::new(schema, relation));
213 }
214 SourcePlan::Join { left, right, .. } => {
215 collect_source_relation_dependencies(left, dependencies, visible_ctes)?;
216 collect_source_relation_dependencies(right, dependencies, visible_ctes)?;
217 }
218 SourcePlan::Subquery { body, .. } => {
219 collect_query_relation_dependencies(body, dependencies, visible_ctes)?;
220 }
221 SourcePlan::Function { relations, .. } => {
222 if let Some(relations) = relations {
223 collect_canonical_relation(&relations.left, dependencies)?;
224 collect_canonical_relation(&relations.right, dependencies)?;
225 }
226 }
227 SourcePlan::FunctionGroup { functions, .. } => {
228 for function in functions {
229 if let Some(relations) = &function.relations {
230 collect_canonical_relation(&relations.left, dependencies)?;
231 collect_canonical_relation(&relations.right, dependencies)?;
232 }
233 }
234 }
235 SourcePlan::Values { .. } => {}
236 }
237 Ok(())
238}
239
240fn collect_canonical_relation(
241 reference: &str,
242 dependencies: &mut RuleDependencies,
243) -> Result<(), SQLError> {
244 let relation = RelationIdentity::from_legacy_name(reference).map_err(|error| {
245 SQLError::Internal(format!(
246 "decode stored rule dependency `{reference}`: {error}"
247 ))
248 })?;
249 dependencies.relations.insert(relation);
250 Ok(())
251}