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