Skip to main content

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