sea_query/query/
window.rs

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