Skip to main content

sea_query/query/
window.rs

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