Skip to main content

drizzle_mysql/builder/
select.rs

1use crate::{common::MySQLSchemaType, helpers, values::MySQLValue};
2use drizzle_core::{SQL, SQLTable, ToSQL, Token};
3
4use super::ExecutableState;
5
6pub use drizzle_core::builder::{
7    SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet, SelectOffsetSet,
8    SelectOrderSet, SelectSetOpSet, SelectWhereSet,
9};
10
11/// Builder state after `having`. A SELECT takes at most one HAVING.
12#[derive(Debug, Clone, Copy, Default)]
13pub struct SelectHavingSet;
14
15impl ExecutableState for SelectHavingSet {
16    type Rows<R: drizzle_core::row::RowCount> = drizzle_core::row::ZeroOrMore;
17}
18
19/// Builder state after an index hint on the FROM table.
20///
21/// The FROM table takes one hint; `Kind` records which one.
22#[doc(hidden)]
23#[derive(Debug, Clone, Copy, Default)]
24pub struct SelectIndexHintSet<Kind>(core::marker::PhantomData<Kind>);
25
26impl<Kind> ExecutableState for SelectIndexHintSet<Kind> {
27    type Rows<R: drizzle_core::row::RowCount> = R;
28}
29
30/// Marker for the MySQL `FOR UPDATE` lock strength.
31#[doc(hidden)]
32#[derive(Debug, Clone, Copy, Default)]
33pub struct ForUpdate;
34
35/// Marker for the MySQL `FOR SHARE` lock strength.
36#[doc(hidden)]
37#[derive(Debug, Clone, Copy, Default)]
38pub struct ForShare;
39
40/// Marker for a locking read without a wait modifier.
41#[doc(hidden)]
42#[derive(Debug, Clone, Copy, Default)]
43pub struct Wait;
44
45/// Marker for a locking read with `NOWAIT`.
46#[doc(hidden)]
47#[derive(Debug, Clone, Copy, Default)]
48pub struct NoWait;
49
50/// Marker for a locking read with `SKIP LOCKED`.
51#[doc(hidden)]
52#[derive(Debug, Clone, Copy, Default)]
53pub struct SkipLocked;
54
55/// Builder state after `for_update` or `for_share`.
56///
57/// `Strength` is [`ForUpdate`] or [`ForShare`]; `Modifier` is [`Wait`],
58/// [`NoWait`] or [`SkipLocked`]. Only `nowait` or `skip_locked` can follow,
59/// once.
60#[derive(Debug, Clone, Copy, Default)]
61pub struct SelectForSet<Strength, Modifier = Wait>(core::marker::PhantomData<(Strength, Modifier)>);
62
63impl<Strength, Modifier> ExecutableState for SelectForSet<Strength, Modifier> {
64    type Rows<R: drizzle_core::row::RowCount> = drizzle_core::row::ZeroOrMore;
65}
66
67/// Clause gate for SELECT methods whose names collide with INSERT/UPDATE/DELETE
68/// builder methods on the shared `QueryBuilder` type.
69///
70/// Coherence can only rule out overlapping inherent impls through a trait
71/// local to this crate, so these clauses use this trait instead of
72/// [`drizzle_core::ClauseAllowed`].
73#[doc(hidden)]
74#[diagnostic::on_unimplemented(
75    message = "builder state `{Self}` does not allow `{C}`",
76    label = "not available at this point of the query",
77    note = "SELECT clauses go in order: FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET",
78    note = "only a SELECT can be a set operand, a subquery, a derived table, or an INSERT source"
79)]
80pub trait SelectClause<C> {}
81
82/// Clause marker for `OFFSET`, which is rendered with a `LIMIT` when none was
83/// given.
84#[doc(hidden)]
85#[derive(Debug, Clone, Copy, Default)]
86pub struct MySqlOffset;
87
88// The shared SELECT states cover JOIN, GROUP BY, HAVING and the
89// CTE/locking-read gate; MySQL adds its own states to those and keeps
90// its own lists for clauses whose order rules differ.
91impl SelectClause<drizzle_core::clause::Where> for SelectFromSet {}
92impl<Kind> SelectClause<drizzle_core::clause::Where> for SelectIndexHintSet<Kind> {}
93impl SelectClause<drizzle_core::clause::Where> for SelectJoinSet {}
94
95impl SelectClause<drizzle_core::clause::OrderBy> for SelectFromSet {}
96impl<Kind> SelectClause<drizzle_core::clause::OrderBy> for SelectIndexHintSet<Kind> {}
97impl SelectClause<drizzle_core::clause::OrderBy> for SelectJoinSet {}
98impl SelectClause<drizzle_core::clause::OrderBy> for SelectWhereSet {}
99impl SelectClause<drizzle_core::clause::OrderBy> for SelectGroupSet {}
100impl SelectClause<drizzle_core::clause::OrderBy> for SelectHavingSet {}
101
102impl SelectClause<drizzle_core::clause::Limit> for SelectFromSet {}
103impl<Kind> SelectClause<drizzle_core::clause::Limit> for SelectIndexHintSet<Kind> {}
104impl SelectClause<drizzle_core::clause::Limit> for SelectJoinSet {}
105impl SelectClause<drizzle_core::clause::Limit> for SelectWhereSet {}
106impl SelectClause<drizzle_core::clause::Limit> for SelectGroupSet {}
107impl SelectClause<drizzle_core::clause::Limit> for SelectHavingSet {}
108impl SelectClause<drizzle_core::clause::Limit> for SelectOrderSet {}
109impl SelectClause<drizzle_core::clause::Limit> for SelectSetOpSet {}
110
111impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectFromSet {}
112impl<Kind> drizzle_core::ClauseAllowed<MySqlOffset> for SelectIndexHintSet<Kind> {}
113impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectJoinSet {}
114impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectWhereSet {}
115impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectGroupSet {}
116impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectHavingSet {}
117impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectOrderSet {}
118impl drizzle_core::ClauseAllowed<MySqlOffset> for SelectSetOpSet {}
119
120impl<Kind> drizzle_core::ClauseAllowed<drizzle_core::clause::Compound>
121    for SelectIndexHintSet<Kind>
122{
123}
124impl drizzle_core::ClauseAllowed<drizzle_core::clause::Compound> for SelectHavingSet {}
125
126impl<Kind> drizzle_core::ClauseAllowed<drizzle_core::clause::Source> for SelectIndexHintSet<Kind> {}
127impl drizzle_core::ClauseAllowed<drizzle_core::clause::Source> for SelectHavingSet {}
128impl<Strength, Modifier> drizzle_core::ClauseAllowed<drizzle_core::clause::Source>
129    for SelectForSet<Strength, Modifier>
130{
131}
132
133impl<Kind> drizzle_core::ClauseAllowed<drizzle_core::clause::Join> for SelectIndexHintSet<Kind> {}
134
135impl<Kind> drizzle_core::ClauseAllowed<drizzle_core::clause::GroupBy> for SelectIndexHintSet<Kind> {}
136
137impl<Kind> drizzle_core::ClauseAllowed<drizzle_core::clause::Simple> for SelectIndexHintSet<Kind> {}
138impl drizzle_core::ClauseAllowed<drizzle_core::clause::Simple> for SelectHavingSet {}
139
140/// A `SELECT` query being built for `MySQL`.
141///
142/// This is [`QueryBuilder`](super::QueryBuilder) in one of the `Select*`
143/// states. Start it with [`QueryBuilder::select`](super::QueryBuilder::select),
144/// then call [`from`](Self::from).
145///
146/// # Clause order
147///
148/// Clauses must be added in SQL order. Each method is only available in the
149/// states listed here:
150///
151/// | After | You can call |
152/// |---|---|
153/// | `select` | `from` |
154/// | `from` | an index hint, joins, `where`, `group_by`, `order_by`, `limit`, `offset` |
155/// | an index hint | joins, `where`, `group_by`, `order_by`, `limit`, `offset` |
156/// | a join | more joins, `where`, `group_by`, `order_by`, `limit`, `offset` |
157/// | `where` | `group_by`, `order_by`, `limit`, `offset` |
158/// | `group_by` | `having`, `order_by`, `limit`, `offset` |
159/// | `having` | `order_by`, `limit`, `offset` |
160/// | `order_by` | `limit`, `offset` |
161/// | `limit` | `offset` |
162/// | a set operation | more set operations, `order_by`, `limit`, `offset` |
163/// | `for_update` / `for_share` | `nowait` or `skip_locked` |
164///
165/// Every state from `from` on (except after a locking clause) also accepts
166/// set operations (`union`, `intersect`, `except` and their `_all` forms).
167/// Every state from `from` on, except a compound query, accepts a locking
168/// clause (`for_update`, `for_share`) and [`into_cte`](Self::into_cte).
169/// A finished query can be named as a derived table with
170/// [`alias`](Self::alias) or compiled with [`prepare`](Self::prepare); one
171/// without a locking clause can also be a subquery or an `INSERT` source.
172///
173/// # Compile-time checks
174///
175/// Besides clause order, the builder rejects: a second index hint on the
176/// FROM table, a second HAVING, a set operation after a locking clause, a
177/// locking clause on a compound query, and a WHERE, HAVING or JOIN
178/// condition that is not boolean. Column references and grouping are
179/// checked by [`prepare`](Self::prepare) and when the query runs.
180///
181/// # Examples
182///
183/// ```rust
184/// # let _ = r####"
185/// # use drizzle::core::expr::{alias, count, eq, gt};
186/// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
187/// # #[MySQLTable(NAME = "users")]
188/// # struct Users {
189/// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
190/// #     #[column(VARCHAR(255))] name: String,
191/// #     #[column(DEFAULT = true)] active: bool,
192/// # }
193/// # #[MySQLTable(NAME = "posts")]
194/// # struct Posts {
195/// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
196/// #     #[column(REFERENCES = Users::id)] user_id: u64,
197/// #     title: String,
198/// # }
199/// # #[MySQLIndex] struct UsersNameIdx(Users::name);
200/// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
201/// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
202/// # let builder = QueryBuilder::new::<Schema>();
203/// # let Schema { users, posts } = Schema::new();
204/// let query = builder
205///     .select((users.id, count(posts.id)))
206///     .from(users)
207///     .inner_join((posts, eq(posts.user_id, users.id)))
208///     .r#where(eq(users.active, true))
209///     .group_by(users.id)
210///     .having(gt(count(posts.id), 0))
211///     .order_by(desc(users.id))
212///     .limit(10)
213///     .offset(20);
214/// // SELECT `users`.`id`, COUNT(`posts`.`id`) FROM `users`
215/// //   INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id`
216/// //   WHERE `users`.`active` = ? GROUP BY `users`.`id`
217/// //   HAVING COUNT(`posts`.`id`)> ? ORDER BY `users`.`id` DESC LIMIT ? OFFSET ?
218/// # "####;
219/// ```
220pub type SelectBuilder<'a, Schema, State, Table = (), Marker = (), Row = (), Grouped = ()> =
221    super::QueryBuilder<'a, Schema, State, Table, Marker, Row, Grouped>;
222
223mod private {
224    use super::{
225        SelectForSet, SelectFromSet, SelectGroupSet, SelectHavingSet, SelectIndexHintSet,
226        SelectJoinSet, SelectLimitSet, SelectOffsetSet, SelectOrderSet, SelectSetOpSet,
227        SelectWhereSet,
228    };
229
230    pub trait SealedSelect {}
231
232    pub trait Prepare {}
233
234    impl Prepare for SelectFromSet {}
235    impl<Kind> Prepare for SelectIndexHintSet<Kind> {}
236    impl Prepare for SelectJoinSet {}
237    impl Prepare for SelectWhereSet {}
238    impl Prepare for SelectGroupSet {}
239    impl Prepare for SelectHavingSet {}
240    impl Prepare for SelectOrderSet {}
241    impl Prepare for SelectLimitSet {}
242    impl Prepare for SelectOffsetSet {}
243    impl Prepare for SelectSetOpSet {}
244    impl<Strength, Modifier> Prepare for SelectForSet<Strength, Modifier> {}
245}
246
247impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
248where
249    State: private::Prepare,
250{
251    /// Renders this query as a [`PreparedStatement`](drizzle_core::prepared::PreparedStatement).
252    ///
253    /// `MySQL` binds parameters by position, so named placeholders are put in
254    /// the order they appear in the SQL (a name used twice is bound twice).
255    /// Bind values by name later with `bind`.
256    ///
257    /// # Compile-time checks
258    ///
259    /// Only compiles when every column reference is in scope (its table is
260    /// in FROM or a JOIN) and every selected column is grouped or inside an
261    /// aggregate.
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// # let _ = r####"
267    /// # use drizzle::core::expr::{alias, count, eq, gt};
268    /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
269    /// # #[MySQLTable(NAME = "users")]
270    /// # struct Users {
271    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
272    /// #     #[column(VARCHAR(255))] name: String,
273    /// #     #[column(DEFAULT = true)] active: bool,
274    /// # }
275    /// # #[MySQLTable(NAME = "posts")]
276    /// # struct Posts {
277    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
278    /// #     #[column(REFERENCES = Users::id)] user_id: u64,
279    /// #     title: String,
280    /// # }
281    /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
282    /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
283    /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
284    /// # let builder = QueryBuilder::new::<Schema>();
285    /// # let Schema { users, posts } = Schema::new();
286    /// let query = builder
287    ///     .select(users.id)
288    ///     .from(users)
289    ///     .r#where(eq(users.name, Placeholder::named("name")));
290    /// let prepared = query.prepare();
291    /// assert_eq!(prepared.sql(), "SELECT `users`.`id` FROM `users` WHERE `users`.`name` = ?");
292    /// # "####;
293    /// ```
294    #[must_use]
295    pub fn prepare<ScopeProof, AggProof>(
296        &self,
297    ) -> drizzle_core::prepared::PreparedStatement<'a, MySQLValue<'a>>
298    where
299        M: drizzle_core::row::MarkerScopeValidFor<ScopeProof>
300            + drizzle_core::row::MarkerAggValidFor<G, AggProof>,
301    {
302        self.prepared_statement()
303    }
304}
305
306impl<'a, S, M> SelectBuilder<'a, S, SelectInitial, (), M> {
307    /// Sets the FROM source: a table, a CTE, or a derived table made with
308    /// [`alias`](Self::alias).
309    ///
310    /// The result row type is inferred from the selected columns and this
311    /// source. With `select(())`, the row is the table's generated select
312    /// model.
313    #[allow(clippy::type_complexity)]
314    pub fn from<T>(
315        self,
316        table: T,
317    ) -> SelectBuilder<
318        'a,
319        S,
320        SelectFromSet,
321        T,
322        drizzle_core::FromMarker<M, T>,
323        <M as drizzle_core::ResolveRow<T>>::Row,
324    >
325    where
326        T: ToSQL<'a, MySQLValue<'a>> + drizzle_core::ScopeEntry,
327        M: drizzle_core::ResolveRow<T>,
328    {
329        SelectBuilder::from_sql(self.sql.append(helpers::from(table)))
330    }
331}
332
333impl<'a, S, T, M, R, G> SelectBuilder<'a, S, SelectFromSet, T, M, R, G>
334where
335    T: crate::traits::MySQLTable<'a>,
336{
337    /// Adds `USE INDEX (..)` to the FROM table.
338    ///
339    /// Pass one `#[MySQLIndex]` index of this table, or a tuple of up to
340    /// eight. An index of another table does not compile. Only one hint
341    /// (`use_index`, `force_index` or `ignore_index`) can be added, right
342    /// after `from`. To hint a joined table, use [`MySQLIndexHintExt`](crate::helpers::MySQLIndexHintExt).
343    ///
344    /// # Examples
345    ///
346    /// ```rust
347    /// # let _ = r####"
348    /// # use drizzle::core::expr::{alias, count, eq, gt};
349    /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
350    /// # #[MySQLTable(NAME = "users")]
351    /// # struct Users {
352    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
353    /// #     #[column(VARCHAR(255))] name: String,
354    /// #     #[column(DEFAULT = true)] active: bool,
355    /// # }
356    /// # #[MySQLTable(NAME = "posts")]
357    /// # struct Posts {
358    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
359    /// #     #[column(REFERENCES = Users::id)] user_id: u64,
360    /// #     title: String,
361    /// # }
362    /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
363    /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
364    /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
365    /// # let builder = QueryBuilder::new::<Schema>();
366    /// # let Schema { users, posts } = Schema::new();
367    /// let query = builder
368    ///     .select(users.id)
369    ///     .from(users)
370    ///     .use_index(UsersNameIdx::new());
371    /// assert_eq!(
372    ///     query.to_sql().sql(),
373    ///     "SELECT `users`.`id` FROM `users` USE INDEX (`users_name_idx`)"
374    /// );
375    /// # "####;
376    /// ```
377    #[must_use]
378    pub fn use_index<Indexes>(
379        self,
380        indexes: Indexes,
381    ) -> SelectBuilder<'a, S, SelectIndexHintSet<helpers::UseIndex>, T, M, R, G>
382    where
383        Indexes: helpers::IndexHintList<'a, T>,
384    {
385        SelectBuilder::from_sql(self.sql.append(
386            helpers::index_hint::<T, Indexes, helpers::UseIndex>(&indexes),
387        ))
388    }
389
390    /// Adds `FORCE INDEX (..)` to the FROM table, which tells `MySQL` to use
391    /// a table scan only if none of these indexes can be used. See
392    /// [`use_index`](Self::use_index).
393    #[must_use]
394    pub fn force_index<Indexes>(
395        self,
396        indexes: Indexes,
397    ) -> SelectBuilder<'a, S, SelectIndexHintSet<helpers::ForceIndex>, T, M, R, G>
398    where
399        Indexes: helpers::IndexHintList<'a, T>,
400    {
401        SelectBuilder::from_sql(self.sql.append(helpers::index_hint::<
402            T,
403            Indexes,
404            helpers::ForceIndex,
405        >(&indexes)))
406    }
407
408    /// Adds `IGNORE INDEX (..)` to the FROM table. See
409    /// [`use_index`](Self::use_index).
410    #[must_use]
411    pub fn ignore_index<Indexes>(
412        self,
413        indexes: Indexes,
414    ) -> SelectBuilder<'a, S, SelectIndexHintSet<helpers::IgnoreIndex>, T, M, R, G>
415    where
416        Indexes: helpers::IndexHintList<'a, T>,
417    {
418        SelectBuilder::from_sql(self.sql.append(helpers::index_hint::<
419            T,
420            Indexes,
421            helpers::IgnoreIndex,
422        >(&indexes)))
423    }
424}
425
426macro_rules! join_on_method {
427    ($name:ident, $join:expr, $kind:ident) => {
428        #[doc = concat!("Adds a join with `", stringify!($name), "`.")]
429        ///
430        /// Pass `(source, condition)` with a boolean condition, or a bare table
431        /// to join on its foreign key to the previous table. The source can be
432        /// a table, a derived table (see [`alias`](Self::alias)), or a table
433        /// with an index hint (see [`MySQLIndexHintExt`](crate::helpers::MySQLIndexHintExt)).
434        /// After a LEFT or RIGHT join, selected columns of the side that may
435        /// be missing decode as `Option`; with `select(())`, that side's
436        /// whole model is an `Option` in the row.
437        ///
438        /// # Examples
439        ///
440        /// ```rust
441        /// # let _ = r####"
442        /// # use drizzle::core::expr::{alias, count, eq, gt};
443        /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
444        /// # #[MySQLTable(NAME = "users")]
445        /// # struct Users {
446        /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
447        /// #     #[column(VARCHAR(255))] name: String,
448        /// #     #[column(DEFAULT = true)] active: bool,
449        /// # }
450        /// # #[MySQLTable(NAME = "posts")]
451        /// # struct Posts {
452        /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
453        /// #     #[column(REFERENCES = Users::id)] user_id: u64,
454        /// #     title: String,
455        /// # }
456        /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
457        /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
458        /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
459        /// # let builder = QueryBuilder::new::<Schema>();
460        /// # let Schema { users, posts } = Schema::new();
461        /// let query = builder
462        ///     .select((users.name, posts.title))
463        ///     .from(users)
464        ///     .left_join((posts, eq(posts.user_id, users.id)));
465        /// assert_eq!(
466        ///     query.to_sql().sql(),
467        ///     "SELECT `users`.`name`, `posts`.`title` FROM `users` LEFT JOIN `posts` ON `posts`.`user_id` = `users`.`id`"
468        /// );
469        /// # "####;
470        /// ```
471        #[allow(clippy::type_complexity)]
472        pub fn $name<J: helpers::JoinArg<'a, T, Via>, Via>(
473            self,
474            arg: J,
475        ) -> SelectBuilder<
476            'a,
477            S,
478            SelectJoinSet,
479            J::JoinedTable,
480            <M as drizzle_core::JoinStep<R, J::JoinedTable, drizzle_core::$kind, J::OnSources>>::Marker,
481            <M as drizzle_core::JoinStep<R, J::JoinedTable, drizzle_core::$kind, J::OnSources>>::Row,
482            G,
483        >
484        where
485            M: drizzle_core::JoinStep<R, J::JoinedTable, drizzle_core::$kind, J::OnSources>,
486        {
487            SelectBuilder::from_sql(self.sql.append(arg.into_join_sql($join)))
488        }
489    };
490}
491
492impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
493where
494    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Join>,
495{
496    join_on_method!(join, drizzle_core::Join::new(), InnerJoin);
497    join_on_method!(inner_join, drizzle_core::Join::new().inner(), InnerJoin);
498    join_on_method!(left_join, drizzle_core::Join::new().left(), LeftJoin);
499    join_on_method!(
500        left_outer_join,
501        drizzle_core::Join::new().left().outer(),
502        LeftJoin
503    );
504    join_on_method!(right_join, drizzle_core::Join::new().right(), RightJoin);
505    join_on_method!(
506        right_outer_join,
507        drizzle_core::Join::new().right().outer(),
508        RightJoin
509    );
510
511    /// Adds a `CROSS JOIN`, which pairs every row with every row of `arg`.
512    ///
513    /// A bare source renders `CROSS JOIN`. For backwards compatibility,
514    /// `(source, condition)` renders the equivalent `INNER JOIN ... ON ...`.
515    #[allow(clippy::type_complexity)]
516    pub fn cross_join<Arg: helpers::CrossJoinArg<'a, T>>(
517        self,
518        arg: Arg,
519    ) -> SelectBuilder<
520        'a,
521        S,
522        SelectJoinSet,
523        Arg::JoinedTable,
524        <M as drizzle_core::JoinStep<
525            R,
526            Arg::JoinedTable,
527            drizzle_core::InnerJoin,
528            Arg::OnSources,
529        >>::Marker,
530        <M as drizzle_core::JoinStep<
531            R,
532            Arg::JoinedTable,
533            drizzle_core::InnerJoin,
534            Arg::OnSources,
535        >>::Row,
536        G,
537    >
538    where
539        M: drizzle_core::JoinStep<R, Arg::JoinedTable, drizzle_core::InnerJoin, Arg::OnSources>,
540    {
541        SelectBuilder::from_sql(self.sql.append(arg.into_cross_join_sql()))
542    }
543
544    /// Adds `INNER JOIN LATERAL (subquery) AS name ON condition`.
545    ///
546    /// A lateral subquery may reference columns of the tables before it.
547    /// Requires `MySQL` 8.0.14 or later.
548    #[allow(clippy::type_complexity)]
549    pub fn inner_join_lateral<Arg>(
550        self,
551        arg: Arg,
552    ) -> SelectBuilder<
553        'a,
554        S,
555        SelectJoinSet,
556        Arg::JoinedTable,
557        <M as drizzle_core::JoinStep<
558            R,
559            Arg::JoinedTable,
560            drizzle_core::Lateral<drizzle_core::InnerJoin>,
561            Arg::OnSources,
562        >>::Marker,
563        <M as drizzle_core::JoinStep<
564            R,
565            Arg::JoinedTable,
566            drizzle_core::Lateral<drizzle_core::InnerJoin>,
567            Arg::OnSources,
568        >>::Row,
569        G,
570    >
571    where
572        Arg: drizzle_core::LateralArg<'a, MySQLValue<'a>>,
573        M: drizzle_core::JoinStep<
574                R,
575                Arg::JoinedTable,
576                drizzle_core::Lateral<drizzle_core::InnerJoin>,
577                Arg::OnSources,
578            >,
579    {
580        SelectBuilder::from_sql(
581            self.sql
582                .append(arg.into_lateral_sql(drizzle_core::Join::new().inner())),
583        )
584    }
585
586    /// Adds `LEFT JOIN LATERAL (subquery) AS name ON condition`.
587    ///
588    /// Like [`inner_join_lateral`](Self::inner_join_lateral), but keeps rows
589    /// with no match. The selection must allow the lateral columns to be
590    /// missing.
591    #[allow(clippy::type_complexity)]
592    pub fn left_join_lateral<Arg, SelectionProof>(
593        self,
594        arg: Arg,
595    ) -> SelectBuilder<
596        'a,
597        S,
598        SelectJoinSet,
599        Arg::JoinedTable,
600        <M as drizzle_core::JoinStep<
601            R,
602            Arg::JoinedTable,
603            drizzle_core::Lateral<drizzle_core::LeftJoin>,
604            Arg::OnSources,
605        >>::Marker,
606        <M as drizzle_core::JoinStep<
607            R,
608            Arg::JoinedTable,
609            drizzle_core::Lateral<drizzle_core::LeftJoin>,
610            Arg::OnSources,
611        >>::Row,
612        G,
613    >
614    where
615        Arg: drizzle_core::LateralArg<'a, MySQLValue<'a>>,
616        M: drizzle_core::JoinStep<
617                R,
618                Arg::JoinedTable,
619                drizzle_core::Lateral<drizzle_core::LeftJoin>,
620                Arg::OnSources,
621            > + drizzle_core::LeftLateralSelection<SelectionProof>,
622    {
623        SelectBuilder::from_sql(
624            self.sql
625                .append(arg.into_lateral_sql(drizzle_core::Join::new().left())),
626        )
627    }
628
629    /// Adds `CROSS JOIN LATERAL (subquery) AS name`, with no ON condition.
630    #[allow(clippy::type_complexity)]
631    pub fn cross_join_lateral<Source>(
632        self,
633        source: Source,
634    ) -> SelectBuilder<
635        'a,
636        S,
637        SelectJoinSet,
638        Source::JoinedTable,
639        <M as drizzle_core::JoinStep<
640            R,
641            Source::JoinedTable,
642            drizzle_core::Lateral<drizzle_core::InnerJoin>,
643        >>::Marker,
644        <M as drizzle_core::JoinStep<
645            R,
646            Source::JoinedTable,
647            drizzle_core::Lateral<drizzle_core::InnerJoin>,
648        >>::Row,
649        G,
650    >
651    where
652        Source: drizzle_core::LateralSource<'a, MySQLValue<'a>>,
653        M: drizzle_core::JoinStep<
654                R,
655                Source::JoinedTable,
656                drizzle_core::Lateral<drizzle_core::InnerJoin>,
657            >,
658    {
659        SelectBuilder::from_sql(self.sql.append(source.into_cross_lateral_sql()))
660    }
661}
662
663impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
664where
665    State: SelectClause<drizzle_core::clause::Where>,
666{
667    /// Adds a WHERE clause. The condition must be a boolean expression.
668    #[allow(clippy::type_complexity)]
669    pub fn r#where<E>(
670        self,
671        condition: E,
672    ) -> SelectBuilder<
673        'a,
674        S,
675        SelectWhereSet,
676        T,
677        <M as drizzle_core::HasScope>::With<E::Sources>,
678        R,
679        G,
680    >
681    where
682        M: drizzle_core::HasScope,
683        E: drizzle_core::expr::Expr<'a, MySQLValue<'a>>,
684        E::SQLType: drizzle_core::types::BooleanLike,
685    {
686        SelectBuilder::from_sql(self.sql.append(helpers::r#where(condition)))
687    }
688}
689
690impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
691where
692    State: drizzle_core::ClauseAllowed<drizzle_core::clause::GroupBy>,
693{
694    /// Adds a GROUP BY clause. Pass one expression or a tuple.
695    ///
696    /// Every selected column that is not inside an aggregate must appear in
697    /// the GROUP BY list, or belong to a table grouped by its single-column
698    /// primary key. This is checked by `prepare` and when the query runs.
699    #[allow(clippy::type_complexity)]
700    pub fn group_by<Gr>(
701        self,
702        columns: Gr,
703    ) -> SelectBuilder<
704        'a,
705        S,
706        SelectGroupSet,
707        T,
708        <M as drizzle_core::HasScope>::With<Gr::Sources>,
709        R,
710        Gr::Columns,
711    >
712    where
713        M: drizzle_core::HasScope,
714        Gr: drizzle_core::IntoGroupBy<'a, MySQLValue<'a>>,
715    {
716        SelectBuilder::from_sql(self.sql.append(helpers::group_by_expr(columns)))
717    }
718}
719
720impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
721where
722    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Having>,
723{
724    /// Adds a HAVING clause, which filters groups.
725    ///
726    /// Only available after `group_by`, and only once. The condition must
727    /// be a boolean expression and may use aggregates.
728    #[allow(clippy::type_complexity)]
729    pub fn having<E>(
730        self,
731        condition: E,
732    ) -> SelectBuilder<
733        'a,
734        S,
735        SelectHavingSet,
736        T,
737        <M as drizzle_core::HasScope>::With<E::Sources>,
738        R,
739        G,
740    >
741    where
742        M: drizzle_core::HasScope,
743        E: drizzle_core::expr::Expr<'a, MySQLValue<'a>>,
744        E::SQLType: drizzle_core::types::BooleanLike,
745    {
746        SelectBuilder::from_sql(self.sql.append(helpers::having(condition)))
747    }
748}
749
750impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
751where
752    State: SelectClause<drizzle_core::clause::OrderBy>,
753{
754    /// Adds an ORDER BY clause. Pass one ordering term or a tuple; wrap a
755    /// column in [`asc`](crate::helpers::asc) or
756    /// [`desc`](crate::helpers::desc) to set the direction.
757    #[allow(clippy::type_complexity)]
758    pub fn order_by<O>(
759        self,
760        order: O,
761    ) -> SelectBuilder<
762        'a,
763        S,
764        SelectOrderSet,
765        T,
766        <M as drizzle_core::HasScope>::With<O::Sources>,
767        R,
768        G,
769    >
770    where
771        M: drizzle_core::HasScope,
772        O: ToSQL<'a, MySQLValue<'a>> + drizzle_core::expr::ExprSources,
773    {
774        SelectBuilder::from_sql(self.sql.append(helpers::order_by(order)))
775    }
776}
777
778impl<'a, S, T, M, R, G> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
779    /// Sorts a compound (set operation) result.
780    ///
781    /// Pass a selected column (rendered without its table name), or
782    /// [`output_alias`](crate::helpers::output_alias) for a named output,
783    /// optionally wrapped in [`asc`](crate::helpers::asc) or
784    /// [`desc`](crate::helpers::desc). A column the first query does not
785    /// select does not compile.
786    ///
787    /// # Examples
788    ///
789    /// ```rust
790    /// # let _ = r####"
791    /// # use drizzle::core::expr::{alias, count, eq, gt};
792    /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
793    /// # #[MySQLTable(NAME = "users")]
794    /// # struct Users {
795    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
796    /// #     #[column(VARCHAR(255))] name: String,
797    /// #     #[column(DEFAULT = true)] active: bool,
798    /// # }
799    /// # #[MySQLTable(NAME = "posts")]
800    /// # struct Posts {
801    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
802    /// #     #[column(REFERENCES = Users::id)] user_id: u64,
803    /// #     title: String,
804    /// # }
805    /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
806    /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
807    /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
808    /// # let builder = QueryBuilder::new::<Schema>();
809    /// # let Schema { users, posts } = Schema::new();
810    /// let query = builder
811    ///     .select(alias(users.name, "label"))
812    ///     .from(users)
813    ///     .union(builder.select(alias(posts.title, "label")).from(posts))
814    ///     .order_by(desc(output_alias("label")));
815    /// assert_eq!(
816    ///     query.to_sql().sql(),
817    ///     "(SELECT `users`.`name` AS `label` FROM `users`) UNION (SELECT `posts`.`title` AS `label` FROM `posts`) ORDER BY `label` DESC"
818    /// );
819    /// # "####;
820    /// ```
821    pub fn order_by<O, Proof>(self, order: O) -> SelectBuilder<'a, S, SelectOrderSet, T, M, R, G>
822    where
823        O: helpers::SetOrderBy<'a, M, T, Proof>,
824    {
825        let order = helpers::SetOrderBy::into_set_order_sql(order);
826        SelectBuilder::from_sql(
827            self.sql
828                .append(SQL::from_iter([Token::ORDER, Token::BY]).append(order)),
829        )
830    }
831}
832
833impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
834where
835    State: SelectClause<drizzle_core::clause::Limit>,
836{
837    /// Adds a LIMIT clause.
838    ///
839    /// Pass a non-negative integer or an integer placeholder. Both are sent
840    /// as bound parameters.
841    ///
842    /// # Panics
843    ///
844    /// Panics when a numeric argument is negative or does not fit in
845    /// `usize`.
846    #[track_caller]
847    pub fn limit<P>(self, limit: P) -> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G>
848    where
849        P: drizzle_core::PaginationArg<'a, MySQLValue<'a>>,
850    {
851        SelectBuilder::from_sql(self.sql.append(helpers::limit(limit)))
852    }
853}
854
855impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
856where
857    State: drizzle_core::ClauseAllowed<MySqlOffset>,
858{
859    /// Skips the first `offset` rows without limiting the row count.
860    ///
861    /// `MySQL` has no bare `OFFSET`, so this renders
862    /// `LIMIT 9223372036854775807 OFFSET ?` (see
863    /// [`drizzle_core::helpers::MYSQL_UNBOUNDED_LIMIT`]).
864    ///
865    /// # Examples
866    ///
867    /// ```rust
868    /// # let _ = r####"
869    /// # use drizzle::core::expr::{alias, count, eq, gt};
870    /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
871    /// # #[MySQLTable(NAME = "users")]
872    /// # struct Users {
873    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
874    /// #     #[column(VARCHAR(255))] name: String,
875    /// #     #[column(DEFAULT = true)] active: bool,
876    /// # }
877    /// # #[MySQLTable(NAME = "posts")]
878    /// # struct Posts {
879    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
880    /// #     #[column(REFERENCES = Users::id)] user_id: u64,
881    /// #     title: String,
882    /// # }
883    /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
884    /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
885    /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
886    /// # let builder = QueryBuilder::new::<Schema>();
887    /// # let Schema { users, posts } = Schema::new();
888    /// let query = builder.select(users.id).from(users).offset(5);
889    /// assert_eq!(
890    ///     query.to_sql().sql(),
891    ///     "SELECT `users`.`id` FROM `users` LIMIT 9223372036854775807 OFFSET ?"
892    /// );
893    /// # "####;
894    /// ```
895    ///
896    /// # Panics
897    ///
898    /// Panics when a numeric argument is negative or does not fit in
899    /// `usize`.
900    #[track_caller]
901    pub fn offset<P>(self, offset: P) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G>
902    where
903        P: drizzle_core::PaginationArg<'a, MySQLValue<'a>>,
904    {
905        SelectBuilder::from_sql(self.sql.append(helpers::standalone_offset(offset)))
906    }
907}
908
909impl<'a, S, T, M, R, G> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G> {
910    /// Adds an OFFSET clause after LIMIT.
911    ///
912    /// # Panics
913    ///
914    /// Panics when a numeric argument is negative or does not fit in
915    /// `usize`.
916    #[track_caller]
917    pub fn offset<P>(self, offset: P) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G>
918    where
919        P: drizzle_core::PaginationArg<'a, MySQLValue<'a>>,
920    {
921        SelectBuilder::from_sql(self.sql.append(drizzle_core::helpers::offset(offset)))
922    }
923}
924
925impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
926where
927    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Simple>,
928{
929    /// Adds `FOR UPDATE`, which locks the selected rows until the
930    /// transaction ends.
931    ///
932    /// Must be the last clause; only [`nowait`](Self::nowait) or
933    /// [`skip_locked`](Self::skip_locked) can follow. Not available on a
934    /// compound query, and the result cannot be a set operand, a subquery or
935    /// an `INSERT` source.
936    ///
937    /// # Examples
938    ///
939    /// ```rust
940    /// # let _ = r####"
941    /// # use drizzle::core::expr::{alias, count, eq, gt};
942    /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
943    /// # #[MySQLTable(NAME = "users")]
944    /// # struct Users {
945    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
946    /// #     #[column(VARCHAR(255))] name: String,
947    /// #     #[column(DEFAULT = true)] active: bool,
948    /// # }
949    /// # #[MySQLTable(NAME = "posts")]
950    /// # struct Posts {
951    /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
952    /// #     #[column(REFERENCES = Users::id)] user_id: u64,
953    /// #     title: String,
954    /// # }
955    /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
956    /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
957    /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
958    /// # let builder = QueryBuilder::new::<Schema>();
959    /// # let Schema { users, posts } = Schema::new();
960    /// let query = builder
961    ///     .select(users.id)
962    ///     .from(users)
963    ///     .limit(2)
964    ///     .for_update()
965    ///     .skip_locked();
966    /// assert_eq!(
967    ///     query.to_sql().sql(),
968    ///     "SELECT `users`.`id` FROM `users` LIMIT ? FOR UPDATE SKIP LOCKED"
969    /// );
970    /// # "####;
971    /// ```
972    #[must_use]
973    pub fn for_update(self) -> SelectBuilder<'a, S, SelectForSet<ForUpdate>, T, M, R, G> {
974        SelectBuilder::from_sql(
975            self.sql
976                .push(drizzle_core::Token::FOR)
977                .push(drizzle_core::Token::UPDATE),
978        )
979    }
980
981    /// Adds `FOR SHARE`, which takes shared locks on the selected rows: other
982    /// transactions can read them but not change them. See
983    /// [`for_update`](Self::for_update).
984    #[must_use]
985    pub fn for_share(self) -> SelectBuilder<'a, S, SelectForSet<ForShare>, T, M, R, G> {
986        SelectBuilder::from_sql(
987            self.sql
988                .push(drizzle_core::Token::FOR)
989                .push(drizzle_core::Token::SHARE),
990        )
991    }
992}
993
994impl<'a, S, Strength, T, M, R, G> SelectBuilder<'a, S, SelectForSet<Strength, Wait>, T, M, R, G> {
995    /// Adds `NOWAIT`: fail at once instead of waiting when a row is locked
996    /// by another transaction.
997    #[must_use]
998    pub fn nowait(self) -> SelectBuilder<'a, S, SelectForSet<Strength, NoWait>, T, M, R, G> {
999        SelectBuilder::from_sql(self.sql.push(drizzle_core::Token::NOWAIT))
1000    }
1001
1002    /// Adds `SKIP LOCKED`: leave out rows locked by another transaction
1003    /// instead of waiting for them.
1004    #[must_use]
1005    pub fn skip_locked(
1006        self,
1007    ) -> SelectBuilder<'a, S, SelectForSet<Strength, SkipLocked>, T, M, R, G> {
1008        SelectBuilder::from_sql(
1009            self.sql
1010                .push(drizzle_core::Token::SKIP)
1011                .push(drizzle_core::Token::LOCKED),
1012        )
1013    }
1014}
1015
1016impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
1017where
1018    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Simple> + ExecutableState,
1019    T: SQLTable<'a, MySQLSchemaType, MySQLValue<'a>>,
1020{
1021    /// Turns this SELECT into a common table expression named `Tag::NAME`.
1022    ///
1023    /// The result derefs to an aliased copy of the FROM table, so you can
1024    /// select its columns with the usual field access. Pass it to
1025    /// [`QueryBuilder::with`](super::QueryBuilder::with), which has an
1026    /// example. Not available on a compound query.
1027    #[must_use]
1028    pub fn into_cte<Tag: drizzle_core::Tag + 'static>(
1029        self,
1030    ) -> super::CTEView<'a, <T as SQLTable<'a, MySQLSchemaType, MySQLValue<'a>>>::Aliased<Tag>, Self>
1031    {
1032        super::CTEView::new(
1033            <T as SQLTable<'a, MySQLSchemaType, MySQLValue<'a>>>::alias::<Tag>(),
1034            Tag::NAME,
1035            self,
1036        )
1037    }
1038}
1039
1040impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
1041where
1042    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Source>,
1043{
1044    /// Names this query so it can be used as a derived table in `from` or a
1045    /// join.
1046    ///
1047    /// `tag` is a value of a [`Tag`](drizzle_core::Tag) type; its `NAME`
1048    /// becomes the SQL alias. The result exposes the selected columns, so
1049    /// the outer query can reference them with typed accessors.
1050    ///
1051    /// # Panics
1052    ///
1053    /// Panics when the projection contains duplicate output names. Name a
1054    /// computed expression with [`drizzle_core::expr::AliasExt::named`] to
1055    /// make each output unique.
1056    #[must_use]
1057    pub fn alias<Tag, AggProof>(
1058        self,
1059        _tag: Tag,
1060    ) -> drizzle_core::Derived<
1061        'a,
1062        MySQLValue<'a>,
1063        Tag,
1064        <M as drizzle_core::DerivedSelection<'a, MySQLValue<'a>, MySQLSchemaType, T>>::Projection,
1065        Self,
1066    >
1067    where
1068        Tag: drizzle_core::Tag,
1069        M: drizzle_core::DerivedSelection<'a, MySQLValue<'a>, MySQLSchemaType, T>
1070            + drizzle_core::row::MarkerAggValidFor<G, AggProof>,
1071        <M as drizzle_core::DerivedSelection<'a, MySQLValue<'a>, MySQLSchemaType, T>>::Projection:
1072            drizzle_core::DerivedProjection<Tag>,
1073    {
1074        // SAFETY: The executable-state, aggregate, and projection bounds
1075        // above prove that this query matches the derived projection; its
1076        // scope travels in `Self`'s sources and is checked where it is used.
1077        unsafe { drizzle_core::Derived::new_unchecked(self) }
1078    }
1079}
1080
1081macro_rules! set_operation {
1082    ($name:ident, $token:expr, $all:expr) => {
1083        #[doc = concat!("Combines this query with `other` using `", stringify!($name), "`.")]
1084        ///
1085        /// Both queries must select the same row type. Each operand is wrapped
1086        /// in parentheses, so its own ORDER BY and LIMIT stay with it. After a
1087        /// set operation you can chain more set operations, then `order_by`,
1088        /// `limit` and `offset` for the combined result. `INTERSECT` and
1089        /// `EXCEPT` need `MySQL` 8.0.31 or later.
1090        ///
1091        /// # Examples
1092        ///
1093        /// ```rust
1094        /// # let _ = r####"
1095        /// # use drizzle::core::expr::{alias, count, eq, gt};
1096        /// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
1097        /// # #[MySQLTable(NAME = "users")]
1098        /// # struct Users {
1099        /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
1100        /// #     #[column(VARCHAR(255))] name: String,
1101        /// #     #[column(DEFAULT = true)] active: bool,
1102        /// # }
1103        /// # #[MySQLTable(NAME = "posts")]
1104        /// # struct Posts {
1105        /// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
1106        /// #     #[column(REFERENCES = Users::id)] user_id: u64,
1107        /// #     title: String,
1108        /// # }
1109        /// # #[MySQLIndex] struct UsersNameIdx(Users::name);
1110        /// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
1111        /// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
1112        /// # let builder = QueryBuilder::new::<Schema>();
1113        /// # let Schema { users, posts } = Schema::new();
1114        /// let query = builder
1115        ///     .select(users.id)
1116        ///     .from(users)
1117        ///     .union(builder.select(posts.user_id).from(posts));
1118        /// assert_eq!(
1119        ///     query.to_sql().sql(),
1120        ///     "(SELECT `users`.`id` FROM `users`) UNION (SELECT `posts`.`user_id` FROM `posts`)"
1121        /// );
1122        /// # "####;
1123        /// ```
1124        #[allow(clippy::type_complexity)]
1125        pub fn $name<O>(
1126            self,
1127            other: O,
1128        ) -> SelectBuilder<
1129            'a,
1130            S,
1131            SelectSetOpSet,
1132            T,
1133            <M as drizzle_core::SetOperand<O::Marker>>::Combined,
1134            R,
1135            G,
1136        >
1137        where
1138            O: IntoSelectQuery<'a, S, R>,
1139            M: drizzle_core::SetOperand<O::Marker>,
1140        {
1141            SelectBuilder::from_sql(helpers::set_op(
1142                self.sql,
1143                $token,
1144                $all,
1145                other.into_select_query().into_select_sql(),
1146            ))
1147        }
1148    };
1149}
1150
1151impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
1152where
1153    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Compound>,
1154{
1155    set_operation!(union, drizzle_core::Token::UNION, false);
1156    set_operation!(union_all, drizzle_core::Token::UNION, true);
1157    set_operation!(intersect, drizzle_core::Token::INTERSECT, false);
1158    set_operation!(intersect_all, drizzle_core::Token::INTERSECT, true);
1159    set_operation!(except, drizzle_core::Token::EXCEPT, false);
1160    set_operation!(except_all, drizzle_core::Token::EXCEPT, true);
1161}
1162
1163/// A finished SELECT with row type `R`.
1164///
1165/// This trait is sealed so INSERT ... SELECT and set operations cannot accept
1166/// arbitrary SQL or DML builders.
1167#[doc(hidden)]
1168pub trait CompletedSelect<'a, S, R>: private::SealedSelect {
1169    type Marker;
1170    type Grouped;
1171
1172    fn into_select_sql(self) -> drizzle_core::SQL<'a, MySQLValue<'a>>;
1173}
1174
1175/// Converts a finished SELECT, or a driver builder that wraps one, into its
1176/// [`CompletedSelect`].
1177///
1178/// Implementations must unwrap to the sealed [`CompletedSelect`] type; they cannot
1179/// manufacture an arbitrary SQL fragment or row marker.
1180#[diagnostic::on_unimplemented(
1181    message = "this query does not select the columns needed here",
1182    label = "it selects different columns",
1183    note = "a set operand (UNION, INTERSECT, EXCEPT) and an INSERT ... SELECT source select the same number of columns, of matching types, in the same order"
1184)]
1185#[doc(hidden)]
1186pub trait IntoSelectQuery<'a, S, R> {
1187    type Marker;
1188    type Grouped;
1189    type Select: CompletedSelect<'a, S, R, Marker = Self::Marker, Grouped = Self::Grouped>;
1190
1191    fn into_select_query(self) -> Self::Select;
1192}
1193
1194impl<'a, S, State, T, M, R, G> private::SealedSelect for SelectBuilder<'a, S, State, T, M, R, G> where
1195    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Compound>
1196{
1197}
1198
1199impl<'a, S, State, T, M, R, G> CompletedSelect<'a, S, R> for SelectBuilder<'a, S, State, T, M, R, G>
1200where
1201    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Compound>,
1202{
1203    type Marker = M;
1204    type Grouped = G;
1205
1206    fn into_select_sql(self) -> drizzle_core::SQL<'a, MySQLValue<'a>> {
1207        self.sql
1208    }
1209}
1210
1211impl<'a, S, State, T, M, R, G> IntoSelectQuery<'a, S, R> for SelectBuilder<'a, S, State, T, M, R, G>
1212where
1213    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Compound>,
1214{
1215    type Marker = M;
1216    type Grouped = G;
1217    type Select = Self;
1218
1219    fn into_select_query(self) -> Self::Select {
1220        self
1221    }
1222}
1223
1224impl<'a, S, State, T, M, R, G> drizzle_core::expr::Expr<'a, MySQLValue<'a>>
1225    for SelectBuilder<'a, S, State, T, M, R, G>
1226where
1227    State: drizzle_core::ClauseAllowed<drizzle_core::clause::Compound> + ExecutableState,
1228    M: drizzle_core::expr::SubqueryType<'a, MySQLValue<'a>> + drizzle_core::SelectSources,
1229{
1230    type SQLType = <M as drizzle_core::expr::SubqueryType<'a, MySQLValue<'a>>>::SQLType;
1231    type Nullable = drizzle_core::expr::Null;
1232    type Aggregate = drizzle_core::expr::Scalar;
1233}
1234
1235impl<S, State: drizzle_core::ClauseAllowed<drizzle_core::clause::Source>, T, M, R, G>
1236    drizzle_core::expr::SelectQuery for SelectBuilder<'_, S, State, T, M, R, G>
1237{
1238}
1239
1240impl<S, State, T, M, R, G> drizzle_core::expr::ExprSources
1241    for SelectBuilder<'_, S, State, T, M, R, G>
1242where
1243    M: drizzle_core::SelectSources,
1244{
1245    type Sources = M::Sources;
1246}