Skip to main content

uqa_sql/plan/
windows.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Canonical window clauses own analyzed inputs; expanded call specifications are derived copies.
8
9use super::{
10    CommandPlan, CtePlan, CtePlanBody, QueryBlockPlan, QueryPlan, RelationalPlan, SourcePlan,
11    UnifiedPlan,
12};
13use crate::{SQLError, ScalarExpr, ScalarWindowSpec};
14
15/// Resolve the canonical own clauses without evaluating any expression.
16pub fn resolved_window_definitions(
17    block: &QueryBlockPlan,
18) -> Result<Vec<ScalarWindowSpec>, SQLError> {
19    let mut resolved: Vec<ScalarWindowSpec> = Vec::with_capacity(block.windows.len());
20    let mut names = std::collections::BTreeSet::new();
21    for (slot, window) in block.windows.iter().enumerate() {
22        if window.spec.definition.is_some() {
23            return Err(invalid("a canonical specification refers to a call slot"));
24        }
25        if let Some(name) = &window.name {
26            if name.is_empty() || !names.insert(name) {
27                return Err(invalid("a canonical window name is empty or repeated"));
28            }
29        }
30        let mut spec = window.spec.clone();
31        if let Some(parent) = window.inherited {
32            let base = resolved
33                .get(parent)
34                .filter(|_| parent < slot && block.windows[parent].name.is_some())
35                .ok_or_else(|| invalid("an inherited window is not an earlier named definition"))?;
36            if !spec.partition_by.is_empty()
37                || (!spec.order_by.is_empty() && !base.order_by.is_empty())
38                || base.frame.is_some()
39            {
40                return Err(invalid(
41                    "a canonical window has incompatible inherited clauses",
42                ));
43            }
44            spec.partition_by.clone_from(&base.partition_by);
45            if spec.order_by.is_empty() {
46                spec.order_by.clone_from(&base.order_by);
47            }
48        }
49        spec.definition = Some(slot);
50        resolved.push(spec);
51    }
52    Ok(resolved)
53}
54
55impl UnifiedPlan {
56    /// Refresh execution copies after input constants or stored identities change. Call only after identity-based input replacement has completed.
57    pub fn normalize_window_definitions(&mut self) -> Result<(), SQLError> {
58        match self {
59            Self::Query(query) => query.normalize_window_definitions(),
60            Self::Command(command) => normalize_command(command),
61        }
62    }
63}
64
65impl QueryPlan {
66    /// Keep each call's execution specification equal to its canonical query-local definition.
67    pub fn normalize_window_definitions(&mut self) -> Result<(), SQLError> {
68        normalize_ctes(&mut self.ctes)?;
69        let result = match &mut self.root {
70            RelationalPlan::QueryBlock(block) => {
71                if let Some(source) = &mut block.from {
72                    normalize_source(source)?;
73                }
74                for query in &mut block.subqueries {
75                    query.normalize_window_definitions()?;
76                }
77                let resolved = resolved_window_definitions(block)?;
78                let mut result = Ok(());
79                let mut update = |node: &mut ScalarExpr| {
80                    if result.is_ok() {
81                        if let ScalarExpr::WindowCall { spec, .. } = node {
82                            if let Some(slot) = spec.definition {
83                                match resolved.get(slot) {
84                                    Some(canonical) => spec.clone_from(canonical),
85                                    None => {
86                                        result = Err(invalid(
87                                            "a call refers to a missing window definition",
88                                        ));
89                                    }
90                                }
91                            }
92                        }
93                    }
94                };
95                visit_local_roots_mut(block, &mut |expression| expression.visit_mut(&mut update));
96                result
97            }
98            RelationalPlan::SetOp {
99                left,
100                right,
101                subqueries,
102                ..
103            } => {
104                left.normalize_window_definitions()?;
105                right.normalize_window_definitions()?;
106                for query in subqueries {
107                    query.normalize_window_definitions()?;
108                }
109                Ok(())
110            }
111            RelationalPlan::Values { subqueries, .. } => {
112                for query in subqueries {
113                    query.normalize_window_definitions()?;
114                }
115                Ok(())
116            }
117        };
118        result?;
119        super::subqueries::prune_query(self);
120        Ok(())
121    }
122}
123
124fn normalize_command(command: &mut CommandPlan) -> Result<(), SQLError> {
125    if let Some(ctes) = command.ctes_mut() {
126        normalize_ctes(ctes)?;
127    }
128    if let Some(source) = command.source_input_mut() {
129        normalize_source(source)?;
130    }
131    for query in command.query_inputs_mut() {
132        query.normalize_window_definitions()?;
133    }
134    match command {
135        CommandPlan::CreateView { query, .. }
136        | CommandPlan::CreateMaterializedView { query, .. }
137        | CommandPlan::CreateTableAs { query, .. }
138        | CommandPlan::DeclareCursor { query, .. } => query.normalize_window_definitions(),
139        CommandPlan::Explain { body, .. } | CommandPlan::Prepare { body, .. } => {
140            body.normalize_window_definitions()
141        }
142        CommandPlan::Execute { params, .. } | CommandPlan::Call { args: params, .. } => {
143            for expression in params {
144                for query in &mut expression.subqueries {
145                    query.normalize_window_definitions()?;
146                }
147            }
148            Ok(())
149        }
150        _ => Ok(()),
151    }
152}
153
154fn normalize_ctes(ctes: &mut [CtePlan]) -> Result<(), SQLError> {
155    for cte in ctes {
156        match &mut cte.body {
157            CtePlanBody::Query(query) => query.normalize_window_definitions()?,
158            CtePlanBody::Command(command) => normalize_command(command)?,
159        }
160    }
161    Ok(())
162}
163
164fn normalize_source(source: &mut SourcePlan) -> Result<(), SQLError> {
165    match source {
166        SourcePlan::Subquery { body, .. } => body.normalize_window_definitions(),
167        SourcePlan::Join { left, right, .. } => {
168            normalize_source(left)?;
169            normalize_source(right)
170        }
171        _ => Ok(()),
172    }
173}
174
175fn visit_local_roots_mut(block: &mut QueryBlockPlan, visit: &mut dyn FnMut(&mut ScalarExpr)) {
176    if let Some(source) = &mut block.from {
177        visit_source_roots_mut(source, visit);
178    }
179    for expression in block
180        .projections
181        .iter_mut()
182        .map(|projection| &mut projection.expr)
183        .chain(block.r#where.iter_mut())
184        .chain(block.group_by.iter_mut())
185        .chain(block.grouping_sets.iter_mut().flatten())
186        .chain(block.having.iter_mut())
187        .chain(block.order_by.iter_mut().map(|order| &mut order.expr))
188        .chain(block.limit.iter_mut())
189        .chain(block.offset.iter_mut())
190        .chain(block.distinct_on.iter_mut())
191    {
192        visit(expression);
193    }
194}
195
196fn visit_source_roots_mut(source: &mut SourcePlan, visit: &mut dyn FnMut(&mut ScalarExpr)) {
197    match source {
198        SourcePlan::Table { .. } | SourcePlan::Subquery { .. } => {}
199        SourcePlan::Join {
200            left, right, on, ..
201        } => {
202            visit_source_roots_mut(left, visit);
203            visit_source_roots_mut(right, visit);
204            if let Some(on) = on {
205                visit(on);
206            }
207        }
208        SourcePlan::Values { rows, .. } => {
209            for expression in rows.iter_mut().flatten() {
210                visit(expression);
211            }
212        }
213        SourcePlan::Function { args, .. } => {
214            for expression in args {
215                visit(expression);
216            }
217        }
218        SourcePlan::FunctionGroup { functions, .. } => {
219            for expression in functions.iter_mut().flat_map(|function| &mut function.args) {
220                visit(expression);
221            }
222        }
223    }
224}
225
226fn invalid(message: &str) -> SQLError {
227    SQLError::Internal(format!("invalid stored window definitions: {message}"))
228}
229
230#[cfg(test)]
231mod tests;