Skip to main content

keelson_psql/statement/
select.rs

1use keelson_core::clause::{
2    Combines, Fetch, GroupBy, HasCombines, HasFetch, HasGroupBy, HasHaving, HasJoins, HasLimit,
3    HasLocks, HasOffset, HasOrderBy, HasSelectList, HasTableRef, HasWhere, HasWindows, HasWith,
4    Having, Join, Limit, Locks, Offset, OrderBy, SelectList, TableRef, Where, Windows, With,
5};
6use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
7use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
8
9use super::{HasExtraTables, write_from_list};
10use crate::Psql;
11use crate::extras::Distinct;
12
13/// A PostgreSQL `SELECT`.
14///
15/// The field order is the clause order of
16/// <https://www.postgresql.org/docs/17/sql-select.html>:
17///
18/// ```text
19/// [ WITH [ RECURSIVE ] with_query [, ...] ]
20/// SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
21///     [ * | expression [ [ AS ] output_name ] [, ...] ]
22///     [ FROM from_item [, ...] ]
23///     [ WHERE condition ]
24///     [ GROUP BY [ ALL | DISTINCT ] grouping_element [, ...] ]
25///     [ HAVING condition ]
26///     [ WINDOW window_name AS ( window_definition ) [, ...] ]
27///     [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
28///     [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
29///     [ LIMIT { count | ALL } ]
30///     [ OFFSET start [ ROW | ROWS ] ]
31///     [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
32///     [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ]
33///       [ NOWAIT | SKIP LOCKED ] [...] ]
34/// ```
35///
36/// The set-operation line is the one the layout does not mirror, and deliberately:
37/// the trailing clauses after it belong to the **combination**, not to this query,
38/// and PostgreSQL says so —
39///
40/// > Without parentheses, these clauses will be taken to apply to the result of the
41/// > `UNION`, not to its right-hand input expression.
42///
43/// — so this query's own `ORDER BY`/`LIMIT`/`OFFSET`/`FETCH`/`FOR` render where the
44/// fields sit and the whole thing is parenthesised when something is combined onto
45/// it, while the combination's live inside [`Combines`].
46#[derive(Debug, Clone, Default)]
47pub struct SelectQuery {
48    /// `WITH …`.
49    pub with: With,
50    /// `DISTINCT` / `DISTINCT ON (…)`. `None` is `ALL`, the default.
51    pub distinct: Option<Distinct>,
52    /// The projection. Empty renders `*`.
53    pub select_list: SelectList,
54    /// The first `FROM` item, with its joins.
55    pub from: TableRef,
56    /// Further comma-separated `FROM` items.
57    pub extra_from: Vec<TableRef>,
58    /// `WHERE …`.
59    pub where_: Where,
60    /// `GROUP BY …`.
61    pub group_by: GroupBy,
62    /// `HAVING …`.
63    pub having: Having,
64    /// `WINDOW …`.
65    pub windows: Windows,
66    /// `ORDER BY …` — this query's own.
67    pub order_by: OrderBy,
68    /// `LIMIT …` — this query's own.
69    pub limit: Limit,
70    /// `OFFSET …` — this query's own.
71    pub offset: Offset,
72    /// `FETCH …` — this query's own.
73    pub fetch: Fetch,
74    /// `FOR UPDATE …`.
75    pub locks: Locks,
76    /// The set operations, and the trailing clauses that belong to their result.
77    pub combines: Combines,
78}
79
80impl SelectQuery {
81    /// An empty `SELECT`, which renders `SELECT *`.
82    pub fn new() -> SelectQuery {
83        SelectQuery::default()
84    }
85
86    /// Apply more mods to an existing query.
87    pub fn apply(&mut self, mods: impl Mod<SelectQuery>) {
88        mods.apply(self);
89    }
90
91    /// Whether this query carries a clause that a set operation would silently
92    /// steal — the condition [`Combines::parenthesises_leading_query`] asks about.
93    fn has_tail_clauses(&self) -> bool {
94        !self.order_by.is_empty()
95            || !self.limit.is_empty()
96            || !self.offset.is_empty()
97            || !self.fetch.is_empty()
98            || !self.locks.is_empty()
99    }
100}
101
102impl Expression for SelectQuery {
103    fn write_sql(&self, w: &mut SqlWriter<'_>) {
104        // gram.y `select_limit`: `LIMIT` and `FETCH` are one production's two
105        // spellings, so a statement gets one of them, never both. Rendering
106        // both would not parse, and picking one would let mod application
107        // order change meaning — so the collision is recorded instead. (The
108        // combination's own pair is judged the same way, in `Combines`.)
109        if !self.limit.is_empty() && !self.fetch.is_empty() {
110            w.record_error(Error::conflicting_clauses("LIMIT", "FETCH"));
111            return;
112        }
113
114        w.write_if(!self.with.is_empty(), "", &self.with, " ");
115
116        let parens = self
117            .combines
118            .parenthesises_leading_query(self.has_tail_clauses());
119        if parens {
120            w.push_str("(");
121        }
122
123        w.push_str("SELECT ");
124        if let Some(distinct) = &self.distinct {
125            w.write_expr(distinct);
126            w.push_str(" ");
127        }
128        // The one clause whose absent rendering is not empty: `*`.
129        w.write_expr(&self.select_list);
130
131        write_from_list(
132            w,
133            " FROM ",
134            &self.from,
135            &self.extra_from,
136            "the FROM item its joins attach to",
137        );
138
139        w.write_if(!self.where_.is_empty(), " ", &self.where_, "");
140        w.write_if(!self.group_by.is_empty(), " ", &self.group_by, "");
141        w.write_if(!self.having.is_empty(), " ", &self.having, "");
142        w.write_if(!self.windows.is_empty(), " ", &self.windows, "");
143        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
144        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
145        w.write_if(!self.offset.is_empty(), " ", &self.offset, "");
146        w.write_if(!self.fetch.is_empty(), " ", &self.fetch, "");
147        w.write_if(!self.locks.is_empty(), " ", &self.locks, "");
148
149        if parens {
150            w.push_str(")");
151        }
152
153        w.write_if(!self.combines.is_empty(), " ", &self.combines, "");
154    }
155}
156
157impl Query for SelectQuery {
158    fn query_type(&self) -> QueryType {
159        QueryType::Select
160    }
161
162    fn dialect(&self) -> &dyn Dialect {
163        &Psql
164    }
165}
166
167impl<H, L, M> QueryExtensions<H, L, M> for SelectQuery {}
168
169impl IntoExpr for SelectQuery {
170    fn into_expr(self) -> Expr {
171        crate::query(self)
172    }
173}
174
175impl IntoExprList for SelectQuery {
176    fn into_expr_list(self) -> Vec<Expr> {
177        vec![self.into_expr()]
178    }
179}
180
181impl HasWith for SelectQuery {
182    fn with_mut(&mut self) -> &mut With {
183        &mut self.with
184    }
185}
186
187impl HasSelectList for SelectQuery {
188    fn select_list_mut(&mut self) -> &mut SelectList {
189        &mut self.select_list
190    }
191}
192
193impl HasTableRef for SelectQuery {
194    fn table_ref_mut(&mut self) -> &mut TableRef {
195        &mut self.from
196    }
197}
198
199impl HasExtraTables for SelectQuery {
200    fn extra_tables_mut(&mut self) -> &mut Vec<TableRef> {
201        &mut self.extra_from
202    }
203}
204
205impl HasJoins for SelectQuery {
206    fn joins_mut(&mut self) -> &mut Vec<Join> {
207        &mut self.from.joins
208    }
209}
210
211impl HasWhere for SelectQuery {
212    fn where_mut(&mut self) -> &mut Where {
213        &mut self.where_
214    }
215}
216
217impl HasGroupBy for SelectQuery {
218    fn group_by_mut(&mut self) -> &mut GroupBy {
219        &mut self.group_by
220    }
221}
222
223impl HasHaving for SelectQuery {
224    fn having_mut(&mut self) -> &mut Having {
225        &mut self.having
226    }
227}
228
229impl HasWindows for SelectQuery {
230    fn windows_mut(&mut self) -> &mut Windows {
231        &mut self.windows
232    }
233}
234
235impl HasOrderBy for SelectQuery {
236    fn order_by_mut(&mut self) -> &mut OrderBy {
237        &mut self.order_by
238    }
239}
240
241impl HasLimit for SelectQuery {
242    fn limit_mut(&mut self) -> &mut Limit {
243        &mut self.limit
244    }
245}
246
247impl HasOffset for SelectQuery {
248    fn offset_mut(&mut self) -> &mut Offset {
249        &mut self.offset
250    }
251}
252
253impl HasFetch for SelectQuery {
254    fn fetch_mut(&mut self) -> &mut Fetch {
255        &mut self.fetch
256    }
257}
258
259impl HasLocks for SelectQuery {
260    fn locks_mut(&mut self) -> &mut Locks {
261        &mut self.locks
262    }
263}
264
265impl HasCombines for SelectQuery {
266    fn combines_mut(&mut self) -> &mut Combines {
267        &mut self.combines
268    }
269}