Skip to main content

qbrs_core/
window.rs

1//! Window functions: `row_number()`/`rank()`/`dense_rank()` `.over(window()
2//! .partition_by(..).order_by(..))`.
3//!
4//! `row_number()`/`rank()`/`dense_rank()` return `WindowFunc<K>`, whose only
5//! method is `.over()`, so `.over()` can't be reached from an arbitrary
6//! expression that would render nonsense SQL.
7//!
8//! **Known limitation**: only the ranking functions. An aggregate used as a
9//! window function (`sum(col) OVER (..)`) needs `.over()` on the aggregate
10//! itself, which is a different builder shape from `WindowFunc`.
11
12use std::marker::PhantomData;
13
14use crate::expr::{BigInt, ExprKind, IntoExpr, Keyed, SortDir};
15use crate::scope::{Concat, Nil};
16use crate::select::OrderKey;
17
18/// Accumulates a window's `PARTITION BY`/`ORDER BY` lists, growing `Req`
19/// via `Concat` as `Expr::and`/`or` do. Partitioning by an out-of-scope
20/// column therefore fails the same `Superset` check any other expression
21/// would.
22pub struct Window<Req> {
23    partition_by: Vec<ExprKind>,
24    order_by: Vec<(ExprKind, SortDir)>,
25    _marker: PhantomData<fn() -> Req>,
26}
27
28/// Starts an empty window spec. Never partitioning or ordering leaves a bare
29/// `OVER ()`, a valid if unusual window over the entire result set.
30pub fn window() -> Window<Nil> {
31    Window {
32        partition_by: Vec::new(),
33        order_by: Vec::new(),
34        _marker: PhantomData,
35    }
36}
37
38impl<Req> Window<Req> {
39    pub fn partition_by<Req2>(
40        self,
41        key: impl IntoExpr<Req = Req2>,
42    ) -> Window<<Req as Concat<Req2>>::Output>
43    where
44        Req: Concat<Req2>,
45    {
46        let mut partition_by = self.partition_by;
47        partition_by.push(key.into_expr().kind);
48        Window {
49            partition_by,
50            order_by: self.order_by,
51            _marker: PhantomData,
52        }
53    }
54
55    pub fn order_by<Req2>(self, key: OrderKey<Req2>) -> Window<<Req as Concat<Req2>>::Output>
56    where
57        Req: Concat<Req2>,
58    {
59        let mut order_by = self.order_by;
60        order_by.push(key.into_parts());
61        Window {
62            partition_by: self.partition_by,
63            order_by,
64            _marker: PhantomData,
65        }
66    }
67}
68
69/// A bare, argument-free window function reference (`row_number()`,
70/// `rank()`, `dense_rank()`). It is not yet a usable expression, since a
71/// window function has no meaning without an `OVER (..)` clause. See this
72/// module's doc comment for why this is a separate type from `Expr` rather
73/// than `Expr` itself.
74///
75/// `K` is the row key `.over(..)` stamps onto the result, so a selected
76/// `row_number()` is readable as `row.row_number()` with nothing declared.
77pub struct WindowFunc<K> {
78    sql: &'static str,
79    _marker: PhantomData<fn() -> K>,
80}
81
82impl<K> crate::row::RowKey for WindowFunc<K> {
83    type Key = K;
84}
85
86/// So `row.get(row_number())` works: the key is the function, and a window
87/// spec would only be noise at the lookup.
88impl<K: crate::row::Spelled> crate::row::LookupKey for WindowFunc<K> {}
89
90impl<K> WindowFunc<K> {
91    /// Every ranking function counts rows, so the result is `BigInt` rather
92    /// than a parameter. An aggregate over a window would have the aggregate's
93    /// own type, and is the separate shape this module defers.
94    pub fn over<WindowReq>(self, window: Window<WindowReq>) -> Keyed<K, WindowReq, BigInt> {
95        Keyed::from_kind(ExprKind::Window {
96            func: self.sql,
97            partition_by: window.partition_by,
98            order_by: window.order_by,
99        })
100    }
101}
102
103fn window_func<K>(sql: &'static str) -> WindowFunc<K> {
104    WindowFunc {
105        sql,
106        _marker: PhantomData,
107    }
108}
109
110crate::row::expr_key!(
111    RowNumber,
112    HasRowNumber,
113    row_number,
114    "The identity a selected `row_number() OVER (..)` is filed under in a row.",
115    'r',
116    'o',
117    'w',
118    '_',
119    'n',
120    'u',
121    'm',
122    'b',
123    'e',
124    'r'
125);
126crate::row::expr_key!(
127    Rank,
128    HasRank,
129    rank,
130    "The identity a selected `rank() OVER (..)` is filed under in a row.",
131    'r',
132    'a',
133    'n',
134    'k'
135);
136crate::row::expr_key!(
137    DenseRank,
138    HasDenseRank,
139    dense_rank,
140    "The identity a selected `dense_rank() OVER (..)` is filed under in a row.",
141    'd',
142    'e',
143    'n',
144    's',
145    'e',
146    '_',
147    'r',
148    'a',
149    'n',
150    'k'
151);
152
153/// `ROW_NUMBER() OVER (..)`: a unique, sequential number per row within its
154/// partition, ordered by the window's `ORDER BY`.
155pub fn row_number() -> WindowFunc<RowNumber> {
156    window_func("row_number()")
157}
158
159/// `RANK() OVER (..)`: like `row_number()`, but rows tied on the `ORDER BY`
160/// key share the same rank, leaving a gap in the sequence afterward.
161pub fn rank() -> WindowFunc<Rank> {
162    window_func("rank()")
163}
164
165/// `DENSE_RANK() OVER (..)`: like `rank()`, but without the gap after a
166/// tie.
167pub fn dense_rank() -> WindowFunc<DenseRank> {
168    window_func("dense_rank()")
169}