Skip to main content

keelson_psql/statement/
table.rs

1use keelson_core::clause::{
2    Combines, Fetch, HasCombines, HasFetch, HasLimit, HasLocks, HasOffset, HasOrderBy, HasTableRef,
3    HasWith, Limit, Locks, Offset, OrderBy, TableRef, 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/// The PostgreSQL `TABLE` command — `TABLE name` is `SELECT * FROM name`.
11///
12/// From <https://www.postgresql.org/docs/17/sql-select.html> (the `TABLE`
13/// section): `TABLE [ ONLY ] table_name [ * ]`, and
14///
15/// > it can be used as a top-level command or as a space-saving syntax variant
16/// > in parts of complex queries. Only the `WITH`, `UNION`, `INTERSECT`,
17/// > `EXCEPT`, `ORDER BY`, `LIMIT`, `OFFSET`, `FETCH` and `FOR` locking clauses
18/// > can be used with `TABLE`; the `WHERE` clause and any form of aggregation
19/// > cannot be used.
20///
21/// The struct is that sentence: exactly those clauses, and no others — a
22/// `table::where_` is a compile error because `HasWhere` is not implemented.
23#[derive(Debug, Clone, Default)]
24pub struct TableQuery {
25    /// `WITH …`.
26    pub with: With,
27    /// The table: `TABLE [ ONLY ] name`. Grammar takes a name only — no alias,
28    /// no column list.
29    pub table: TableRef,
30    /// `ORDER BY …` — this statement's own.
31    pub order_by: OrderBy,
32    /// `LIMIT …` — this statement's own.
33    pub limit: Limit,
34    /// `OFFSET …` — this statement's own.
35    pub offset: Offset,
36    /// `FETCH …` — this statement's own.
37    pub fetch: Fetch,
38    /// `FOR UPDATE …`.
39    pub locks: Locks,
40    /// The set operations, and the trailing clauses that belong to their result.
41    pub combines: Combines,
42}
43
44impl TableQuery {
45    /// A `TABLE` with no table yet — which does not build until it has one.
46    pub fn new() -> TableQuery {
47        TableQuery::default()
48    }
49
50    /// Apply more mods to an existing query.
51    pub fn apply(&mut self, mods: impl Mod<TableQuery>) {
52        mods.apply(self);
53    }
54
55    /// Whether this statement carries a clause a set operation would silently
56    /// steal — the condition [`Combines::parenthesises_leading_query`] asks
57    /// about.
58    fn has_tail_clauses(&self) -> bool {
59        !self.order_by.is_empty()
60            || !self.limit.is_empty()
61            || !self.offset.is_empty()
62            || !self.fetch.is_empty()
63            || !self.locks.is_empty()
64    }
65}
66
67impl Expression for TableQuery {
68    fn write_sql(&self, w: &mut SqlWriter<'_>) {
69        // One production, two spellings — same rule as SELECT.
70        if !self.limit.is_empty() && !self.fetch.is_empty() {
71            w.record_error(Error::conflicting_clauses("LIMIT", "FETCH"));
72            return;
73        }
74        if self.table.is_empty() {
75            w.record_error(Error::Incomplete("the table of a TABLE statement"));
76            return;
77        }
78
79        w.write_if(!self.with.is_empty(), "", &self.with, " ");
80
81        let parens = self
82            .combines
83            .parenthesises_leading_query(self.has_tail_clauses());
84        if parens {
85            w.push_str("(");
86        }
87
88        w.push_str("TABLE ");
89        w.write_expr(&self.table);
90
91        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
92        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
93        w.write_if(!self.offset.is_empty(), " ", &self.offset, "");
94        w.write_if(!self.fetch.is_empty(), " ", &self.fetch, "");
95        w.write_if(!self.locks.is_empty(), " ", &self.locks, "");
96
97        if parens {
98            w.push_str(")");
99        }
100
101        w.write_if(!self.combines.is_empty(), " ", &self.combines, "");
102    }
103}
104
105impl Query for TableQuery {
106    fn query_type(&self) -> QueryType {
107        // `TABLE name` is `SELECT * FROM name`, rows and all.
108        QueryType::Select
109    }
110
111    fn dialect(&self) -> &dyn Dialect {
112        &Psql
113    }
114}
115
116impl<H, L, M> QueryExtensions<H, L, M> for TableQuery {}
117
118impl IntoExpr for TableQuery {
119    fn into_expr(self) -> Expr {
120        crate::query(self)
121    }
122}
123
124impl IntoExprList for TableQuery {
125    fn into_expr_list(self) -> Vec<Expr> {
126        vec![self.into_expr()]
127    }
128}
129
130impl HasWith for TableQuery {
131    fn with_mut(&mut self) -> &mut With {
132        &mut self.with
133    }
134}
135
136impl HasTableRef for TableQuery {
137    fn table_ref_mut(&mut self) -> &mut TableRef {
138        &mut self.table
139    }
140}
141
142impl HasOrderBy for TableQuery {
143    fn order_by_mut(&mut self) -> &mut OrderBy {
144        &mut self.order_by
145    }
146}
147
148impl HasLimit for TableQuery {
149    fn limit_mut(&mut self) -> &mut Limit {
150        &mut self.limit
151    }
152}
153
154impl HasOffset for TableQuery {
155    fn offset_mut(&mut self) -> &mut Offset {
156        &mut self.offset
157    }
158}
159
160impl HasFetch for TableQuery {
161    fn fetch_mut(&mut self) -> &mut Fetch {
162        &mut self.fetch
163    }
164}
165
166impl HasLocks for TableQuery {
167    fn locks_mut(&mut self) -> &mut Locks {
168        &mut self.locks
169    }
170}
171
172impl HasCombines for TableQuery {
173    fn combines_mut(&mut self) -> &mut Combines {
174        &mut self.combines
175    }
176}