keelson_sqlite/window.rs
1//! Mods for a window definition — what goes inside `OVER (…)` or after
2//! `WINDOW name AS`.
3//!
4//! From <https://www.sqlite.org/syntax/window-defn.html>:
5//!
6//! ```text
7//! ( [ base-window-name ] [ PARTITION BY expr [, ...] ]
8//! [ ORDER BY ordering-term [, ...] ] [ frame-spec ] )
9//! ```
10//!
11//! The frame is [`sqlite::frame`](crate::frame), a separate module because the same
12//! mods apply to a bare [`Frame`](keelson_core::clause::Frame) as well as to a
13//! window.
14//!
15//! Every part is optional, all of them at once included: `OVER ()` is legal and
16//! means the whole partition, which is [`over(())`](crate::Function::over).
17//!
18//! ```
19//! use keelson_sqlite::{f, frame, quote, window};
20//!
21//! // sum("views") OVER (PARTITION BY "user_id" ORDER BY "id" ROWS UNBOUNDED PRECEDING)
22//! let e = f("sum", quote("views")).over((
23//! window::partition_by(quote("user_id")),
24//! window::order_by(quote("id")),
25//! frame::rows(),
26//! ));
27//! ```
28
29use std::borrow::Cow;
30
31use keelson_core::clause::HasWindow;
32use keelson_core::expr::IntoExprList;
33use keelson_core::{Mod, mod_fn};
34
35pub use crate::shared::order_by;
36
37/// Extend an existing named window, taking its `PARTITION BY` and — unless this one
38/// has its own — its `ORDER BY`.
39///
40/// SQLite refuses this when the base window has a frame specification, which is why
41/// [`Function::over_name`](crate::Function::over_name) exists: that writes
42/// `OVER "w"`, a reference, where this builds `OVER ("w" …)`, a copy.
43pub fn based_on<Q: HasWindow>(name: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
44 let name = name.into();
45 mod_fn(move |q: &mut Q| q.window_mut().based_on = Some(name))
46}
47
48/// `PARTITION BY a, b`. Several calls accumulate.
49pub fn partition_by<Q: HasWindow>(expressions: impl IntoExprList) -> impl Mod<Q> {
50 let expressions = expressions.into_expr_list();
51 mod_fn(move |q: &mut Q| q.window_mut().add_partition_by(expressions))
52}