Skip to main content

drizzle_sqlite/builder/
select.rs

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