Skip to main content

siderite_orm/expr/
window.rs

1//! Window functions: `RowNumber::new().partition_by([..]).order_by([..])`.
2//!
3//! Windows need [`Feature::WindowFunctions`](crate::Feature::WindowFunctions).
4//! Window frames are not modelled: every function uses the database default
5//! frame (`RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` when an
6//! ordering is given), so `LastValue` returns the last *peer* of the current
7//! row, exactly as raw SQL would.
8
9// `RowNumber::new()` and friends are constructors of the `Window` they name.
10#![allow(clippy::new_ret_no_self)]
11
12use super::{Aggregate, Expr};
13use crate::plan::OrderExpr;
14
15/// The function evaluated over the window.
16#[derive(Debug, Clone, PartialEq)]
17pub enum WindowFunc {
18    /// `ROW_NUMBER()`.
19    RowNumber,
20    /// `RANK()`.
21    Rank,
22    /// `DENSE_RANK()`.
23    DenseRank,
24    /// `PERCENT_RANK()`.
25    PercentRank,
26    /// `CUME_DIST()`.
27    CumeDist,
28    /// `NTILE(n)`.
29    Ntile(u32),
30    /// `LAG(expr, offset [, default])`.
31    Lag {
32        /// Value read from a preceding row.
33        expr: Box<Expr>,
34        /// Rows to look back.
35        offset: u32,
36        /// Value when there is no such row.
37        default: Option<Box<Expr>>,
38    },
39    /// `LEAD(expr, offset [, default])`.
40    Lead {
41        /// Value read from a following row.
42        expr: Box<Expr>,
43        /// Rows to look ahead.
44        offset: u32,
45        /// Value when there is no such row.
46        default: Option<Box<Expr>>,
47    },
48    /// `FIRST_VALUE(expr)`.
49    FirstValue(Box<Expr>),
50    /// `LAST_VALUE(expr)`.
51    LastValue(Box<Expr>),
52    /// An aggregate evaluated over the window (`SUM(x) OVER (..)`, running totals).
53    Aggregate(Aggregate),
54}
55
56/// A window function with its `PARTITION BY` and `ORDER BY`.
57#[derive(Debug, Clone, PartialEq)]
58pub struct Window {
59    /// Function.
60    pub func: WindowFunc,
61    /// `PARTITION BY` expressions.
62    pub partition_by: Vec<Expr>,
63    /// `ORDER BY` terms inside the window.
64    pub order_by: Vec<OrderExpr>,
65}
66
67impl Window {
68    /// Window evaluating `func` over the whole result (no partition or ordering yet).
69    pub fn over(func: WindowFunc) -> Self {
70        Self {
71            func,
72            partition_by: Vec::new(),
73            order_by: Vec::new(),
74        }
75    }
76
77    /// Restart the function for every distinct value of `exprs`.
78    #[must_use]
79    pub fn partition_by<E: Into<Expr>>(mut self, exprs: impl IntoIterator<Item = E>) -> Self {
80        self.partition_by = exprs.into_iter().map(Into::into).collect();
81        self
82    }
83
84    /// Order rows inside each partition.
85    #[must_use]
86    pub fn order_by(mut self, terms: impl IntoIterator<Item = OrderExpr>) -> Self {
87        self.order_by = terms.into_iter().collect();
88        self
89    }
90
91    /// Every expression inside the window: function arguments, partition keys
92    /// and ordering terms.
93    pub(super) fn children(&self) -> Vec<&Expr> {
94        let func: Vec<&Expr> = match &self.func {
95            WindowFunc::Lag { expr, default, .. } | WindowFunc::Lead { expr, default, .. } => {
96                std::iter::once(&**expr).chain(default.as_deref()).collect()
97            }
98            WindowFunc::FirstValue(expr) | WindowFunc::LastValue(expr) => vec![expr],
99            WindowFunc::Aggregate(agg) => agg.children(),
100            _ => Vec::new(),
101        };
102        func.into_iter()
103            .chain(&self.partition_by)
104            .chain(self.order_by.iter().map(|o| &o.expr))
105            .collect()
106    }
107
108    /// Mutable counterpart of [`children`](Self::children).
109    pub(super) fn children_mut(&mut self) -> Vec<&mut Expr> {
110        let func: Vec<&mut Expr> = match &mut self.func {
111            WindowFunc::Lag { expr, default, .. } | WindowFunc::Lead { expr, default, .. } => {
112                std::iter::once(&mut **expr)
113                    .chain(default.as_deref_mut())
114                    .collect()
115            }
116            WindowFunc::FirstValue(expr) | WindowFunc::LastValue(expr) => vec![expr],
117            WindowFunc::Aggregate(agg) => agg.children_mut(),
118            _ => Vec::new(),
119        };
120        func.into_iter()
121            .chain(&mut self.partition_by)
122            .chain(self.order_by.iter_mut().map(|o| &mut o.expr))
123            .collect()
124    }
125}
126
127impl From<Window> for Expr {
128    fn from(window: Window) -> Self {
129        Expr::Window(Box::new(window))
130    }
131}
132
133macro_rules! window_fn {
134    ($($(#[$doc:meta])* $name:ident => $func:ident;)*) => {$(
135        $(#[$doc])*
136        #[derive(Debug, Clone, Copy)]
137        pub struct $name;
138
139        impl $name {
140            /// The window function; add `partition_by` / `order_by` on the result.
141            pub fn new() -> Window {
142                Window::over(WindowFunc::$func)
143            }
144        }
145    )*};
146}
147
148window_fn! {
149    /// `ROW_NUMBER()`.
150    RowNumber => RowNumber;
151    /// `RANK()`.
152    Rank => Rank;
153    /// `DENSE_RANK()`.
154    DenseRank => DenseRank;
155    /// `PERCENT_RANK()`.
156    PercentRank => PercentRank;
157    /// `CUME_DIST()`.
158    CumeDist => CumeDist;
159}
160
161/// `NTILE(n)`.
162#[derive(Debug, Clone, Copy)]
163pub struct Ntile;
164
165impl Ntile {
166    /// Split each partition into `buckets` groups.
167    pub fn new(buckets: u32) -> Window {
168        Window::over(WindowFunc::Ntile(buckets))
169    }
170}
171
172macro_rules! shift_fn {
173    ($($(#[$doc:meta])* $name:ident => $func:ident;)*) => {$(
174        $(#[$doc])*
175        #[derive(Debug, Clone, Copy)]
176        pub struct $name;
177
178        impl $name {
179            /// `expr` read `offset` rows away; `NULL` past the partition edge.
180            pub fn new(expr: impl Into<Expr>, offset: u32) -> Window {
181                Window::over(WindowFunc::$func {
182                    expr: Box::new(expr.into()),
183                    offset,
184                    default: None,
185                })
186            }
187
188            /// Like [`new`](Self::new) with `default` past the partition edge.
189            pub fn or_default(
190                expr: impl Into<Expr>,
191                offset: u32,
192                default: impl Into<Expr>,
193            ) -> Window {
194                Window::over(WindowFunc::$func {
195                    expr: Box::new(expr.into()),
196                    offset,
197                    default: Some(Box::new(default.into())),
198                })
199            }
200        }
201    )*};
202}
203
204shift_fn! {
205    /// `LAG(expr, offset)`.
206    Lag => Lag;
207    /// `LEAD(expr, offset)`.
208    Lead => Lead;
209}
210
211macro_rules! value_fn {
212    ($($(#[$doc:meta])* $name:ident => $func:ident;)*) => {$(
213        $(#[$doc])*
214        #[derive(Debug, Clone, Copy)]
215        pub struct $name;
216
217        impl $name {
218            /// The window function over `expr`.
219            pub fn new(expr: impl Into<Expr>) -> Window {
220                Window::over(WindowFunc::$func(Box::new(expr.into())))
221            }
222        }
223    )*};
224}
225
226value_fn! {
227    /// `FIRST_VALUE(expr)`.
228    FirstValue => FirstValue;
229    /// `LAST_VALUE(expr)`.
230    LastValue => LastValue;
231}