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}