Skip to main content

keelson_sqlite/statement/
select.rs

1use keelson_core::clause::{
2    GroupBy, HasGroupBy, HasHaving, HasJoins, HasLimit, HasOffset, HasOrderBy, HasSelectList,
3    HasTableRef, HasValues, HasWhere, HasWindows, HasWith, Having, Join, Limit, Offset, OrderBy,
4    SelectList, TableRef, Values, 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::Sqlite;
11use crate::extras::{Compounds, HasCompounds};
12
13/// A SQLite `SELECT`.
14///
15/// The field order is the clause order of <https://www.sqlite.org/lang_select.html>:
16///
17/// ```text
18/// [ WITH [ RECURSIVE ] common-table-expression [, ...] ]
19/// select-core [ compound-operator select-core ]*
20/// [ ORDER BY ordering-term [, ...] ]
21/// [ LIMIT expr [ ( OFFSET | , ) expr ] ]
22///
23/// select-core:
24///     SELECT [ DISTINCT | ALL ] result-column [, ...]
25///         [ FROM table-or-subquery [, ...] | join-clause ]
26///         [ WHERE expr ]
27///         [ GROUP BY expr [, ...] [ HAVING expr ] ]
28///         [ WINDOW window-name AS window-defn [, ...] ]
29///   | VALUES ( expr [, ...] ) [, ...]
30/// ```
31///
32/// Three things in that grammar are unlike PostgreSQL's, and all three are visible
33/// in this type.
34///
35/// **A compound operand is a bare `select-core`.** There are no parentheses around
36/// it and none may be added — a parenthesised select is a `table-or-subquery` in
37/// SQLite, never a compound operand. So the `ORDER BY` and `LIMIT` after the last
38/// operand are the *only* ones there can be, they always apply to the whole
39/// compound, and this type therefore has one set of them rather than PostgreSQL's
40/// two. See [`Compound`](crate::Compound).
41///
42/// **`OFFSET` lives inside the `LIMIT` production.** `SELECT … OFFSET 5` with no
43/// `LIMIT` is a syntax error, so an offset without a limit is a recorded
44/// [`Error::Incomplete`] rather than SQL that will be rejected later.
45///
46/// **`VALUES (…), (…)` is a `select-core` in its own right.** [`values`](Self::values)
47/// is that alternative: when it is non-empty the statement *is* a `VALUES`
48/// statement, and the clauses that only a `SELECT` core can carry are a recorded
49/// failure rather than silently dropped. A real SQLite additionally refuses
50/// `ORDER BY`/`LIMIT` when the **last** core is a `VALUES`, which its own parser
51/// accepts; that one is left to the engine, since whether it holds depends on what
52/// is compounded after the `VALUES`.
53#[derive(Debug, Clone, Default)]
54pub struct SelectQuery {
55    /// `WITH …`.
56    pub with: With,
57    /// `SELECT DISTINCT`. `false` is `ALL`, the default, which adds nothing.
58    pub distinct: bool,
59    /// The result columns. Empty renders `*`.
60    pub select_list: SelectList,
61    /// The `VALUES (…), (…)` alternative to the whole `SELECT` core.
62    pub values: Values,
63    /// The first `FROM` item, with its joins.
64    pub from: TableRef,
65    /// Further comma-separated `FROM` items.
66    pub extra_from: Vec<TableRef>,
67    /// `WHERE …`.
68    pub where_: Where,
69    /// `GROUP BY …`.
70    pub group_by: GroupBy,
71    /// `HAVING …`.
72    pub having: Having,
73    /// `WINDOW …`.
74    pub windows: Windows,
75    /// The compound operands, applied left to right.
76    pub compounds: Compounds,
77    /// `ORDER BY …`, which belongs to the whole compound when there is one.
78    pub order_by: OrderBy,
79    /// `LIMIT …`.
80    pub limit: Limit,
81    /// `OFFSET …`, which cannot stand without a [`limit`](Self::limit).
82    pub offset: Offset,
83}
84
85impl SelectQuery {
86    /// An empty `SELECT`, which renders `SELECT *`.
87    pub fn new() -> SelectQuery {
88        SelectQuery::default()
89    }
90
91    /// Apply more mods to an existing query.
92    pub fn apply(&mut self, mods: impl Mod<SelectQuery>) {
93        mods.apply(self);
94    }
95
96    /// The first clause set on this query that only a `SELECT` core can carry, if
97    /// any — the check a `VALUES` core has to make before it renders.
98    fn select_only_clause(&self) -> Option<&'static str> {
99        if self.distinct {
100            Some("DISTINCT")
101        } else if !self.select_list.is_empty() {
102            Some("a result-column list")
103        } else if !self.from.is_empty() || !self.extra_from.is_empty() {
104            Some("a FROM clause")
105        } else if !self.where_.is_empty() {
106            Some("a WHERE clause")
107        } else if !self.group_by.is_empty() {
108            Some("a GROUP BY clause")
109        } else if !self.having.is_empty() {
110            Some("a HAVING clause")
111        } else if !self.windows.is_empty() {
112            Some("a WINDOW clause")
113        } else {
114            None
115        }
116    }
117
118    /// Write the `select-core`: either the `VALUES` alternative or the `SELECT` one.
119    fn write_core(&self, w: &mut SqlWriter<'_>) {
120        if !self.values.is_empty() {
121            if let Some(clause) = self.select_only_clause() {
122                w.record_error(Error::other(format!(
123                    "a VALUES statement cannot carry {clause}"
124                )));
125                return;
126            }
127            w.write_expr(&self.values);
128            return;
129        }
130
131        w.push_str("SELECT ");
132        if self.distinct {
133            w.push_str("DISTINCT ");
134        }
135        // The one clause whose absent rendering is not empty: `*`.
136        w.write_expr(&self.select_list);
137
138        write_from_list(
139            w,
140            " FROM ",
141            &self.from,
142            &self.extra_from,
143            "the FROM item its joins attach to",
144        );
145
146        w.write_if(!self.where_.is_empty(), " ", &self.where_, "");
147        w.write_if(!self.group_by.is_empty(), " ", &self.group_by, "");
148        w.write_if(!self.having.is_empty(), " ", &self.having, "");
149        w.write_if(!self.windows.is_empty(), " ", &self.windows, "");
150    }
151}
152
153impl Expression for SelectQuery {
154    fn write_sql(&self, w: &mut SqlWriter<'_>) {
155        w.write_if(!self.with.is_empty(), "", &self.with, " ");
156
157        self.write_core(w);
158
159        w.write_if(!self.compounds.is_empty(), " ", &self.compounds, "");
160        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
161
162        if self.limit.is_empty() && !self.offset.is_empty() {
163            // `LIMIT expr [ ( OFFSET | , ) expr ]`: the offset hangs off the limit
164            // and there is no production that lets it stand alone.
165            w.record_error(Error::Incomplete("the LIMIT that an OFFSET belongs to"));
166            return;
167        }
168        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
169        w.write_if(!self.offset.is_empty(), " ", &self.offset, "");
170    }
171}
172
173impl Query for SelectQuery {
174    fn query_type(&self) -> QueryType {
175        QueryType::Select
176    }
177
178    fn dialect(&self) -> &dyn Dialect {
179        &Sqlite
180    }
181}
182
183impl<H, L, M> QueryExtensions<H, L, M> for SelectQuery {}
184
185impl IntoExpr for SelectQuery {
186    fn into_expr(self) -> Expr {
187        crate::query(self)
188    }
189}
190
191impl IntoExprList for SelectQuery {
192    fn into_expr_list(self) -> Vec<Expr> {
193        vec![self.into_expr()]
194    }
195}
196
197impl HasWith for SelectQuery {
198    fn with_mut(&mut self) -> &mut With {
199        &mut self.with
200    }
201}
202
203impl HasSelectList for SelectQuery {
204    fn select_list_mut(&mut self) -> &mut SelectList {
205        &mut self.select_list
206    }
207}
208
209impl HasValues for SelectQuery {
210    fn values_mut(&mut self) -> &mut Values {
211        &mut self.values
212    }
213}
214
215impl HasTableRef for SelectQuery {
216    fn table_ref_mut(&mut self) -> &mut TableRef {
217        &mut self.from
218    }
219}
220
221impl HasExtraTables for SelectQuery {
222    fn extra_tables_mut(&mut self) -> &mut Vec<TableRef> {
223        &mut self.extra_from
224    }
225}
226
227impl HasJoins for SelectQuery {
228    fn joins_mut(&mut self) -> &mut Vec<Join> {
229        &mut self.from.joins
230    }
231}
232
233impl HasWhere for SelectQuery {
234    fn where_mut(&mut self) -> &mut Where {
235        &mut self.where_
236    }
237}
238
239impl HasGroupBy for SelectQuery {
240    fn group_by_mut(&mut self) -> &mut GroupBy {
241        &mut self.group_by
242    }
243}
244
245impl HasHaving for SelectQuery {
246    fn having_mut(&mut self) -> &mut Having {
247        &mut self.having
248    }
249}
250
251impl HasWindows for SelectQuery {
252    fn windows_mut(&mut self) -> &mut Windows {
253        &mut self.windows
254    }
255}
256
257impl HasOrderBy for SelectQuery {
258    fn order_by_mut(&mut self) -> &mut OrderBy {
259        &mut self.order_by
260    }
261}
262
263impl HasLimit for SelectQuery {
264    fn limit_mut(&mut self) -> &mut Limit {
265        &mut self.limit
266    }
267}
268
269impl HasOffset for SelectQuery {
270    fn offset_mut(&mut self) -> &mut Offset {
271        &mut self.offset
272    }
273}
274
275impl HasCompounds for SelectQuery {
276    fn compounds_mut(&mut self) -> &mut Compounds {
277        &mut self.compounds
278    }
279}