uqa_sql/semantics/
cte_strategy.rs1use crate::{
10 ast::CteMaterialization,
11 plan::{CtePlan, QueryPlan},
12 semantics::{
13 cte_definition_references, cte_references_own_name, ordered_plan_ctes,
14 reachable_plan_cte_names, single_reference_plan_cte_names,
15 volatility::{query_contains_volatile_function, VolatilityCatalog},
16 },
17 SQLError,
18};
19use std::collections::{BTreeMap, BTreeSet};
20
21pub struct ScheduledCte<'a> {
22 pub plan: &'a CtePlan,
23 pub deferred: bool,
24}
25
26pub fn schedule_plan_ctes<'a>(
27 catalog: &dyn VolatilityCatalog,
28 plan: &'a QueryPlan,
29) -> Result<Vec<ScheduledCte<'a>>, SQLError> {
30 let ordered = ordered_plan_ctes(plan)?;
31 let reachable = reachable_plan_cte_names(plan);
32 let single_reference = single_reference_plan_cte_names(plan);
33 Ok(ordered
34 .into_iter()
35 .filter(|cte| reachable.contains(&cte.name))
36 .map(|cte| ScheduledCte {
37 plan: cte,
38 deferred: !cte.body.modifies_data()
39 && !cte_references_own_name(cte)
40 && match cte.materialization {
41 CteMaterialization::Default => single_reference.contains(&cte.name),
42 CteMaterialization::Materialized => false,
43 CteMaterialization::NotMaterialized => true,
44 }
45 && matches!(
46 cte.body
47 .query()
48 .map_or(Ok(true), |query| query_contains_volatile_function(
49 catalog, query
50 )),
51 Ok(false)
52 ),
53 })
54 .collect())
55}
56
57pub struct StatementCteOrder<'a> {
59 pub primary: Vec<&'a CtePlan>,
61 pub postponed: Vec<&'a CtePlan>,
63}
64
65pub fn order_statement_ctes<'a>(
67 ctes: &'a [CtePlan],
68 scheduled: Vec<&'a CtePlan>,
69 primary_references: &BTreeSet<String>,
70) -> StatementCteOrder<'a> {
71 if !scheduled.iter().any(|cte| cte.body.modifies_data()) {
72 return StatementCteOrder {
73 primary: scheduled,
74 postponed: Vec::new(),
75 };
76 }
77 let references = ctes
78 .iter()
79 .enumerate()
80 .map(|(index, cte)| (cte.name.as_str(), cte_definition_references(ctes, index)))
81 .collect::<BTreeMap<_, _>>();
82 let read_through = |roots: Vec<String>| {
84 let mut reached = BTreeSet::new();
85 let mut pending = roots;
86 while let Some(name) = pending.pop() {
87 if let Some(names) = references.get(name.as_str()) {
88 pending.extend(
89 names
90 .iter()
91 .filter(|name| !reached.contains(*name))
92 .cloned(),
93 );
94 }
95 reached.insert(name);
96 }
97 reached
98 };
99 let read = read_through(primary_references.iter().cloned().collect());
100 let mut postponed_names = BTreeSet::new();
101 let mut postponed = Vec::new();
102 for item in ctes
103 .iter()
104 .rev()
105 .filter(|cte| cte.body.modifies_data() && !read.contains(&cte.name))
106 {
107 let needed = read_through(vec![item.name.clone()]);
108 for cte in &scheduled {
109 if needed.contains(&cte.name)
110 && !read.contains(&cte.name)
111 && postponed_names.insert(cte.name.clone())
112 {
113 postponed.push(*cte);
114 }
115 }
116 }
117 let primary = scheduled
118 .into_iter()
119 .filter(|cte| !postponed_names.contains(&cte.name))
120 .collect();
121 StatementCteOrder { primary, postponed }
122}
123
124#[cfg(test)]
125mod tests;