Skip to main content

squonk_ast/ast/
window.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! Window-function clauses: the `OVER` specification, frame, and the SELECT-level
5//! `WINDOW` clause.
6//!
7//! These nodes hang off [`FunctionCall::over`](super::FunctionCall) (an inline or
8//! named `OVER` clause) and [`Select::windows`](super::Select) (the named-window
9//! definitions an `OVER name` reference resolves against). Frame *units* and
10//! *exclusions* are closed tag enums (one canonical shape per construct);
11//! the frame *bounds* carry the optional offset expression they bind.
12
13use super::{Expr, Extension, Ident, NoExt, OrderByExpr};
14use crate::vocab::Meta;
15use thin_vec::ThinVec;
16
17/// The `OVER` clause attached to a window function call.
18///
19/// `OVER name` references a definition from the query's `WINDOW` clause;
20/// `OVER ( … )` carries the definition inline. Boxed in the inline case so the
21/// reference case (just an [`Ident`]) stays small.
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
24#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
25pub enum WindowSpec<X: Extension = NoExt> {
26    /// `OVER window_name` — a reference to a named [`WINDOW`-clause](NamedWindow)
27    /// definition.
28    Named {
29        /// Name referenced by this syntax.
30        name: Ident,
31        /// Source location and node identity.
32        meta: Meta,
33    },
34    /// An inline `OVER (…)` window specification.
35    Inline {
36        /// The inline `OVER (…)` window definition; see [`WindowDefinition`].
37        definition: Box<WindowDefinition<X>>,
38        /// Source location and node identity.
39        meta: Meta,
40    },
41}
42
43/// A window definition: the body of `OVER ( … )` or of a `WINDOW name AS ( … )`
44/// entry.
45///
46/// Every part is optional. `existing` is the leading base-window name PostgreSQL
47/// allows inside the parens (`OVER (w PARTITION BY …)`), which the definition then
48/// extends. Empty `partition_by`/`order_by` and a `None` frame mean the clause was
49/// not written.
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
52#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
53pub struct WindowDefinition<X: Extension = NoExt> {
54    /// A base window name this definition extends, if written first inside the parens.
55    pub existing: Option<Ident>,
56    /// Expressions that partition the input rows.
57    pub partition_by: ThinVec<Expr<X>>,
58    /// Ordering terms in source order.
59    pub order_by: ThinVec<OrderByExpr<X>>,
60    /// The frame clause (`ROWS`/`RANGE`/`GROUPS` …), if written.
61    pub frame: Option<WindowFrame<X>>,
62    /// Source location and node identity.
63    pub meta: Meta,
64}
65
66/// A window frame: `{ ROWS | RANGE | GROUPS } <extent> [ <exclusion> ]`.
67///
68/// `end` is `Some` only for the `BETWEEN <start> AND <end>` spelling; a bare
69/// `<start>` bound leaves it `None`.
70#[derive(Clone, Debug, PartialEq, Eq, Hash)]
71#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
72#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
73pub struct WindowFrame<X: Extension = NoExt> {
74    /// Whether the frame is measured in `ROWS`/`RANGE`/`GROUPS`; see [`WindowFrameUnits`].
75    pub units: WindowFrameUnits,
76    /// The starting frame bound; see [`WindowFrameBound`].
77    pub start: WindowFrameBound<X>,
78    /// Optional end for this syntax.
79    pub end: Option<WindowFrameBound<X>>,
80    /// Optional exclusion for this syntax.
81    pub exclusion: Option<WindowFrameExclusion>,
82    /// Source location and node identity.
83    pub meta: Meta,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
87#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
88#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
89/// The SQL window frame units forms represented by the AST.
90pub enum WindowFrameUnits {
91    /// `ROWS` — the frame is counted in physical rows.
92    Rows,
93    /// `RANGE` — the frame spans rows with peer ordering-key values.
94    Range,
95    /// `GROUPS` — the frame is counted in peer groups.
96    Groups,
97}
98
99/// One bound of a window frame.
100///
101/// The offset forms carry their `<expr> PRECEDING`/`FOLLOWING` distance; the
102/// `UNBOUNDED`/`CURRENT ROW` forms carry their keyword span in `Meta`.
103#[derive(Clone, Debug, PartialEq, Eq, Hash)]
104#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
105#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
106pub enum WindowFrameBound<X: Extension = NoExt> {
107    /// `CURRENT ROW` — the bound at the current row.
108    CurrentRow {
109        /// Source location and node identity.
110        meta: Meta,
111    },
112    /// `UNBOUNDED PRECEDING` — the bound at the partition's first row.
113    UnboundedPreceding {
114        /// Source location and node identity.
115        meta: Meta,
116    },
117    /// `UNBOUNDED FOLLOWING` — the bound at the partition's last row.
118    UnboundedFollowing {
119        /// Source location and node identity.
120        meta: Meta,
121    },
122    /// `<offset> PRECEDING` — a bound the given distance before the current row.
123    Preceding {
124        /// Row or frame offset selected by this syntax.
125        offset: Box<Expr<X>>,
126        /// Source location and node identity.
127        meta: Meta,
128    },
129    /// `<offset> FOLLOWING` — a bound the given distance after the current row.
130    Following {
131        /// Row or frame offset selected by this syntax.
132        offset: Box<Expr<X>>,
133        /// Source location and node identity.
134        meta: Meta,
135    },
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
139#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
140#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
141/// The SQL window frame exclusion forms represented by the AST.
142pub enum WindowFrameExclusion {
143    /// `EXCLUDE CURRENT ROW` — omit the current row from the frame.
144    CurrentRow,
145    /// `EXCLUDE GROUP` — omit the current row and its peers.
146    Group,
147    /// `EXCLUDE TIES` — omit the current row's peers but keep the current row.
148    Ties,
149    /// `EXCLUDE NO OTHERS` — the default; exclude nothing.
150    NoOthers,
151}
152
153#[derive(Clone, Debug, PartialEq, Eq, Hash)]
154#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
155#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
156/// An SQL named window.
157pub struct NamedWindow<X: Extension = NoExt> {
158    /// Name referenced by this syntax.
159    pub name: Ident,
160    /// The window definition bound to the name; see [`WindowDefinition`].
161    pub definition: WindowDefinition<X>,
162    /// Source location and node identity.
163    pub meta: Meta,
164}