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 ///
472 /// Non-aggregate columns in SELECT must appear in the GROUP BY list, with
473 /// one exception: grouping by a table's single-column primary key
474 /// functionally determines the whole row (SQL:1999), so any scalar column
475 /// of that table may be selected. Prefer `.group_by(table.pk)` over
476 /// listing every selected column — it also lets `SQLite` stream groups in
477 /// key order instead of sorting through a temp B-tree.
478 pub fn group_by<Gr>(
479 self,
480 columns: Gr,
481 ) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, Gr::Columns>
482 where
483 Gr: drizzle_core::IntoGroupBy<'a, SQLiteValue<'a>>,
484 {
485 SelectBuilder {
486 sql: self.sql.append(helpers::group_by_expr(columns)),
487 schema: PhantomData,
488 state: PhantomData,
489 table: PhantomData,
490 marker: PhantomData,
491 row: PhantomData,
492 grouped: PhantomData,
493 }
494 }
495}
496
497// HAVING (available only from SelectGroupSet)
498impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
499where
500 State: drizzle_core::HavingAllowed,
501{
502 /// Adds a HAVING clause after GROUP BY.
503 pub fn having<E>(self, condition: E) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, G>
504 where
505 E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
506 E::SQLType: drizzle_core::types::BooleanLike,
507 {
508 SelectBuilder {
509 sql: self.sql.append(helpers::having(condition)),
510 schema: PhantomData,
511 state: PhantomData,
512 table: PhantomData,
513 marker: PhantomData,
514 row: PhantomData,
515 grouped: PhantomData,
516 }
517 }
518}
519
520// ORDER BY (available from many states)
521impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
522where
523 State: drizzle_core::OrderByAllowed,
524{
525 /// Sorts the query results.
526 #[inline]
527 pub fn order_by<TOrderBy>(
528 self,
529 expressions: TOrderBy,
530 ) -> SelectBuilder<'a, S, SelectOrderSet, T, M, R, G>
531 where
532 TOrderBy: drizzle_core::ToSQL<'a, SQLiteValue<'a>>,
533 {
534 SelectBuilder {
535 sql: self.sql.append(helpers::order_by(expressions)),
536 schema: PhantomData,
537 state: PhantomData,
538 table: PhantomData,
539 marker: PhantomData,
540 row: PhantomData,
541 grouped: PhantomData,
542 }
543 }
544}
545
546// LIMIT (available from many states)
547impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
548where
549 State: drizzle_core::LimitAllowed,
550{
551 /// Limits the number of rows returned.
552 ///
553 /// # Panics
554 ///
555 /// Panics when a signed numeric argument is negative or a numeric value
556 /// does not fit in `usize`.
557 #[inline]
558 #[must_use]
559 #[track_caller]
560 pub fn limit<P>(self, limit: P) -> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G>
561 where
562 P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
563 {
564 SelectBuilder {
565 sql: self.sql.append(helpers::limit(limit)),
566 schema: PhantomData,
567 state: PhantomData,
568 table: PhantomData,
569 marker: PhantomData,
570 row: PhantomData,
571 grouped: PhantomData,
572 }
573 }
574}
575
576// OFFSET (available from SelectFromSet and SelectLimitSet)
577impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
578where
579 State: drizzle_core::OffsetAllowed,
580{
581 /// Sets the offset for the query results.
582 ///
583 /// # Panics
584 ///
585 /// Panics when a signed numeric argument is negative or a numeric value
586 /// does not fit in `usize`.
587 #[inline]
588 #[must_use]
589 #[track_caller]
590 pub fn offset<P>(self, offset: P) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G>
591 where
592 P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
593 {
594 SelectBuilder {
595 sql: self.sql.append(helpers::offset(offset)),
596 schema: PhantomData,
597 state: PhantomData,
598 table: PhantomData,
599 marker: PhantomData,
600 row: PhantomData,
601 grouped: PhantomData,
602 }
603 }
604}
605
606//------------------------------------------------------------------------------
607// CTE support
608//------------------------------------------------------------------------------
609
610impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
611where
612 State: AsCteState,
613 T: SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>,
614{
615 /// Converts this SELECT query into a typed CTE using alias tag name.
616 #[inline]
617 #[must_use]
618 pub fn into_cte<Tag: drizzle_core::Tag + 'static>(
619 self,
620 ) -> super::CTEView<
621 'a,
622 <T as SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>>::Aliased<Tag>,
623 Self,
624 > {
625 let name = Tag::NAME;
626 super::CTEView::new(
627 <T as SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>>::alias::<Tag>(),
628 name,
629 self,
630 )
631 }
632}
633
634//------------------------------------------------------------------------------
635// Set operation support (UNION / INTERSECT / EXCEPT)
636//------------------------------------------------------------------------------
637
638impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
639where
640 State: drizzle_core::ExecutableState,
641{
642 /// Combines this query with another using UNION.
643 pub fn union(
644 self,
645 other: impl IntoSelect<'a, S, M, R>,
646 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
647 SelectBuilder {
648 sql: helpers::union(self.sql, other.into_select()),
649 schema: PhantomData,
650 state: PhantomData,
651 table: PhantomData,
652 marker: PhantomData,
653 row: PhantomData,
654 grouped: PhantomData,
655 }
656 }
657
658 /// Combines this query with another using UNION ALL.
659 pub fn union_all(
660 self,
661 other: impl IntoSelect<'a, S, M, R>,
662 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
663 SelectBuilder {
664 sql: helpers::union_all(self.sql, other.into_select()),
665 schema: PhantomData,
666 state: PhantomData,
667 table: PhantomData,
668 marker: PhantomData,
669 row: PhantomData,
670 grouped: PhantomData,
671 }
672 }
673
674 /// Combines this query with another using INTERSECT.
675 pub fn intersect(
676 self,
677 other: impl IntoSelect<'a, S, M, R>,
678 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
679 SelectBuilder {
680 sql: helpers::intersect(self.sql, other.into_select()),
681 schema: PhantomData,
682 state: PhantomData,
683 table: PhantomData,
684 marker: PhantomData,
685 row: PhantomData,
686 grouped: PhantomData,
687 }
688 }
689
690 /// Combines this query with another using INTERSECT ALL.
691 pub fn intersect_all(
692 self,
693 other: impl IntoSelect<'a, S, M, R>,
694 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
695 SelectBuilder {
696 sql: helpers::intersect_all(self.sql, other.into_select()),
697 schema: PhantomData,
698 state: PhantomData,
699 table: PhantomData,
700 marker: PhantomData,
701 row: PhantomData,
702 grouped: PhantomData,
703 }
704 }
705
706 /// Combines this query with another using EXCEPT.
707 pub fn except(
708 self,
709 other: impl IntoSelect<'a, S, M, R>,
710 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
711 SelectBuilder {
712 sql: helpers::except(self.sql, other.into_select()),
713 schema: PhantomData,
714 state: PhantomData,
715 table: PhantomData,
716 marker: PhantomData,
717 row: PhantomData,
718 grouped: PhantomData,
719 }
720 }
721
722 /// Combines this query with another using EXCEPT ALL.
723 pub fn except_all(
724 self,
725 other: impl IntoSelect<'a, S, M, R>,
726 ) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
727 SelectBuilder {
728 sql: helpers::except_all(self.sql, other.into_select()),
729 schema: PhantomData,
730 state: PhantomData,
731 table: PhantomData,
732 marker: PhantomData,
733 row: PhantomData,
734 grouped: PhantomData,
735 }
736 }
737}
738
739//------------------------------------------------------------------------------
740// Expr impl for subquery usage
741//------------------------------------------------------------------------------
742
743impl<'a, S, State, T, M, R, G> drizzle_core::expr::Expr<'a, SQLiteValue<'a>>
744 for SelectBuilder<'a, S, State, T, M, R, G>
745where
746 State: drizzle_core::ExecutableState,
747 M: drizzle_core::expr::SubqueryType<'a, SQLiteValue<'a>>,
748{
749 type SQLType = <M as drizzle_core::expr::SubqueryType<'a, SQLiteValue<'a>>>::SQLType;
750 type Nullable = drizzle_core::expr::Null;
751 type Aggregate = drizzle_core::expr::Scalar;
752}
753
754//------------------------------------------------------------------------------
755// IntoSelect conversion trait
756//------------------------------------------------------------------------------
757
758/// Conversion trait for types that can become a `SelectBuilder`.
759/// Used by set operations to accept both raw `SelectBuilder` and `DrizzleBuilder`.
760pub trait IntoSelect<'a, S, M, R> {
761 type State: drizzle_core::ExecutableState;
762 type Table;
763 fn into_select(self) -> SelectBuilder<'a, S, Self::State, Self::Table, M, R>;
764}
765
766impl<'a, S, State: drizzle_core::ExecutableState, T, M, R, G> IntoSelect<'a, S, M, R>
767 for SelectBuilder<'a, S, State, T, M, R, G>
768{
769 type State = State;
770 type Table = T;
771 fn into_select(self) -> SelectBuilder<'a, S, State, T, M, R> {
772 SelectBuilder {
773 sql: self.sql,
774 schema: PhantomData,
775 state: PhantomData,
776 table: PhantomData,
777 marker: PhantomData,
778 row: PhantomData,
779 grouped: PhantomData,
780 }
781 }
782}