Skip to main content

drizzle_postgres/builder/
update.rs

1use crate::common::PostgresSchemaType;
2use crate::values::PostgresValue;
3use core::fmt::Debug;
4use core::marker::PhantomData;
5use drizzle_core::{SQLTable, ToSQL};
6
7// Import the ExecutableState trait
8use super::ExecutableState;
9
10//------------------------------------------------------------------------------
11// Type State Markers
12//------------------------------------------------------------------------------
13
14/// Marker for the initial state of `UpdateBuilder`
15#[derive(Debug, Clone, Copy, Default)]
16pub struct UpdateInitial;
17
18/// Marker for the state after SET clause
19#[derive(Debug, Clone, Copy, Default)]
20pub struct UpdateSetClauseSet;
21
22/// Marker for the state after FROM clause
23#[derive(Debug, Clone, Copy, Default)]
24pub struct UpdateFromSet;
25
26/// Marker for the state after WHERE clause
27#[derive(Debug, Clone, Copy, Default)]
28pub struct UpdateWhereSet;
29
30/// Marker for the state after RETURNING clause
31#[derive(Debug, Clone, Copy, Default)]
32pub struct UpdateReturningSet;
33
34// Mark states that can execute update queries
35impl ExecutableState for UpdateSetClauseSet {}
36impl ExecutableState for UpdateFromSet {}
37impl ExecutableState for UpdateWhereSet {}
38impl ExecutableState for UpdateReturningSet {}
39
40//------------------------------------------------------------------------------
41// UpdateBuilder Definition
42//------------------------------------------------------------------------------
43
44/// Builds an UPDATE query specifically for `PostgreSQL`
45pub type UpdateBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
46    super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
47
48type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
49    <Columns as drizzle_core::IntoSelectTarget>::Marker,
50    drizzle_core::Cons<Table, drizzle_core::Nil>,
51>;
52
53type ReturningRow<Table, Columns> =
54    <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
55
56type ReturningBuilder<'a, S, T, Columns> = UpdateBuilder<
57    'a,
58    S,
59    UpdateReturningSet,
60    T,
61    ReturningMarker<T, Columns>,
62    ReturningRow<T, Columns>,
63>;
64
65//------------------------------------------------------------------------------
66// Initial State Implementation
67//------------------------------------------------------------------------------
68
69impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
70where
71    Table: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
72{
73    /// Sets the values to update and transitions to the `SetClauseSet` state
74    #[inline]
75    pub fn set(
76        self,
77        values: Table::Update,
78    ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
79        let sql = crate::helpers::set::<Table, PostgresSchemaType, PostgresValue<'a>>(&values);
80        drop(values);
81        UpdateBuilder {
82            sql: self.sql.append(sql),
83            schema: PhantomData,
84            state: PhantomData,
85            table: PhantomData,
86            marker: PhantomData,
87            row: PhantomData,
88            grouped: PhantomData,
89        }
90    }
91}
92
93//------------------------------------------------------------------------------
94// Post-SET Implementation
95//------------------------------------------------------------------------------
96
97impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
98    /// Adds a FROM clause and transitions to the `FromSet` state
99    #[inline]
100    pub fn from(
101        self,
102        source: impl ToSQL<'a, PostgresValue<'a>>,
103    ) -> UpdateBuilder<'a, S, UpdateFromSet, T> {
104        let from_sql = crate::helpers::from(source);
105        UpdateBuilder {
106            sql: self.sql.append(from_sql),
107            schema: PhantomData,
108            state: PhantomData,
109            table: PhantomData,
110            marker: PhantomData,
111            row: PhantomData,
112            grouped: PhantomData,
113        }
114    }
115
116    /// Adds a WHERE condition and transitions to the `WhereSet` state
117    #[inline]
118    pub fn r#where<E>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T>
119    where
120        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
121        E::SQLType: drizzle_core::types::BooleanLike,
122    {
123        let where_sql = crate::helpers::r#where(condition);
124        UpdateBuilder {
125            sql: self.sql.append(where_sql),
126            schema: PhantomData,
127            state: PhantomData,
128            table: PhantomData,
129            marker: PhantomData,
130            row: PhantomData,
131            grouped: PhantomData,
132        }
133    }
134
135    /// Adds a RETURNING clause and transitions to the `ReturningSet` state
136    #[inline]
137    pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
138    where
139        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
140        Columns::Marker: drizzle_core::ResolveRow<T>,
141    {
142        let returning_sql = crate::helpers::returning(columns);
143        UpdateBuilder {
144            sql: self.sql.append(returning_sql),
145            schema: PhantomData,
146            state: PhantomData,
147            table: PhantomData,
148            marker: PhantomData,
149            row: PhantomData,
150            grouped: PhantomData,
151        }
152    }
153}
154
155//------------------------------------------------------------------------------
156// Post-FROM Implementation
157//------------------------------------------------------------------------------
158
159impl<'a, S, T> UpdateBuilder<'a, S, UpdateFromSet, T> {
160    /// Adds a WHERE condition after FROM
161    #[inline]
162    pub fn r#where<E>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T>
163    where
164        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
165        E::SQLType: drizzle_core::types::BooleanLike,
166    {
167        let where_sql = crate::helpers::r#where(condition);
168        UpdateBuilder {
169            sql: self.sql.append(where_sql),
170            schema: PhantomData,
171            state: PhantomData,
172            table: PhantomData,
173            marker: PhantomData,
174            row: PhantomData,
175            grouped: PhantomData,
176        }
177    }
178
179    /// Adds a RETURNING clause after FROM
180    #[inline]
181    pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
182    where
183        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
184        Columns::Marker: drizzle_core::ResolveRow<T>,
185    {
186        let returning_sql = crate::helpers::returning(columns);
187        UpdateBuilder {
188            sql: self.sql.append(returning_sql),
189            schema: PhantomData,
190            state: PhantomData,
191            table: PhantomData,
192            marker: PhantomData,
193            row: PhantomData,
194            grouped: PhantomData,
195        }
196    }
197}
198
199//------------------------------------------------------------------------------
200// Post-WHERE Implementation
201//------------------------------------------------------------------------------
202
203impl<'a, S, T> UpdateBuilder<'a, S, UpdateWhereSet, T> {
204    /// Adds a RETURNING clause after WHERE
205    #[inline]
206    pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
207    where
208        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
209        Columns::Marker: drizzle_core::ResolveRow<T>,
210    {
211        let returning_sql = crate::helpers::returning(columns);
212        UpdateBuilder {
213            sql: self.sql.append(returning_sql),
214            schema: PhantomData,
215            state: PhantomData,
216            table: PhantomData,
217            marker: PhantomData,
218            row: PhantomData,
219            grouped: PhantomData,
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227    use drizzle_core::{SQL, ToSQL};
228
229    #[test]
230    fn test_update_builder_creation() {
231        let builder = UpdateBuilder::<(), UpdateInitial, ()> {
232            sql: SQL::raw("UPDATE test"),
233            schema: PhantomData,
234            state: PhantomData,
235            table: PhantomData,
236            marker: PhantomData,
237            row: PhantomData,
238            grouped: PhantomData,
239        };
240
241        assert_eq!(builder.to_sql().sql(), "UPDATE test");
242    }
243}