toasty_core/stmt/
query.rs

1use super::{
2    Delete, ExprSet, Limit, Node, OrderBy, Path, Returning, Select, Source, Statement, Update,
3    UpdateTarget, Values, Visit, VisitMut, With,
4};
5use crate::stmt::{self, ExprSetOp, Filter, SetOp};
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct Query {
9    /// Any CTEs
10    pub with: Option<With>,
11
12    /// The body of the query. Either `SELECT`, `UNION`, `VALUES`, or possibly
13    /// other types of queries depending on database support.
14    pub body: ExprSet,
15
16    /// When `true`, the Query returns a *single* record vs. a list. Note, that
17    /// this is different from `LIMIT 1` as there should only ever be 1 possible
18    /// result. Also, the return type becomes `Record` instead of `List`.
19    pub single: bool,
20
21    /// ORDER BY
22    pub order_by: Option<OrderBy>,
23
24    /// LIMIT and OFFSET (count or keyset)
25    pub limit: Option<Limit>,
26
27    /// FOR { UPDATE | SHARE }
28    pub locks: Vec<Lock>,
29}
30
31#[derive(Debug, Clone, PartialEq)]
32pub enum Lock {
33    Update,
34    Share,
35}
36
37#[derive(Debug)]
38pub struct QueryBuilder {
39    query: Query,
40}
41
42impl Query {
43    pub fn new(body: impl Into<ExprSet>) -> Self {
44        Self {
45            with: None,
46            body: body.into(),
47            single: false,
48            order_by: None,
49            limit: None,
50            locks: vec![],
51        }
52    }
53
54    pub fn new_single(body: impl Into<ExprSet>) -> Self {
55        Self {
56            with: None,
57            body: body.into(),
58            single: true,
59            order_by: None,
60            limit: None,
61            locks: vec![],
62        }
63    }
64
65    pub fn new_select(source: impl Into<Source>, filter: impl Into<Filter>) -> Self {
66        Self::builder(Select::new(source, filter)).build()
67    }
68
69    pub fn builder(body: impl Into<ExprSet>) -> QueryBuilder {
70        QueryBuilder {
71            query: Query::new(body),
72        }
73    }
74
75    pub fn unit() -> Self {
76        Query::new(Values::default())
77    }
78
79    pub fn values(values: impl Into<Values>) -> Self {
80        Self {
81            with: None,
82            body: ExprSet::Values(values.into()),
83            single: false,
84            order_by: None,
85            limit: None,
86            locks: vec![],
87        }
88    }
89
90    pub fn update(self) -> Update {
91        let ExprSet::Select(select) = &self.body else {
92            todo!("stmt={self:#?}");
93        };
94
95        assert!(select.source.is_model());
96
97        stmt::Update {
98            target: UpdateTarget::Query(Box::new(self)),
99            assignments: stmt::Assignments::default(),
100            filter: Filter::default(),
101            condition: stmt::Condition::default(),
102            returning: None,
103        }
104    }
105
106    pub fn delete(self) -> Delete {
107        match self.body {
108            ExprSet::Select(select) => Delete {
109                from: select.source,
110                filter: select.filter,
111                returning: None,
112            },
113            _ => todo!("{self:#?}"),
114        }
115    }
116
117    pub fn add_filter(&mut self, filter: impl Into<Filter>) {
118        self.body.as_select_mut_unwrap().add_filter(filter);
119    }
120
121    pub fn add_union(&mut self, other: impl Into<Self>) {
122        let rhs = other.into();
123
124        match (&mut self.body, rhs.body) {
125            (ExprSet::SetOp(_), ExprSet::SetOp(_)) => todo!(),
126            (ExprSet::SetOp(lhs), rhs) if lhs.is_union() => {
127                lhs.operands.push(rhs);
128            }
129            (_, ExprSet::SetOp(_)) => todo!(),
130            (me, rhs) => {
131                let lhs = std::mem::take(me);
132                *me = ExprSet::SetOp(ExprSetOp {
133                    op: SetOp::Union,
134                    operands: vec![lhs, rhs],
135                });
136            }
137        }
138    }
139
140    pub fn include(&mut self, path: impl Into<Path>) {
141        match &mut self.body {
142            ExprSet::Select(body) => body.include(path),
143            _ => todo!(),
144        }
145    }
146}
147
148impl Statement {
149    pub fn is_query(&self) -> bool {
150        matches!(self, Statement::Query(_))
151    }
152
153    /// Attempts to return a reference to an inner [`Query`].
154    ///
155    /// * If `self` is a [`Statement::Query`], a reference to the inner [`Query`] is
156    ///   returned wrapped in [`Some`].
157    /// * Else, [`None`] is returned.
158    pub fn as_query(&self) -> Option<&Query> {
159        match self {
160            Self::Query(query) => Some(query),
161            _ => None,
162        }
163    }
164
165    /// Returns a mutable reference to the inner [`Query`], if this is a query statement.
166    ///
167    /// * If `self` is a [`Statement::Query`], a mutable reference to the inner [`Query`] is
168    ///   returned wrapped in [`Some`].
169    /// * Else, [`None`] is returned.
170    pub fn as_query_mut(&mut self) -> Option<&mut Query> {
171        match self {
172            Self::Query(query) => Some(query),
173            _ => None,
174        }
175    }
176
177    /// Consumes `self` and returns the inner [`Query`].
178    ///
179    /// # Panics
180    ///
181    /// If `self` is not a [`Statement::Query`].
182    #[track_caller]
183    pub fn into_query(self) -> Query {
184        match self {
185            Self::Query(query) => query,
186            v => panic!("expected `Query`, found {v:#?}"),
187        }
188    }
189}
190
191impl From<Query> for Statement {
192    fn from(value: Query) -> Self {
193        Self::Query(value)
194    }
195}
196
197impl Node for Query {
198    fn visit<V: Visit>(&self, mut visit: V) {
199        visit.visit_stmt_query(self);
200    }
201
202    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
203        visit.visit_stmt_query_mut(self);
204    }
205}
206
207impl QueryBuilder {
208    pub fn with(mut self, with: impl Into<With>) -> Self {
209        self.query.with = Some(with.into());
210        self
211    }
212
213    pub fn locks(mut self, locks: impl Into<Vec<Lock>>) -> Self {
214        self.query.locks = locks.into();
215        self
216    }
217
218    pub fn filter(mut self, filter: impl Into<Filter>) -> Self {
219        let filter = filter.into();
220
221        match &mut self.query.body {
222            ExprSet::Select(select) => {
223                select.filter = filter;
224            }
225            _ => todo!("query={self:#?}"),
226        }
227
228        self
229    }
230
231    pub fn returning(mut self, returning: impl Into<Returning>) -> Self {
232        let returning = returning.into();
233
234        match &mut self.query.body {
235            ExprSet::Select(select) => {
236                select.returning = returning;
237            }
238            _ => todo!(),
239        }
240
241        self
242    }
243
244    pub fn build(self) -> Query {
245        self.query
246    }
247}
248
249impl From<QueryBuilder> for Query {
250    fn from(value: QueryBuilder) -> Self {
251        value.build()
252    }
253}