Skip to main content

drizzle_sqlite/builder/
select.rs

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