drizzle_postgres/builder/
select.rs

1use crate::common::PostgresSchemaType;
2use crate::traits::PostgresTable;
3use crate::values::PostgresValue;
4use crate::{ToPostgresSQL, helpers};
5use drizzle_core::SQL;
6use drizzle_core::traits::SQLTable;
7use paste::paste;
8use std::fmt::Debug;
9use std::marker::PhantomData;
10
11// Import the ExecutableState trait
12use super::ExecutableState;
13
14//------------------------------------------------------------------------------
15// Type State Markers
16//------------------------------------------------------------------------------
17
18/// Marker for the initial state of SelectBuilder.
19#[derive(Debug, Clone, Copy, Default)]
20pub struct SelectInitial;
21
22impl SelectInitial {
23    /// Creates a new SelectInitial marker
24    #[inline]
25    pub const fn new() -> Self {
26        Self
27    }
28}
29
30/// Marker for the state after FROM clause
31#[derive(Debug, Clone, Copy, Default)]
32pub struct SelectFromSet;
33
34/// Marker for the state after JOIN clause
35#[derive(Debug, Clone, Copy, Default)]
36pub struct SelectJoinSet;
37
38/// Marker for the state after WHERE clause
39#[derive(Debug, Clone, Copy, Default)]
40pub struct SelectWhereSet;
41
42/// Marker for the state after GROUP BY clause
43#[derive(Debug, Clone, Copy, Default)]
44pub struct SelectGroupSet;
45
46/// Marker for the state after ORDER BY clause
47#[derive(Debug, Clone, Copy, Default)]
48pub struct SelectOrderSet;
49
50/// Marker for the state after LIMIT clause
51#[derive(Debug, Clone, Copy, Default)]
52pub struct SelectLimitSet;
53
54/// Marker for the state after OFFSET clause
55#[derive(Debug, Clone, Copy, Default)]
56pub struct SelectOffsetSet;
57
58// Const constructors for all marker types
59impl SelectFromSet {
60    #[inline]
61    pub const fn new() -> Self {
62        Self
63    }
64}
65impl SelectJoinSet {
66    #[inline]
67    pub const fn new() -> Self {
68        Self
69    }
70}
71impl SelectWhereSet {
72    #[inline]
73    pub const fn new() -> Self {
74        Self
75    }
76}
77impl SelectGroupSet {
78    #[inline]
79    pub const fn new() -> Self {
80        Self
81    }
82}
83impl SelectOrderSet {
84    #[inline]
85    pub const fn new() -> Self {
86        Self
87    }
88}
89impl SelectLimitSet {
90    #[inline]
91    pub const fn new() -> Self {
92        Self
93    }
94}
95impl SelectOffsetSet {
96    #[inline]
97    pub const fn new() -> Self {
98        Self
99    }
100}
101
102#[doc(hidden)]
103macro_rules! join_impl {
104    () => {
105        join_impl!(natural);
106        join_impl!(natural_left);
107        join_impl!(left);
108        join_impl!(left_outer);
109        join_impl!(natural_left_outer);
110        join_impl!(natural_right);
111        join_impl!(right);
112        join_impl!(right_outer);
113        join_impl!(natural_right_outer);
114        join_impl!(natural_full);
115        join_impl!(full);
116        join_impl!(full_outer);
117        join_impl!(natural_full_outer);
118        join_impl!(inner);
119        join_impl!(cross);
120
121        // USING variants only for non-natural, non-cross joins
122        join_using_impl!(left);
123        join_using_impl!(left_outer);
124        join_using_impl!(right);
125        join_using_impl!(right_outer);
126        join_using_impl!(full);
127        join_using_impl!(full_outer);
128        join_using_impl!(inner);
129        join_using_impl!(); // Plain JOIN
130    };
131    ($type:ident) => {
132        paste! {
133            /// JOIN with ON clause
134            pub fn [<$type _join>]<U:  PostgresTable<'a>>(
135                self,
136                table: U,
137                condition: impl ToPostgresSQL<'a>,
138            ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
139                SelectBuilder {
140                    sql: self.sql.append(helpers::[<$type _join>](table, condition)),
141                    schema: PhantomData,
142                    state: PhantomData,
143                    table: PhantomData,
144                }
145            }
146        }
147    };
148}
149
150macro_rules! join_using_impl {
151    () => {
152        /// JOIN with USING clause (PostgreSQL-specific)
153        pub fn join_using<U: PostgresTable<'a>>(
154            self,
155            table: U,
156            columns: impl ToPostgresSQL<'a>,
157        ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
158            SelectBuilder {
159                sql: self.sql.append(helpers::join_using(table, columns)),
160                schema: PhantomData,
161                state: PhantomData,
162                table: PhantomData,
163            }
164        }
165    };
166    ($type:ident) => {
167        paste! {
168            /// JOIN with USING clause (PostgreSQL-specific)
169            pub fn [<$type _join_using>]<U:  PostgresTable<'a>>(
170                self,
171                table: U,
172                columns: impl ToPostgresSQL<'a>,
173            ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
174                SelectBuilder {
175                    sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
176                    schema: PhantomData,
177                    state: PhantomData,
178                    table: PhantomData,
179                }
180            }
181        }
182    };
183}
184
185// Mark states that can execute queries as implementing the ExecutableState trait
186impl ExecutableState for SelectFromSet {}
187impl ExecutableState for SelectWhereSet {}
188impl ExecutableState for SelectLimitSet {}
189impl ExecutableState for SelectOffsetSet {}
190impl ExecutableState for SelectOrderSet {}
191impl ExecutableState for SelectGroupSet {}
192impl ExecutableState for SelectJoinSet {}
193
194//------------------------------------------------------------------------------
195// SelectBuilder Definition
196//------------------------------------------------------------------------------
197
198/// Builds a SELECT query specifically for PostgreSQL
199pub type SelectBuilder<'a, Schema, State, Table = ()> =
200    super::QueryBuilder<'a, Schema, State, Table>;
201
202//------------------------------------------------------------------------------
203// Initial State Implementation
204//------------------------------------------------------------------------------
205
206impl<'a, S> SelectBuilder<'a, S, SelectInitial> {
207    /// Specifies the table to select FROM and transitions state
208    #[inline]
209    pub fn from<T>(self, query: T) -> SelectBuilder<'a, S, SelectFromSet, T>
210    where
211        T: ToPostgresSQL<'a>,
212    {
213        SelectBuilder {
214            sql: self.sql.append(helpers::from(query)),
215            schema: PhantomData,
216            state: PhantomData,
217            table: PhantomData,
218        }
219    }
220}
221
222//------------------------------------------------------------------------------
223// Post-FROM State Implementation
224//------------------------------------------------------------------------------
225
226impl<'a, S, T> SelectBuilder<'a, S, SelectFromSet, T>
227where
228    T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
229{
230    /// Adds a JOIN clause to the query
231    #[inline]
232    pub fn join<U: PostgresTable<'a>>(
233        self,
234        table: U,
235        condition: SQL<'a, PostgresValue<'a>>,
236    ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
237        SelectBuilder {
238            sql: self.sql.append(helpers::join(table, condition)),
239            schema: PhantomData,
240            state: PhantomData,
241            table: PhantomData,
242        }
243    }
244
245    join_impl!();
246
247    #[inline]
248    pub fn r#where(
249        self,
250        condition: SQL<'a, PostgresValue<'a>>,
251    ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
252        SelectBuilder {
253            sql: self.sql.append(helpers::r#where(condition)),
254            schema: PhantomData,
255            state: PhantomData,
256            table: PhantomData,
257        }
258    }
259
260    /// Adds a GROUP BY clause to the query
261    pub fn group_by(
262        self,
263        expressions: Vec<SQL<'a, PostgresValue<'a>>>,
264    ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
265        SelectBuilder {
266            sql: self.sql.append(helpers::group_by(expressions)),
267            schema: PhantomData,
268            state: PhantomData,
269            table: PhantomData,
270        }
271    }
272
273    /// Limits the number of rows returned
274    #[inline]
275    pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
276        SelectBuilder {
277            sql: self.sql.append(helpers::limit(limit)),
278            schema: PhantomData,
279            state: PhantomData,
280            table: PhantomData,
281        }
282    }
283
284    /// Sets the offset for the query results
285    #[inline]
286    pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
287        SelectBuilder {
288            sql: self.sql.append(helpers::offset(offset)),
289            schema: PhantomData,
290            state: PhantomData,
291            table: PhantomData,
292        }
293    }
294
295    /// Sorts the query results
296    #[inline]
297    pub fn order_by<TOrderBy>(
298        self,
299        expressions: TOrderBy,
300    ) -> SelectBuilder<'a, S, SelectOrderSet, T>
301    where
302        TOrderBy: ToPostgresSQL<'a>,
303    {
304        SelectBuilder {
305            sql: self.sql.append(helpers::order_by(expressions)),
306            schema: PhantomData,
307            state: PhantomData,
308            table: PhantomData,
309        }
310    }
311}
312
313//------------------------------------------------------------------------------
314// Post-JOIN State Implementation
315//------------------------------------------------------------------------------
316
317impl<'a, S, T> SelectBuilder<'a, S, SelectJoinSet, T> {
318    /// Adds a WHERE condition after a JOIN
319    #[inline]
320    pub fn r#where(
321        self,
322        condition: SQL<'a, PostgresValue<'a>>,
323    ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
324        SelectBuilder {
325            sql: self.sql.append(crate::helpers::r#where(condition)),
326            schema: PhantomData,
327            state: PhantomData,
328            table: PhantomData,
329        }
330    }
331    /// Sorts the query results
332    #[inline]
333    pub fn order_by<TOrderBy>(
334        self,
335        expressions: TOrderBy,
336    ) -> SelectBuilder<'a, S, SelectOrderSet, T>
337    where
338        TOrderBy: ToPostgresSQL<'a>,
339    {
340        SelectBuilder {
341            sql: self.sql.append(helpers::order_by(expressions)),
342            schema: PhantomData,
343            state: PhantomData,
344            table: PhantomData,
345        }
346    }
347    /// Adds a JOIN clause to the query
348    #[inline]
349    pub fn join<U: PostgresTable<'a>>(
350        self,
351        table: U,
352        condition: SQL<'a, PostgresValue<'a>>,
353    ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
354        SelectBuilder {
355            sql: self.sql.append(helpers::join(table, condition)),
356            schema: PhantomData,
357            state: PhantomData,
358            table: PhantomData,
359        }
360    }
361    join_impl!();
362}
363
364//------------------------------------------------------------------------------
365// Post-WHERE State Implementation
366//------------------------------------------------------------------------------
367
368impl<'a, S, T> SelectBuilder<'a, S, SelectWhereSet, T> {
369    /// Adds a GROUP BY clause after a WHERE
370    pub fn group_by(
371        self,
372        expressions: Vec<SQL<'a, PostgresValue<'a>>>,
373    ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
374        SelectBuilder {
375            sql: self.sql.append(helpers::group_by(expressions)),
376            schema: PhantomData,
377            state: PhantomData,
378            table: PhantomData,
379        }
380    }
381
382    /// Adds an ORDER BY clause after a WHERE
383    pub fn order_by<TOrderBy>(
384        self,
385        expressions: TOrderBy,
386    ) -> SelectBuilder<'a, S, SelectOrderSet, T>
387    where
388        TOrderBy: ToPostgresSQL<'a>,
389    {
390        SelectBuilder {
391            sql: self.sql.append(helpers::order_by(expressions)),
392            schema: PhantomData,
393            state: PhantomData,
394            table: PhantomData,
395        }
396    }
397
398    /// Adds a LIMIT clause after a WHERE
399    pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
400        SelectBuilder {
401            sql: self.sql.append(helpers::limit(limit)),
402            schema: PhantomData,
403            state: PhantomData,
404            table: PhantomData,
405        }
406    }
407}
408
409//------------------------------------------------------------------------------
410// Post-GROUP BY State Implementation
411//------------------------------------------------------------------------------
412
413impl<'a, S, T> SelectBuilder<'a, S, SelectGroupSet, T> {
414    /// Adds a HAVING clause after GROUP BY
415    pub fn having(
416        self,
417        condition: SQL<'a, PostgresValue<'a>>,
418    ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
419        SelectBuilder {
420            sql: self.sql.append(helpers::having(condition)),
421            schema: PhantomData,
422            state: PhantomData,
423            table: PhantomData,
424        }
425    }
426
427    /// Adds an ORDER BY clause after GROUP BY
428    pub fn order_by<TOrderBy>(
429        self,
430        expressions: TOrderBy,
431    ) -> SelectBuilder<'a, S, SelectOrderSet, T>
432    where
433        TOrderBy: ToPostgresSQL<'a>,
434    {
435        SelectBuilder {
436            sql: self.sql.append(helpers::order_by(expressions)),
437            schema: PhantomData,
438            state: PhantomData,
439            table: PhantomData,
440        }
441    }
442}
443
444//------------------------------------------------------------------------------
445// Post-ORDER BY State Implementation
446//------------------------------------------------------------------------------
447
448impl<'a, S, T> SelectBuilder<'a, S, SelectOrderSet, T> {
449    /// Adds a LIMIT clause after ORDER BY
450    pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
451        SelectBuilder {
452            sql: self.sql.append(helpers::limit(limit)),
453            schema: PhantomData,
454            state: PhantomData,
455            table: PhantomData,
456        }
457    }
458}
459
460//------------------------------------------------------------------------------
461// Post-LIMIT State Implementation
462//------------------------------------------------------------------------------
463
464impl<'a, S, T> SelectBuilder<'a, S, SelectLimitSet, T> {
465    /// Adds an OFFSET clause after LIMIT
466    pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
467        SelectBuilder {
468            sql: self.sql.append(helpers::offset(offset)),
469            schema: PhantomData,
470            state: PhantomData,
471            table: PhantomData,
472        }
473    }
474}
475
476#[cfg(test)]
477mod tests {
478    use super::*;
479    use drizzle_core::{SQL, ToSQL};
480
481    #[test]
482    fn test_select_builder_creation() {
483        let builder = SelectBuilder::<(), SelectInitial> {
484            sql: SQL::raw("SELECT *"),
485            schema: PhantomData,
486            state: PhantomData,
487            table: PhantomData,
488        };
489
490        assert_eq!(builder.to_sql().sql(), "SELECT *");
491    }
492}