sea_query/query/
window.rs

1use std::borrow::Cow;
2
3use crate::{expr::*, query::*, types::*};
4use inherent::inherent;
5
6pub trait OverStatement {
7    #[doc(hidden)]
8    // Implementation for the trait.
9    fn add_partition_by(&mut self, partition: Expr) -> &mut Self;
10
11    /// Partition by column.
12    fn partition_by<T>(&mut self, col: T) -> &mut Self
13    where
14        T: IntoColumnRef,
15    {
16        self.add_partition_by(Expr::Column(col.into_column_ref()))
17    }
18
19    /// Partition by custom string.
20    fn partition_by_customs<I, T>(&mut self, cols: I) -> &mut Self
21    where
22        T: Into<Cow<'static, str>>,
23        I: IntoIterator<Item = T>,
24    {
25        cols.into_iter().for_each(|c| {
26            self.add_partition_by(Expr::Custom(c.into()));
27        });
28        self
29    }
30
31    /// Partition by vector of columns.
32    fn partition_by_columns<I, T>(&mut self, cols: I) -> &mut Self
33    where
34        T: IntoColumnRef,
35        I: IntoIterator<Item = T>,
36    {
37        cols.into_iter().for_each(|c| {
38            self.add_partition_by(Expr::Column(c.into_column_ref()));
39        });
40        self
41    }
42}
43
44/// frame_start or frame_end clause
45#[derive(Debug, Clone, PartialEq)]
46pub enum Frame {
47    UnboundedPreceding,
48    Preceding(u32),
49    CurrentRow,
50    Following(u32),
51    UnboundedFollowing,
52}
53
54/// Frame type
55#[derive(Debug, Clone, PartialEq)]
56pub enum FrameType {
57    Range,
58    Rows,
59}
60
61/// Frame clause
62#[derive(Debug, Clone, PartialEq)]
63pub struct FrameClause {
64    pub(crate) r#type: FrameType,
65    pub(crate) start: Frame,
66    pub(crate) end: Option<Frame>,
67}
68
69/// Window expression
70///
71/// # References:
72///
73/// 1. <https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html>
74/// 2. <https://www.sqlite.org/windowfunctions.html>
75/// 3. <https://www.postgresql.org/docs/current/tutorial-window.html>
76#[derive(Default, Debug, Clone, PartialEq)]
77pub struct WindowStatement {
78    pub(crate) partition_by: Vec<Expr>,
79    pub(crate) order_by: Vec<OrderExpr>,
80    pub(crate) frame: Option<FrameClause>,
81}
82
83impl WindowStatement {
84    /// Construct a new [`WindowStatement`]
85    pub fn new() -> Self {
86        Self::default()
87    }
88
89    pub fn take(&mut self) -> Self {
90        Self {
91            partition_by: std::mem::take(&mut self.partition_by),
92            order_by: std::mem::take(&mut self.order_by),
93            frame: self.frame.take(),
94        }
95    }
96
97    /// Construct a new [`WindowStatement`] with PARTITION BY column
98    pub fn partition_by<T>(col: T) -> Self
99    where
100        T: IntoColumnRef,
101    {
102        let mut window = Self::new();
103        window.add_partition_by(Expr::Column(col.into_column_ref()));
104        window
105    }
106
107    /// Construct a new [`WindowStatement`] with PARTITION BY custom
108    pub fn partition_by_custom<T>(col: T) -> Self
109    where
110        T: Into<Cow<'static, str>>,
111    {
112        let mut window = Self::new();
113        window.add_partition_by(Expr::cust(col));
114        window
115    }
116
117    /// frame clause for frame_start
118    /// # Examples:
119    ///
120    /// ```
121    /// use sea_query::{tests_cfg::*, *};
122    ///
123    /// let query = Query::select()
124    ///     .from(Char::Table)
125    ///     .expr_window_as(
126    ///         Expr::col(Char::Character),
127    ///         WindowStatement::partition_by(Char::FontSize)
128    ///             .frame_start(FrameType::Rows, Frame::UnboundedPreceding)
129    ///             .take(),
130    ///         "C")
131    ///     .to_owned();
132    ///
133    /// assert_eq!(
134    ///     query.to_string(MysqlQueryBuilder),
135    ///     r#"SELECT `character` OVER ( PARTITION BY `font_size` ROWS UNBOUNDED PRECEDING ) AS `C` FROM `character`"#
136    /// );
137    /// assert_eq!(
138    ///     query.to_string(PostgresQueryBuilder),
139    ///     r#"SELECT "character" OVER ( PARTITION BY "font_size" ROWS UNBOUNDED PRECEDING ) AS "C" FROM "character""#
140    /// );
141    /// assert_eq!(
142    ///     query.to_string(SqliteQueryBuilder),
143    ///     r#"SELECT "character" OVER ( PARTITION BY "font_size" ROWS UNBOUNDED PRECEDING ) AS "C" FROM "character""#
144    /// );
145    /// ```
146    pub fn frame_start(&mut self, r#type: FrameType, start: Frame) -> &mut Self {
147        self.frame(r#type, start, None)
148    }
149
150    /// frame clause for BETWEEN frame_start AND frame_end
151    ///
152    /// # Examples:
153    ///
154    /// ```
155    /// use sea_query::{tests_cfg::*, *};
156    ///
157    /// let query = Query::select()
158    ///     .from(Char::Table)
159    ///     .expr_window_as(
160    ///         Expr::col(Char::Character),
161    ///         WindowStatement::partition_by(Char::FontSize)
162    ///             .frame_between(FrameType::Rows, Frame::UnboundedPreceding, Frame::UnboundedFollowing)
163    ///             .take(),
164    ///         "C")
165    ///     .to_owned();
166    ///
167    /// assert_eq!(
168    ///     query.to_string(MysqlQueryBuilder),
169    ///     r#"SELECT `character` OVER ( PARTITION BY `font_size` ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS `C` FROM `character`"#
170    /// );
171    /// assert_eq!(
172    ///     query.to_string(PostgresQueryBuilder),
173    ///     r#"SELECT "character" OVER ( PARTITION BY "font_size" ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS "C" FROM "character""#
174    /// );
175    /// assert_eq!(
176    ///     query.to_string(SqliteQueryBuilder),
177    ///     r#"SELECT "character" OVER ( PARTITION BY "font_size" ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS "C" FROM "character""#
178    /// );
179    /// ```
180    pub fn frame_between(&mut self, r#type: FrameType, start: Frame, end: Frame) -> &mut Self {
181        self.frame(r#type, start, Some(end))
182    }
183
184    /// frame clause
185    pub fn frame(&mut self, r#type: FrameType, start: Frame, end: Option<Frame>) -> &mut Self {
186        let frame_clause = FrameClause { r#type, start, end };
187        self.frame = Some(frame_clause);
188        self
189    }
190}
191
192impl OverStatement for WindowStatement {
193    fn add_partition_by(&mut self, partition: Expr) -> &mut Self {
194        self.partition_by.push(partition);
195        self
196    }
197}
198
199#[inherent]
200impl OrderedStatement for WindowStatement {
201    pub fn add_order_by(&mut self, order: OrderExpr) -> &mut Self {
202        self.order_by.push(order);
203        self
204    }
205
206    pub fn clear_order_by(&mut self) -> &mut Self {
207        self.order_by = Vec::new();
208        self
209    }
210
211    pub fn order_by<T>(&mut self, col: T, order: Order) -> &mut Self
212    where
213        T: IntoColumnRef;
214
215    pub fn order_by_expr(&mut self, expr: Expr, order: Order) -> &mut Self;
216    pub fn order_by_customs<I, T>(&mut self, cols: I) -> &mut Self
217    where
218        T: Into<Cow<'static, str>>,
219        I: IntoIterator<Item = (T, Order)>;
220    pub fn order_by_columns<I, T>(&mut self, cols: I) -> &mut Self
221    where
222        T: IntoColumnRef,
223        I: IntoIterator<Item = (T, Order)>;
224    pub fn order_by_with_nulls<T>(
225        &mut self,
226        col: T,
227        order: Order,
228        nulls: NullOrdering,
229    ) -> &mut Self
230    where
231        T: IntoColumnRef;
232    pub fn order_by_expr_with_nulls(
233        &mut self,
234        expr: Expr,
235        order: Order,
236        nulls: NullOrdering,
237    ) -> &mut Self;
238    pub fn order_by_customs_with_nulls<I, T>(&mut self, cols: I) -> &mut Self
239    where
240        T: Into<Cow<'static, str>>,
241        I: IntoIterator<Item = (T, Order, NullOrdering)>;
242    pub fn order_by_columns_with_nulls<I, T>(&mut self, cols: I) -> &mut Self
243    where
244        T: IntoColumnRef,
245        I: IntoIterator<Item = (T, Order, NullOrdering)>;
246}