qbrs_core/statement.rs
1//! What `INSERT`, `UPDATE` and `DELETE` have in common: they write to one
2//! table, and any of them can be asked for the rows it touched.
3
4use std::marker::PhantomData;
5
6use crate::dialect::{Dialect, SupportsReturning};
7use crate::expr::Value;
8use crate::render::{QuerySink, SelectItem, Sink, render_select_list};
9use crate::scope::{Cons, Nil, NotNull, Table, TableSlot};
10use crate::select::Selection;
11
12/// A statement that writes to a single table. Sealed: the three writing
13/// statements are the whole set, and `Returning` is defined against this
14/// rather than against each of them.
15pub trait Statement: private::Sealed {
16 type Dialect: Dialect;
17 type Table: Table;
18
19 /// Renders into whatever sink the statement is going into — a
20 /// `QuerySink` when it is the statement, a `FragmentSink` when it is a
21 /// CTE body whose placeholders the host query will number.
22 #[doc(hidden)]
23 fn render_into(&self, sink: &mut dyn Sink);
24
25 /// The dialect is an argument for the reason `Select::to_sql`'s is.
26 fn to_sql(&self, _dialect: Self::Dialect) -> (String, Vec<Value>) {
27 let mut sink = QuerySink::<Self::Dialect>::new();
28 self.render_into(&mut sink);
29 sink.finish()
30 }
31
32 /// `RETURNING`, on whichever of the three this is — the clause is the
33 /// same clause. A distinct type rather than `Self` with a flag set: the
34 /// execution layer needs `Sel`'s concrete type to know what to decode a
35 /// returned row into, and an optional field would erase it.
36 ///
37 /// The selection is checked against [`WrittenTable`] — this statement's
38 /// own row.
39 ///
40 /// **Known limitation**: SQL's `RETURNING` reaches further. A scalar
41 /// subquery there is the deferral
42 /// [`Select::contains`](crate::select::Select::contains) states, and the
43 /// joined columns an `UPDATE .. FROM` / `DELETE .. USING` lets it name
44 /// need those clauses, which aren't built either. Bind this statement as
45 /// a CTE body ([`cte::with`](crate::cte::with)) and join from the outer
46 /// query instead: that is checked, and the write and the read it feeds
47 /// stay one statement.
48 fn returning<Sel, Idx>(self, sel: Sel) -> Returning<Self, Sel>
49 where
50 Self: Sized,
51 Self::Dialect: SupportsReturning,
52 Sel: Selection<WrittenTable<Self::Table>, Idx>,
53 {
54 Returning {
55 returning: sel.items(),
56 statement: self,
57 _marker: PhantomData,
58 }
59 }
60}
61
62pub(crate) mod private {
63 pub trait Sealed {}
64}
65
66/// The scope a `RETURNING` clause is checked against: the table being
67/// written to, and nothing else.
68pub type WrittenTable<T> = Cons<TableSlot<T, NotNull>, Nil>;
69
70/// `<statement> RETURNING <selection>`. A distinct type rather than a flag
71/// on the statement, because `Sel` has to survive to the point rows are
72/// decoded — and one type rather than three, because the clause is the same
73/// clause whichever statement it follows.
74pub struct Returning<S, Sel> {
75 pub(crate) statement: S,
76 pub(crate) returning: Vec<SelectItem>,
77 pub(crate) _marker: PhantomData<fn() -> Sel>,
78}
79
80impl<S: Statement, Sel> Returning<S, Sel> {
81 pub fn to_sql(&self, _dialect: S::Dialect) -> (String, Vec<Value>) {
82 let mut sink = QuerySink::<S::Dialect>::new();
83 self.render_into(&mut sink);
84 sink.finish()
85 }
86
87 pub(crate) fn render_into(&self, sink: &mut dyn Sink) {
88 self.statement.render_into(sink);
89 sink.text(" RETURNING ");
90 render_select_list::<S::Dialect>(&self.returning, sink);
91 }
92}