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}