Skip to main content

keelson_psql/statement/
values.rs

1use keelson_core::clause::{
2    Combines, Fetch, HasCombines, HasFetch, HasLimit, HasOffset, HasOrderBy, HasValues, HasWith,
3    Limit, Offset, OrderBy, Values, With,
4};
5use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
6use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
7
8use crate::Psql;
9
10/// A standalone PostgreSQL `VALUES` statement.
11///
12/// From <https://www.postgresql.org/docs/17/sql-values.html>:
13///
14/// ```text
15/// VALUES ( expression [, ...] ) [, ...]
16///     [ ORDER BY sort_expression [ ASC | DESC | USING operator ] [, ...] ]
17///     [ LIMIT { count | ALL } ]
18///     [ OFFSET start [ ROW | ROWS ] ]
19///     [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
20/// ```
21///
22/// `VALUES` is a `simple_select` alternative in `gram.y`, so it also takes a
23/// leading `WITH` and participates in set operations — `VALUES (1) UNION
24/// SELECT …` — and the tail clauses of a combination live in [`Combines`], as
25/// they do for a `SELECT`. The columns of the result are named `column1`,
26/// `column2`, …, which is what an `ORDER BY` here refers to (or `ORDER BY 1`).
27///
28/// It has no `FOR UPDATE`: PostgreSQL rejects a locking clause on `VALUES`, so
29/// `HasLocks` is not implemented and `values::for_update` does not exist.
30///
31/// The rows are the same [`Values`] clause an `INSERT` holds, minus its
32/// query alternative: a standalone `VALUES` *is* rows, so a
33/// [`Values::query`] set here is a recorded build error rather than something to
34/// render around.
35#[derive(Debug, Clone, Default)]
36pub struct ValuesQuery {
37    /// `WITH …`.
38    pub with: With,
39    /// The rows.
40    pub values: Values,
41    /// `ORDER BY …` — this statement's own.
42    pub order_by: OrderBy,
43    /// `LIMIT …` — this statement's own.
44    pub limit: Limit,
45    /// `OFFSET …` — this statement's own.
46    pub offset: Offset,
47    /// `FETCH …` — this statement's own.
48    pub fetch: Fetch,
49    /// The set operations, and the trailing clauses that belong to their result.
50    pub combines: Combines,
51}
52
53impl ValuesQuery {
54    /// A `VALUES` with no rows yet — which does not build until it has one.
55    pub fn new() -> ValuesQuery {
56        ValuesQuery::default()
57    }
58
59    /// Apply more mods to an existing query.
60    pub fn apply(&mut self, mods: impl Mod<ValuesQuery>) {
61        mods.apply(self);
62    }
63
64    /// Whether this statement carries a clause a set operation would silently
65    /// steal — the condition [`Combines::parenthesises_leading_query`] asks
66    /// about.
67    fn has_tail_clauses(&self) -> bool {
68        !self.order_by.is_empty()
69            || !self.limit.is_empty()
70            || !self.offset.is_empty()
71            || !self.fetch.is_empty()
72    }
73}
74
75impl Expression for ValuesQuery {
76    fn write_sql(&self, w: &mut SqlWriter<'_>) {
77        // One production, two spellings — same rule as SELECT.
78        if !self.limit.is_empty() && !self.fetch.is_empty() {
79            w.record_error(Error::conflicting_clauses("LIMIT", "FETCH"));
80            return;
81        }
82        // The query alternative of the shared clause belongs to INSERT; a
83        // standalone VALUES has no slot for it, and silently dropping either
84        // part would render a statement the caller did not write.
85        if self.values.query.is_some() {
86            w.record_error(Error::other(
87                "a standalone VALUES statement takes rows; a source query belongs to INSERT",
88            ));
89            return;
90        }
91        if self.values.rows.is_empty() {
92            w.record_error(Error::Incomplete("the rows of a VALUES statement"));
93            return;
94        }
95
96        w.write_if(!self.with.is_empty(), "", &self.with, " ");
97
98        let parens = self
99            .combines
100            .parenthesises_leading_query(self.has_tail_clauses());
101        if parens {
102            w.push_str("(");
103        }
104
105        w.write_expr(&self.values);
106
107        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
108        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
109        w.write_if(!self.offset.is_empty(), " ", &self.offset, "");
110        w.write_if(!self.fetch.is_empty(), " ", &self.fetch, "");
111
112        if parens {
113            w.push_str(")");
114        }
115
116        w.write_if(!self.combines.is_empty(), " ", &self.combines, "");
117    }
118}
119
120impl Query for ValuesQuery {
121    fn query_type(&self) -> QueryType {
122        // Rows come back, exactly as from a SELECT — and keelson-sqlite reads
123        // its VALUES select-core the same way.
124        QueryType::Select
125    }
126
127    fn dialect(&self) -> &dyn Dialect {
128        &Psql
129    }
130}
131
132impl<H, L, M> QueryExtensions<H, L, M> for ValuesQuery {}
133
134impl IntoExpr for ValuesQuery {
135    fn into_expr(self) -> Expr {
136        crate::query(self)
137    }
138}
139
140impl IntoExprList for ValuesQuery {
141    fn into_expr_list(self) -> Vec<Expr> {
142        vec![self.into_expr()]
143    }
144}
145
146impl HasWith for ValuesQuery {
147    fn with_mut(&mut self) -> &mut With {
148        &mut self.with
149    }
150}
151
152impl HasValues for ValuesQuery {
153    fn values_mut(&mut self) -> &mut Values {
154        &mut self.values
155    }
156}
157
158impl HasOrderBy for ValuesQuery {
159    fn order_by_mut(&mut self) -> &mut OrderBy {
160        &mut self.order_by
161    }
162}
163
164impl HasLimit for ValuesQuery {
165    fn limit_mut(&mut self) -> &mut Limit {
166        &mut self.limit
167    }
168}
169
170impl HasOffset for ValuesQuery {
171    fn offset_mut(&mut self) -> &mut Offset {
172        &mut self.offset
173    }
174}
175
176impl HasFetch for ValuesQuery {
177    fn fetch_mut(&mut self) -> &mut Fetch {
178        &mut self.fetch
179    }
180}
181
182impl HasCombines for ValuesQuery {
183    fn combines_mut(&mut self) -> &mut Combines {
184        &mut self.combines
185    }
186}