Skip to main content

drizzle_sqlite/builder/
select.rs

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