drizzle_postgres/
builder.rs

1use drizzle_core::Token;
2// Re-export common enums and traits from core
3pub use drizzle_core::builder::{BuilderInit, ExecutableState, OrderByClause};
4pub use drizzle_core::{
5    OrderBy, SQL, ToSQL,
6    traits::{SQLSchema, SQLTable},
7};
8
9// Local imports
10use crate::{common::PostgresSchemaType, traits::PostgresTable, values::PostgresValue};
11use std::{fmt::Debug, marker::PhantomData};
12
13// Import modules - these provide specific builder types
14pub mod cte;
15pub mod delete;
16pub mod insert;
17pub mod prepared;
18pub mod select;
19pub mod update;
20
21// Re-export CTE types
22pub use cte::{CTEDefinition, CTEView};
23
24// Export state markers for easier use
25pub use delete::{DeleteInitial, DeleteReturningSet, DeleteWhereSet};
26pub use insert::{
27    Conflict, InsertInitial, InsertOnConflictSet, InsertReturningSet, InsertValuesSet,
28};
29pub use select::{
30    SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet, SelectOffsetSet,
31    SelectOrderSet, SelectWhereSet,
32};
33pub use update::{
34    UpdateFromSet, UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet,
35};
36
37#[derive(Debug, Clone)]
38pub struct CTEInit;
39
40impl ExecutableState for CTEInit {}
41
42/// Main query builder for PostgreSQL
43///
44/// The `S` type parameter represents the schema type, which is used
45/// to ensure type safety when building queries.
46#[derive(Debug, Clone, Default)]
47pub struct QueryBuilder<'a, Schema = (), State = (), Table = ()> {
48    pub sql: SQL<'a, PostgresValue<'a>>,
49    schema: PhantomData<Schema>,
50    state: PhantomData<State>,
51    table: PhantomData<Table>,
52}
53
54//------------------------------------------------------------------------------
55// QueryBuilder Implementation
56//------------------------------------------------------------------------------
57
58impl<'a, Schema, State, Table> ToSQL<'a, PostgresValue<'a>>
59    for QueryBuilder<'a, Schema, State, Table>
60{
61    fn to_sql(&self) -> SQL<'a, PostgresValue<'a>> {
62        self.sql.clone()
63    }
64}
65
66impl<'a> QueryBuilder<'a> {
67    /// Creates a new query builder for the given schema
68    pub const fn new<S>() -> QueryBuilder<'a, S, BuilderInit> {
69        QueryBuilder {
70            sql: SQL::empty(),
71            schema: PhantomData,
72            state: PhantomData,
73            table: PhantomData,
74        }
75    }
76}
77
78impl<'a, Schema> QueryBuilder<'a, Schema, BuilderInit> {
79    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
80    where
81        T: ToSQL<'a, PostgresValue<'a>>,
82    {
83        let sql = crate::helpers::select(columns);
84        select::SelectBuilder {
85            sql,
86            schema: PhantomData,
87            state: PhantomData,
88            table: PhantomData,
89        }
90    }
91
92    /// Begins a SELECT DISTINCT query with the specified columns.
93    ///
94    /// SELECT DISTINCT removes duplicate rows from the result set.
95    pub fn select_distinct<T>(
96        &self,
97        columns: T,
98    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
99    where
100        T: ToSQL<'a, PostgresValue<'a>>,
101    {
102        let sql = crate::helpers::select_distinct(columns);
103        select::SelectBuilder {
104            sql,
105            schema: PhantomData,
106            state: PhantomData,
107            table: PhantomData,
108        }
109    }
110
111    /// Begins a SELECT DISTINCT ON query with the specified columns.
112    pub fn select_distinct_on<On, Columns>(
113        &self,
114        on: On,
115        columns: Columns,
116    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
117    where
118        On: ToSQL<'a, PostgresValue<'a>>,
119        Columns: ToSQL<'a, PostgresValue<'a>>,
120    {
121        let sql = crate::helpers::select_distinct_on(on, columns);
122        select::SelectBuilder {
123            sql,
124            schema: PhantomData,
125            state: PhantomData,
126            table: PhantomData,
127        }
128    }
129}
130
131impl<'a, Schema> QueryBuilder<'a, Schema, CTEInit> {
132    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
133    where
134        T: ToSQL<'a, PostgresValue<'a>>,
135    {
136        let sql = self.sql.clone().append(crate::helpers::select(columns));
137        select::SelectBuilder {
138            sql,
139            schema: PhantomData,
140            state: PhantomData,
141            table: PhantomData,
142        }
143    }
144
145    /// Begins a SELECT DISTINCT query with the specified columns after a CTE.
146    pub fn select_distinct<T>(
147        &self,
148        columns: T,
149    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
150    where
151        T: ToSQL<'a, PostgresValue<'a>>,
152    {
153        let sql = self
154            .sql
155            .clone()
156            .append(crate::helpers::select_distinct(columns));
157        select::SelectBuilder {
158            sql,
159            schema: PhantomData,
160            state: PhantomData,
161            table: PhantomData,
162        }
163    }
164
165    /// Begins a SELECT DISTINCT ON query with the specified columns after a CTE.
166    pub fn select_distinct_on<On, Columns>(
167        &self,
168        on: On,
169        columns: Columns,
170    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
171    where
172        On: ToSQL<'a, PostgresValue<'a>>,
173        Columns: ToSQL<'a, PostgresValue<'a>>,
174    {
175        let sql = self
176            .sql
177            .clone()
178            .append(crate::helpers::select_distinct_on(on, columns));
179        select::SelectBuilder {
180            sql,
181            schema: PhantomData,
182            state: PhantomData,
183            table: PhantomData,
184        }
185    }
186
187    /// Begins an INSERT query after a CTE.
188    pub fn insert<Table>(
189        &self,
190        table: Table,
191    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
192    where
193        Table: PostgresTable<'a>,
194    {
195        let sql = self.sql.clone().append(crate::helpers::insert(table));
196
197        insert::InsertBuilder {
198            sql,
199            schema: PhantomData,
200            state: PhantomData,
201            table: PhantomData,
202        }
203    }
204
205    /// Begins an UPDATE query after a CTE.
206    pub fn update<Table>(
207        &self,
208        table: Table,
209    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
210    where
211        Table: PostgresTable<'a>,
212    {
213        let sql = self.sql.clone().append(crate::helpers::update::<
214            'a,
215            Table,
216            PostgresSchemaType,
217            PostgresValue<'a>,
218        >(table));
219
220        update::UpdateBuilder {
221            sql,
222            schema: PhantomData,
223            state: PhantomData,
224            table: PhantomData,
225        }
226    }
227
228    /// Begins a DELETE query after a CTE.
229    pub fn delete<Table>(
230        &self,
231        table: Table,
232    ) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, Table>
233    where
234        Table: PostgresTable<'a>,
235    {
236        let sql = self.sql.clone().append(crate::helpers::delete::<
237            'a,
238            Table,
239            PostgresSchemaType,
240            PostgresValue<'a>,
241        >(table));
242
243        delete::DeleteBuilder {
244            sql,
245            schema: PhantomData,
246            state: PhantomData,
247            table: PhantomData,
248        }
249    }
250
251    pub fn with<C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
252    where
253        C: CTEDefinition<'a>,
254    {
255        let sql = self
256            .sql
257            .clone()
258            .push(Token::COMMA)
259            .append(cte.cte_definition());
260        QueryBuilder {
261            sql,
262            schema: PhantomData,
263            state: PhantomData,
264            table: PhantomData,
265        }
266    }
267}
268
269impl<'a, Schema> QueryBuilder<'a, Schema, BuilderInit> {
270    pub fn insert<Table>(
271        &self,
272        table: Table,
273    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
274    where
275        Table: PostgresTable<'a>,
276    {
277        let sql = crate::helpers::insert(table);
278
279        insert::InsertBuilder {
280            sql,
281            schema: PhantomData,
282            state: PhantomData,
283            table: PhantomData,
284        }
285    }
286
287    pub fn update<Table>(
288        &self,
289        table: Table,
290    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
291    where
292        Table: PostgresTable<'a>,
293    {
294        let sql = crate::helpers::update::<'a, Table, PostgresSchemaType, PostgresValue<'a>>(table);
295
296        update::UpdateBuilder {
297            sql,
298            schema: PhantomData,
299            state: PhantomData,
300            table: PhantomData,
301        }
302    }
303
304    pub fn delete<Table>(
305        &self,
306        table: Table,
307    ) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, Table>
308    where
309        Table: PostgresTable<'a>,
310    {
311        let sql = crate::helpers::delete::<'a, Table, PostgresSchemaType, PostgresValue<'a>>(table);
312
313        delete::DeleteBuilder {
314            sql,
315            schema: PhantomData,
316            state: PhantomData,
317            table: PhantomData,
318        }
319    }
320
321    pub fn with<C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
322    where
323        C: CTEDefinition<'a>,
324    {
325        let sql = SQL::from(Token::WITH).append(cte.cte_definition());
326        QueryBuilder {
327            sql,
328            schema: PhantomData,
329            state: PhantomData,
330            table: PhantomData,
331        }
332    }
333}
334
335// Marker trait to indicate a query builder state is executable
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[test]
341    fn test_query_builder_new() {
342        let qb = QueryBuilder::new::<()>();
343        let sql = qb.to_sql();
344        assert_eq!(sql.sql(), "");
345        assert_eq!(sql.params().count(), 0);
346    }
347
348    #[test]
349    fn test_builder_init_type() {
350        let _state = BuilderInit;
351    }
352}