Skip to main content

drizzle_postgres/builder/
select.rs

1use crate::common::PostgresSchemaType;
2use crate::helpers;
3use crate::traits::PostgresTable;
4use crate::values::PostgresValue;
5use core::fmt::Debug;
6use core::marker::PhantomData;
7use drizzle_core::ToSQL;
8use drizzle_core::traits::SQLTable;
9use paste::paste;
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
22/// Marker for the state after FROM clause
23#[derive(Debug, Clone, Copy, Default)]
24pub struct SelectFromSet;
25
26/// Marker for the state after JOIN clause
27#[derive(Debug, Clone, Copy, Default)]
28pub struct SelectJoinSet;
29
30/// Marker for the state after WHERE clause
31#[derive(Debug, Clone, Copy, Default)]
32pub struct SelectWhereSet;
33
34/// Marker for the state after GROUP BY clause
35#[derive(Debug, Clone, Copy, Default)]
36pub struct SelectGroupSet;
37
38/// Marker for the state after ORDER BY clause
39#[derive(Debug, Clone, Copy, Default)]
40pub struct SelectOrderSet;
41
42/// Marker for the state after LIMIT clause
43#[derive(Debug, Clone, Copy, Default)]
44pub struct SelectLimitSet;
45
46/// Marker for the state after OFFSET clause
47#[derive(Debug, Clone, Copy, Default)]
48pub struct SelectOffsetSet;
49
50/// Marker for the state after set operations (UNION/INTERSECT/EXCEPT)
51#[derive(Debug, Clone, Copy, Default)]
52pub struct SelectSetOpSet;
53
54/// Marker for the state after FOR UPDATE/SHARE clause
55#[derive(Debug, Clone, Copy, Default)]
56pub struct SelectForSet;
57
58//------------------------------------------------------------------------------
59// Join macros (generates all join variants)
60//------------------------------------------------------------------------------
61
62#[doc(hidden)]
63macro_rules! join_impl {
64    () => {
65        join_impl!(natural, Join::new().natural(), drizzle_core::AfterJoin);
66        join_impl!(natural_left, Join::new().natural().left(), drizzle_core::AfterLeftJoin);
67        join_impl!(left, Join::new().left(), drizzle_core::AfterLeftJoin);
68        join_impl!(left_outer, Join::new().left().outer(), drizzle_core::AfterLeftJoin);
69        join_impl!(natural_left_outer, Join::new().natural().left().outer(), drizzle_core::AfterLeftJoin);
70        join_impl!(natural_right, Join::new().natural().right(), drizzle_core::AfterRightJoin);
71        join_impl!(right, Join::new().right(), drizzle_core::AfterRightJoin);
72        join_impl!(right_outer, Join::new().right().outer(), drizzle_core::AfterRightJoin);
73        join_impl!(natural_right_outer, Join::new().natural().right().outer(), drizzle_core::AfterRightJoin);
74        join_impl!(natural_full, Join::new().natural().full(), drizzle_core::AfterFullJoin);
75        join_impl!(full, Join::new().full(), drizzle_core::AfterFullJoin);
76        join_impl!(full_outer, Join::new().full().outer(), drizzle_core::AfterFullJoin);
77        join_impl!(natural_full_outer, Join::new().natural().full().outer(), drizzle_core::AfterFullJoin);
78        join_impl!(inner, Join::new().inner(), drizzle_core::AfterJoin);
79        join_impl!(cross, Join::new().cross(), drizzle_core::AfterJoin);
80
81        // USING variants only for non-natural, non-cross joins
82        join_using_impl!(left, drizzle_core::AfterLeftJoin);
83        join_using_impl!(left_outer, drizzle_core::AfterLeftJoin);
84        join_using_impl!(right, drizzle_core::AfterRightJoin);
85        join_using_impl!(right_outer, drizzle_core::AfterRightJoin);
86        join_using_impl!(full, drizzle_core::AfterFullJoin);
87        join_using_impl!(full_outer, drizzle_core::AfterFullJoin);
88        join_using_impl!(inner, drizzle_core::AfterJoin);
89        join_using_impl!(); // Plain JOIN
90    };
91    ($type:ident, $join_expr:expr, $join_trait:path) => {
92        paste! {
93            /// JOIN with ON clause
94            pub fn [<$type _join>]<J: crate::helpers::JoinArg<'a, T>>(
95                self,
96                arg: J,
97            ) -> SelectBuilder<'a, S, SelectJoinSet, J::JoinedTable, <M as drizzle_core::ScopePush<J::JoinedTable>>::Out, <M as $join_trait<R, J::JoinedTable>>::NewRow, G>
98            where
99                M: $join_trait<R, J::JoinedTable> + drizzle_core::ScopePush<J::JoinedTable>,
100            {
101                use drizzle_core::Join;
102                SelectBuilder {
103                    sql: self.sql.append(arg.into_join_sql($join_expr)),
104                    schema: PhantomData,
105                    state: PhantomData,
106                    table: PhantomData,
107                    marker: PhantomData,
108                    row: PhantomData,
109                    grouped: PhantomData,
110                }
111            }
112        }
113    };
114}
115
116macro_rules! join_using_impl {
117    () => {
118        /// JOIN with USING clause (PostgreSQL-specific)
119        pub fn join_using<U: PostgresTable<'a>>(
120            self,
121            table: U,
122            columns: impl ToSQL<'a, PostgresValue<'a>>,
123        ) -> SelectBuilder<
124            'a,
125            S,
126            SelectJoinSet,
127            U,
128            <M as drizzle_core::ScopePush<U>>::Out,
129            <M as drizzle_core::AfterJoin<R, U>>::NewRow,
130            G,
131        >
132        where
133            M: drizzle_core::AfterJoin<R, U> + drizzle_core::ScopePush<U>,
134        {
135            SelectBuilder {
136                sql: self.sql.append(helpers::join_using(table, columns)),
137                schema: PhantomData,
138                state: PhantomData,
139                table: PhantomData,
140                marker: PhantomData,
141                row: PhantomData,
142                grouped: PhantomData,
143            }
144        }
145    };
146    ($type:ident, $join_trait:path) => {
147        paste! {
148            /// JOIN with USING clause (PostgreSQL-specific)
149            pub fn [<$type _join_using>]<U: PostgresTable<'a>>(
150                self,
151                table: U,
152                columns: impl ToSQL<'a, PostgresValue<'a>>,
153            ) -> SelectBuilder<
154                'a,
155                S,
156                SelectJoinSet,
157                U,
158                <M as drizzle_core::ScopePush<U>>::Out,
159                <M as $join_trait<R, U>>::NewRow,
160                G,
161            >
162            where
163                M: $join_trait<R, U> + drizzle_core::ScopePush<U>,
164            {
165                SelectBuilder {
166                    sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
167                    schema: PhantomData,
168                    state: PhantomData,
169                    table: PhantomData,
170                    marker: PhantomData,
171                    row: PhantomData,
172                    grouped: PhantomData,
173                }
174            }
175        }
176    };
177}
178
179//------------------------------------------------------------------------------
180// Capability trait impls for each state
181//------------------------------------------------------------------------------
182
183// ExecutableState
184impl ExecutableState for SelectFromSet {}
185impl ExecutableState for SelectWhereSet {}
186impl ExecutableState for SelectLimitSet {}
187impl ExecutableState for SelectOffsetSet {}
188impl ExecutableState for SelectOrderSet {}
189impl ExecutableState for SelectGroupSet {}
190impl ExecutableState for SelectJoinSet {}
191impl ExecutableState for SelectSetOpSet {}
192impl ExecutableState for SelectForSet {}
193
194// WhereAllowed
195impl drizzle_core::WhereAllowed for SelectFromSet {}
196impl drizzle_core::WhereAllowed for SelectJoinSet {}
197
198// GroupByAllowed
199impl drizzle_core::GroupByAllowed for SelectFromSet {}
200impl drizzle_core::GroupByAllowed for SelectJoinSet {}
201impl drizzle_core::GroupByAllowed for SelectWhereSet {}
202
203// OrderByAllowed
204impl drizzle_core::OrderByAllowed for SelectFromSet {}
205impl drizzle_core::OrderByAllowed for SelectJoinSet {}
206impl drizzle_core::OrderByAllowed for SelectWhereSet {}
207impl drizzle_core::OrderByAllowed for SelectGroupSet {}
208impl drizzle_core::OrderByAllowed for SelectSetOpSet {}
209
210// LimitAllowed
211impl drizzle_core::LimitAllowed for SelectFromSet {}
212impl drizzle_core::LimitAllowed for SelectJoinSet {}
213impl drizzle_core::LimitAllowed for SelectWhereSet {}
214impl drizzle_core::LimitAllowed for SelectGroupSet {}
215impl drizzle_core::LimitAllowed for SelectOrderSet {}
216impl drizzle_core::LimitAllowed for SelectSetOpSet {}
217
218// OffsetAllowed
219impl drizzle_core::OffsetAllowed for SelectFromSet {}
220impl drizzle_core::OffsetAllowed for SelectLimitSet {}
221impl drizzle_core::OffsetAllowed for SelectSetOpSet {}
222
223// JoinAllowed
224impl drizzle_core::JoinAllowed for SelectFromSet {}
225impl drizzle_core::JoinAllowed for SelectJoinSet {}
226
227// HavingAllowed
228impl drizzle_core::HavingAllowed for SelectGroupSet {}
229
230// GroupByApplied (for aggregate/scalar mixing enforcement)
231impl drizzle_core::GroupByApplied for SelectGroupSet {}
232impl drizzle_core::GroupByApplied for SelectOrderSet {}
233impl drizzle_core::GroupByApplied for SelectLimitSet {}
234impl drizzle_core::GroupByApplied for SelectOffsetSet {}
235impl drizzle_core::GroupByApplied for SelectSetOpSet {}
236
237#[doc(hidden)]
238pub trait AsCteState {}
239
240impl AsCteState for SelectFromSet {}
241impl AsCteState for SelectJoinSet {}
242impl AsCteState for SelectWhereSet {}
243impl AsCteState for SelectGroupSet {}
244impl AsCteState for SelectOrderSet {}
245impl AsCteState for SelectLimitSet {}
246impl AsCteState for SelectOffsetSet {}
247
248//------------------------------------------------------------------------------
249// SelectBuilder Definition
250//------------------------------------------------------------------------------
251
252/// Builds a SELECT query specifically for `PostgreSQL`
253pub type SelectBuilder<'a, Schema, State, Table = (), Marker = (), Row = (), Grouped = ()> =
254    super::QueryBuilder<'a, Schema, State, Table, Marker, Row, Grouped>;
255
256//------------------------------------------------------------------------------
257// Initial State: .from()
258//------------------------------------------------------------------------------
259
260impl<'a, S, M> SelectBuilder<'a, S, SelectInitial, (), M> {
261    /// Specifies the table to select FROM and transitions state.
262    #[inline]
263    #[allow(clippy::type_complexity)]
264    pub fn from<T>(
265        self,
266        query: T,
267    ) -> SelectBuilder<
268        'a,
269        S,
270        SelectFromSet,
271        T,
272        drizzle_core::Scoped<M, drizzle_core::Cons<T, drizzle_core::Nil>>,
273        <M as drizzle_core::ResolveRow<T>>::Row,
274    >
275    where
276        T: ToSQL<'a, PostgresValue<'a>>,
277        M: drizzle_core::ResolveRow<T>,
278    {
279        SelectBuilder {
280            sql: self.sql.append(helpers::from(query)),
281            schema: PhantomData,
282            state: PhantomData,
283            table: PhantomData,
284            marker: PhantomData,
285            row: PhantomData,
286            grouped: PhantomData,
287        }
288    }
289}
290
291//------------------------------------------------------------------------------
292// Capability-gated methods (generic over State)
293//------------------------------------------------------------------------------
294
295// JOIN (available from SelectFromSet and SelectJoinSet)
296impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
297where
298    State: drizzle_core::JoinAllowed,
299{
300    /// Adds an INNER JOIN clause to the query.
301    #[inline]
302    #[allow(clippy::type_complexity)]
303    pub fn join<J: crate::helpers::JoinArg<'a, T>>(
304        self,
305        arg: J,
306    ) -> SelectBuilder<
307        'a,
308        S,
309        SelectJoinSet,
310        J::JoinedTable,
311        <M as drizzle_core::ScopePush<J::JoinedTable>>::Out,
312        <M as drizzle_core::AfterJoin<R, J::JoinedTable>>::NewRow,
313        G,
314    >
315    where
316        M: drizzle_core::AfterJoin<R, J::JoinedTable> + drizzle_core::ScopePush<J::JoinedTable>,
317    {
318        use drizzle_core::Join;
319        SelectBuilder {
320            sql: self.sql.append(arg.into_join_sql(Join::new())),
321            schema: PhantomData,
322            state: PhantomData,
323            table: PhantomData,
324            marker: PhantomData,
325            row: PhantomData,
326            grouped: PhantomData,
327        }
328    }
329
330    join_impl!();
331}
332
333// WHERE (available from SelectFromSet and SelectJoinSet)
334impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
335where
336    State: drizzle_core::WhereAllowed,
337{
338    /// Adds a WHERE clause to filter query results.
339    #[inline]
340    pub fn r#where<E>(self, condition: E) -> SelectBuilder<'a, S, SelectWhereSet, T, M, R, G>
341    where
342        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
343        E::SQLType: drizzle_core::types::BooleanLike,
344    {
345        SelectBuilder {
346            sql: self.sql.append(helpers::r#where(condition)),
347            schema: PhantomData,
348            state: PhantomData,
349            table: PhantomData,
350            marker: PhantomData,
351            row: PhantomData,
352            grouped: PhantomData,
353        }
354    }
355}
356
357// GROUP BY (available from SelectFromSet, SelectJoinSet, SelectWhereSet)
358impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
359where
360    State: drizzle_core::GroupByAllowed,
361{
362    /// Adds a GROUP BY clause to the query.
363    pub fn group_by<Gr>(
364        self,
365        columns: Gr,
366    ) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, Gr::Columns>
367    where
368        Gr: drizzle_core::IntoGroupBy<'a, PostgresValue<'a>>,
369    {
370        SelectBuilder {
371            sql: self.sql.append(helpers::group_by_expr(columns)),
372            schema: PhantomData,
373            state: PhantomData,
374            table: PhantomData,
375            marker: PhantomData,
376            row: PhantomData,
377            grouped: PhantomData,
378        }
379    }
380}
381
382// HAVING (available only from SelectGroupSet)
383impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
384where
385    State: drizzle_core::HavingAllowed,
386{
387    /// Adds a HAVING clause after GROUP BY.
388    pub fn having<E>(self, condition: E) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, G>
389    where
390        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
391        E::SQLType: drizzle_core::types::BooleanLike,
392    {
393        SelectBuilder {
394            sql: self.sql.append(helpers::having(condition)),
395            schema: PhantomData,
396            state: PhantomData,
397            table: PhantomData,
398            marker: PhantomData,
399            row: PhantomData,
400            grouped: PhantomData,
401        }
402    }
403}
404
405// ORDER BY (available from many states)
406impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
407where
408    State: drizzle_core::OrderByAllowed,
409{
410    /// Sorts the query results.
411    #[inline]
412    pub fn order_by<TOrderBy>(
413        self,
414        expressions: TOrderBy,
415    ) -> SelectBuilder<'a, S, SelectOrderSet, T, M, R, G>
416    where
417        TOrderBy: ToSQL<'a, PostgresValue<'a>>,
418    {
419        SelectBuilder {
420            sql: self.sql.append(helpers::order_by(expressions)),
421            schema: PhantomData,
422            state: PhantomData,
423            table: PhantomData,
424            marker: PhantomData,
425            row: PhantomData,
426            grouped: PhantomData,
427        }
428    }
429}
430
431// LIMIT (available from many states)
432impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
433where
434    State: drizzle_core::LimitAllowed,
435{
436    /// Limits the number of rows returned.
437    #[inline]
438    #[must_use]
439    pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G> {
440        SelectBuilder {
441            sql: self.sql.append(helpers::limit(limit)),
442            schema: PhantomData,
443            state: PhantomData,
444            table: PhantomData,
445            marker: PhantomData,
446            row: PhantomData,
447            grouped: PhantomData,
448        }
449    }
450}
451
452// OFFSET (available from SelectFromSet, SelectLimitSet, SelectSetOpSet)
453impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
454where
455    State: drizzle_core::OffsetAllowed,
456{
457    /// Sets the offset for the query results.
458    #[inline]
459    #[must_use]
460    pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G> {
461        SelectBuilder {
462            sql: self.sql.append(helpers::offset(offset)),
463            schema: PhantomData,
464            state: PhantomData,
465            table: PhantomData,
466            marker: PhantomData,
467            row: PhantomData,
468            grouped: PhantomData,
469        }
470    }
471}
472
473//------------------------------------------------------------------------------
474// CTE support
475//------------------------------------------------------------------------------
476
477impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
478where
479    State: AsCteState,
480    T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
481{
482    /// Converts this SELECT query into a typed CTE using alias tag name.
483    #[inline]
484    #[must_use]
485    pub fn into_cte<Tag: drizzle_core::Tag + 'static>(
486        self,
487    ) -> super::CTEView<
488        'a,
489        <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased<Tag>,
490        Self,
491    > {
492        let name = Tag::NAME;
493        super::CTEView::new(
494            <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias::<Tag>(),
495            name,
496            self,
497        )
498    }
499}
500
501//------------------------------------------------------------------------------
502// Set operation support (UNION / INTERSECT / EXCEPT)
503//------------------------------------------------------------------------------
504
505impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
506where
507    State: ExecutableState,
508{
509    /// Combines this query with another using UNION.
510    pub fn union(
511        self,
512        other: impl IntoSelect<'a, S, M, R>,
513    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
514        SelectBuilder {
515            sql: helpers::union(self.sql, other.into_select()),
516            schema: PhantomData,
517            state: PhantomData,
518            table: PhantomData,
519            marker: PhantomData,
520            row: PhantomData,
521            grouped: PhantomData,
522        }
523    }
524
525    /// Combines this query with another using UNION ALL.
526    pub fn union_all(
527        self,
528        other: impl IntoSelect<'a, S, M, R>,
529    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
530        SelectBuilder {
531            sql: helpers::union_all(self.sql, other.into_select()),
532            schema: PhantomData,
533            state: PhantomData,
534            table: PhantomData,
535            marker: PhantomData,
536            row: PhantomData,
537            grouped: PhantomData,
538        }
539    }
540
541    /// Combines this query with another using INTERSECT.
542    pub fn intersect(
543        self,
544        other: impl IntoSelect<'a, S, M, R>,
545    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
546        SelectBuilder {
547            sql: helpers::intersect(self.sql, other.into_select()),
548            schema: PhantomData,
549            state: PhantomData,
550            table: PhantomData,
551            marker: PhantomData,
552            row: PhantomData,
553            grouped: PhantomData,
554        }
555    }
556
557    /// Combines this query with another using INTERSECT ALL.
558    pub fn intersect_all(
559        self,
560        other: impl IntoSelect<'a, S, M, R>,
561    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
562        SelectBuilder {
563            sql: helpers::intersect_all(self.sql, other.into_select()),
564            schema: PhantomData,
565            state: PhantomData,
566            table: PhantomData,
567            marker: PhantomData,
568            row: PhantomData,
569            grouped: PhantomData,
570        }
571    }
572
573    /// Combines this query with another using EXCEPT.
574    pub fn except(
575        self,
576        other: impl IntoSelect<'a, S, M, R>,
577    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
578        SelectBuilder {
579            sql: helpers::except(self.sql, other.into_select()),
580            schema: PhantomData,
581            state: PhantomData,
582            table: PhantomData,
583            marker: PhantomData,
584            row: PhantomData,
585            grouped: PhantomData,
586        }
587    }
588
589    /// Combines this query with another using EXCEPT ALL.
590    pub fn except_all(
591        self,
592        other: impl IntoSelect<'a, S, M, R>,
593    ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
594        SelectBuilder {
595            sql: helpers::except_all(self.sql, other.into_select()),
596            schema: PhantomData,
597            state: PhantomData,
598            table: PhantomData,
599            marker: PhantomData,
600            row: PhantomData,
601            grouped: PhantomData,
602        }
603    }
604}
605
606//------------------------------------------------------------------------------
607// Expr impl for subquery usage
608//------------------------------------------------------------------------------
609
610impl<'a, S, State, T, M, R, G> drizzle_core::expr::Expr<'a, PostgresValue<'a>>
611    for SelectBuilder<'a, S, State, T, M, R, G>
612where
613    State: ExecutableState,
614    M: drizzle_core::expr::SubqueryType<'a, PostgresValue<'a>>,
615{
616    type SQLType = <M as drizzle_core::expr::SubqueryType<'a, PostgresValue<'a>>>::SQLType;
617    type Nullable = drizzle_core::expr::Null;
618    type Aggregate = drizzle_core::expr::Scalar;
619}
620
621//------------------------------------------------------------------------------
622// IntoSelect conversion trait
623//------------------------------------------------------------------------------
624
625/// Conversion trait for types that can become a `SelectBuilder`.
626/// Used by set operations to accept both raw `SelectBuilder` and `DrizzleBuilder`.
627pub trait IntoSelect<'a, S, M, R> {
628    type State: ExecutableState;
629    type Table;
630    fn into_select(self) -> SelectBuilder<'a, S, Self::State, Self::Table, M, R>;
631}
632
633impl<'a, S, State: ExecutableState, T, M, R, G> IntoSelect<'a, S, M, R>
634    for SelectBuilder<'a, S, State, T, M, R, G>
635{
636    type State = State;
637    type Table = T;
638    fn into_select(self) -> SelectBuilder<'a, S, State, T, M, R> {
639        SelectBuilder {
640            sql: self.sql,
641            schema: PhantomData,
642            state: PhantomData,
643            table: PhantomData,
644            marker: PhantomData,
645            row: PhantomData,
646            grouped: PhantomData,
647        }
648    }
649}
650
651//------------------------------------------------------------------------------
652// FOR UPDATE/SHARE Row Locking (PostgreSQL-specific)
653//------------------------------------------------------------------------------
654
655/// Trait for states that can have FOR UPDATE/SHARE clauses applied.
656pub trait ForLockableState {}
657
658impl ForLockableState for SelectFromSet {}
659impl ForLockableState for SelectWhereSet {}
660impl ForLockableState for SelectOrderSet {}
661impl ForLockableState for SelectLimitSet {}
662impl ForLockableState for SelectOffsetSet {}
663impl ForLockableState for SelectJoinSet {}
664impl ForLockableState for SelectGroupSet {}
665
666impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
667where
668    State: ForLockableState,
669{
670    /// Adds FOR UPDATE clause to lock selected rows for update.
671    #[must_use]
672    pub fn for_update(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
673        SelectBuilder {
674            sql: self.sql.append(helpers::for_update()),
675            schema: PhantomData,
676            state: PhantomData,
677            table: PhantomData,
678            marker: PhantomData,
679            row: PhantomData,
680            grouped: PhantomData,
681        }
682    }
683
684    /// Adds FOR SHARE clause to lock selected rows for shared access.
685    #[must_use]
686    pub fn for_share(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
687        SelectBuilder {
688            sql: self.sql.append(helpers::for_share()),
689            schema: PhantomData,
690            state: PhantomData,
691            table: PhantomData,
692            marker: PhantomData,
693            row: PhantomData,
694            grouped: PhantomData,
695        }
696    }
697
698    /// Adds FOR NO KEY UPDATE clause.
699    #[must_use]
700    pub fn for_no_key_update(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
701        SelectBuilder {
702            sql: self.sql.append(helpers::for_no_key_update()),
703            schema: PhantomData,
704            state: PhantomData,
705            table: PhantomData,
706            marker: PhantomData,
707            row: PhantomData,
708            grouped: PhantomData,
709        }
710    }
711
712    /// Adds FOR KEY SHARE clause.
713    #[must_use]
714    pub fn for_key_share(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
715        SelectBuilder {
716            sql: self.sql.append(helpers::for_key_share()),
717            schema: PhantomData,
718            state: PhantomData,
719            table: PhantomData,
720            marker: PhantomData,
721            row: PhantomData,
722            grouped: PhantomData,
723        }
724    }
725
726    /// Adds FOR UPDATE OF table clause.
727    pub fn for_update_of<U: PostgresTable<'a>>(
728        self,
729        table: U,
730    ) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
731        SelectBuilder {
732            sql: self.sql.append(helpers::for_update_of(table.name())),
733            schema: PhantomData,
734            state: PhantomData,
735            table: PhantomData,
736            marker: PhantomData,
737            row: PhantomData,
738            grouped: PhantomData,
739        }
740    }
741
742    /// Adds FOR SHARE OF table clause.
743    pub fn for_share_of<U: PostgresTable<'a>>(
744        self,
745        table: U,
746    ) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
747        SelectBuilder {
748            sql: self.sql.append(helpers::for_share_of(table.name())),
749            schema: PhantomData,
750            state: PhantomData,
751            table: PhantomData,
752            marker: PhantomData,
753            row: PhantomData,
754            grouped: PhantomData,
755        }
756    }
757}
758
759//------------------------------------------------------------------------------
760// Post-FOR State Implementation (NOWAIT / SKIP LOCKED)
761//------------------------------------------------------------------------------
762
763impl<S, T, M, R, G> SelectBuilder<'_, S, SelectForSet, T, M, R, G> {
764    /// Adds NOWAIT option to fail immediately if rows are locked.
765    #[must_use]
766    pub fn nowait(self) -> Self {
767        SelectBuilder {
768            sql: self.sql.append(helpers::nowait()),
769            schema: PhantomData,
770            state: PhantomData,
771            table: PhantomData,
772            marker: PhantomData,
773            row: PhantomData,
774            grouped: PhantomData,
775        }
776    }
777
778    /// Adds SKIP LOCKED option to skip over locked rows.
779    #[must_use]
780    pub fn skip_locked(self) -> Self {
781        SelectBuilder {
782            sql: self.sql.append(helpers::skip_locked()),
783            schema: PhantomData,
784            state: PhantomData,
785            table: PhantomData,
786            marker: PhantomData,
787            row: PhantomData,
788            grouped: PhantomData,
789        }
790    }
791}
792
793#[cfg(test)]
794mod tests {
795    use super::*;
796    use drizzle_core::{SQL, ToSQL};
797
798    #[test]
799    fn test_select_builder_creation() {
800        let builder = SelectBuilder::<(), SelectInitial> {
801            sql: SQL::raw("SELECT *"),
802            schema: PhantomData,
803            state: PhantomData,
804            table: PhantomData,
805            marker: PhantomData,
806            row: PhantomData,
807            grouped: PhantomData,
808        };
809
810        assert_eq!(builder.to_sql().sql(), "SELECT *");
811    }
812}