Skip to main content

drizzle_mysql/
helpers.rs

1//! `MySQL` helpers used with the query builder: ORDER BY terms
2//! ([`asc`], [`desc`], [`output_alias`]) and index hints for joined tables
3//! ([`MySQLIndexHintExt`]).
4//!
5//! Examples use the `drizzle` crate and are not compiled here; see the
6//! [`builder`](crate::builder) module.
7
8#[cfg(not(feature = "std"))]
9use crate::prelude::*;
10use crate::{traits::MySQLTable, values::MySQLValue};
11use drizzle_core::{
12    SQL, SQLChunk, SQLIndex, SQLIndexInfo, SQLTableInfo, ToSQL, Token, helpers, traits::SQLModel,
13};
14
15pub use drizzle_core::Join;
16pub(crate) use helpers::{
17    delete, from, group_by_expr, having, limit, order_by, select, select_distinct, set, update,
18    r#where,
19};
20
21/// A boolean expression accepted as a `JOIN ... ON` condition.
22#[doc(hidden)]
23pub trait JoinCondition<'a>:
24    join_condition_private::Sealed<'a> + ToSQL<'a, MySQLValue<'a>>
25{
26}
27
28mod join_condition_private {
29    pub trait Sealed<'a> {}
30
31    impl<'a, T> Sealed<'a> for T
32    where
33        T: drizzle_core::expr::Expr<'a, crate::values::MySQLValue<'a>>,
34        T::SQLType: drizzle_core::types::BooleanLike,
35    {
36    }
37}
38
39impl<'a, T> JoinCondition<'a> for T
40where
41    T: drizzle_core::expr::Expr<'a, MySQLValue<'a>>,
42    T::SQLType: drizzle_core::types::BooleanLike,
43{
44}
45
46/// A source that can follow `JOIN`: a `MySQL` table, a derived table
47/// (subquery with an alias), or a table with an index hint.
48#[doc(hidden)]
49#[diagnostic::on_unimplemented(
50    message = "`{Self}` cannot follow JOIN",
51    label = "join a table, a view, or an aliased subquery"
52)]
53pub trait JoinSource<'a>: join_source_private::Sealed {
54    type JoinedTable;
55
56    fn into_join_source_sql(self) -> SQL<'a, MySQLValue<'a>>;
57}
58
59mod join_source_private {
60    pub trait Sealed {}
61}
62
63impl<'a, Table> join_source_private::Sealed for Table where Table: MySQLTable<'a> {}
64
65impl<'a, Name, Projection, Query> join_source_private::Sealed
66    for drizzle_core::Derived<'a, MySQLValue<'a>, Name, Projection, Query>
67where
68    Name: drizzle_core::Tag,
69    Projection: drizzle_core::DerivedProjection<Name>,
70    Query: ToSQL<'a, MySQLValue<'a>>,
71{
72}
73
74impl<'a, Table> JoinSource<'a> for Table
75where
76    Table: MySQLTable<'a>,
77{
78    type JoinedTable = Table;
79
80    fn into_join_source_sql(self) -> SQL<'a, MySQLValue<'a>> {
81        self.into_sql()
82    }
83}
84
85impl<'a, Name, Projection, Query> JoinSource<'a>
86    for drizzle_core::Derived<'a, MySQLValue<'a>, Name, Projection, Query>
87where
88    Name: drizzle_core::Tag,
89    Projection: drizzle_core::DerivedProjection<Name>,
90    Query: ToSQL<'a, MySQLValue<'a>>,
91{
92    type JoinedTable = Self;
93
94    fn into_join_source_sql(self) -> SQL<'a, MySQLValue<'a>> {
95        self.into_sql()
96    }
97}
98
99/// A source or legacy tuple accepted by [`crate::builder::SelectBuilder::cross_join`].
100///
101/// A bare source renders `CROSS JOIN`. The legacy `(source, predicate)`
102/// form renders the equivalent portable `INNER JOIN ... ON ...`, because
103/// PostgreSQL does not allow an `ON` clause after `CROSS JOIN`.
104#[doc(hidden)]
105pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
106    type JoinedTable;
107    /// Sources read by the legacy `ON` predicate (see [`drizzle_core::scope`]).
108    type OnSources;
109
110    fn into_cross_join_sql(self) -> SQL<'a, MySQLValue<'a>>;
111}
112
113mod cross_join_arg_private {
114    pub trait Sealed {}
115
116    impl<'a, Source> Sealed for Source where Source: super::JoinSource<'a> {}
117
118    impl<'a, Source, Condition> Sealed for (Source, Condition)
119    where
120        Source: super::JoinSource<'a>,
121        Condition: super::JoinCondition<'a>,
122    {
123    }
124}
125
126impl<'a, Source, FromTable> CrossJoinArg<'a, FromTable> for Source
127where
128    Source: JoinSource<'a>,
129{
130    type JoinedTable = Source::JoinedTable;
131    type OnSources = ();
132
133    fn into_cross_join_sql(self) -> SQL<'a, MySQLValue<'a>> {
134        Join::new()
135            .cross()
136            .into_sql()
137            .append(self.into_join_source_sql())
138    }
139}
140
141impl<'a, Source, Condition, FromTable> CrossJoinArg<'a, FromTable> for (Source, Condition)
142where
143    Source: JoinSource<'a>,
144    Condition: JoinCondition<'a> + drizzle_core::expr::ExprSources,
145{
146    type JoinedTable = Source::JoinedTable;
147    type OnSources = Condition::Sources;
148
149    fn into_cross_join_sql(self) -> SQL<'a, MySQLValue<'a>> {
150        let (source, condition) = self;
151        Join::new()
152            .inner()
153            .into_sql()
154            .append(source.into_join_source_sql())
155            .push(Token::ON)
156            .append(condition.into_sql())
157    }
158}
159
160drizzle_core::impl_join_arg_trait!(
161    table_trait: MySQLTable<'a>,
162    table_info_trait: SQLTableInfo,
163    condition_trait: JoinCondition<'a>,
164    join_source_trait: JoinSource<'a>,
165    value_type: MySQLValue<'a>,
166);
167
168mod index_hint_private {
169    pub trait Kind {}
170    pub trait List<'a, Table> {}
171}
172
173/// Marker for a MySQL `USE INDEX` hint.
174#[doc(hidden)]
175#[derive(Debug, Clone, Copy, Default)]
176pub struct UseIndex;
177
178/// Marker for a MySQL `FORCE INDEX` hint.
179#[doc(hidden)]
180#[derive(Debug, Clone, Copy, Default)]
181pub struct ForceIndex;
182
183/// Marker for a MySQL `IGNORE INDEX` hint.
184#[doc(hidden)]
185#[derive(Debug, Clone, Copy, Default)]
186pub struct IgnoreIndex;
187
188impl index_hint_private::Kind for UseIndex {}
189impl index_hint_private::Kind for ForceIndex {}
190impl index_hint_private::Kind for IgnoreIndex {}
191
192#[doc(hidden)]
193pub trait IndexHintKind: index_hint_private::Kind {
194    const SQL: &'static str;
195}
196
197impl IndexHintKind for UseIndex {
198    const SQL: &'static str = "USE INDEX ";
199}
200
201impl IndexHintKind for ForceIndex {
202    const SQL: &'static str = "FORCE INDEX ";
203}
204
205impl IndexHintKind for IgnoreIndex {
206    const SQL: &'static str = "IGNORE INDEX ";
207}
208
209/// One or more generated indexes belonging to the same MySQL table.
210#[diagnostic::on_unimplemented(
211    message = "`{Self}` is not a list of `{Table}` indexes",
212    label = "an index hint names one or more indexes of the hinted table",
213    note = "pass one generated index of `{Table}`, or a tuple of them"
214)]
215#[doc(hidden)]
216pub trait IndexHintList<'a, Table>: index_hint_private::List<'a, Table> {
217    fn names(&self) -> SQL<'a, MySQLValue<'a>>;
218}
219
220impl<'a, Table, Index> index_hint_private::List<'a, Table> for Index where
221    Index: SQLIndex<'a, crate::common::MySQLSchemaType, MySQLValue<'a>, Table = Table>
222{
223}
224
225impl<'a, Table, Index> IndexHintList<'a, Table> for Index
226where
227    Index: SQLIndex<'a, crate::common::MySQLSchemaType, MySQLValue<'a>, Table = Table>,
228{
229    fn names(&self) -> SQL<'a, MySQLValue<'a>> {
230        SQL::ident(SQLIndexInfo::name(self))
231    }
232}
233
234macro_rules! index_hint_tuple {
235    ($($index:ident: $field:tt),+) => {
236        impl<'a, Table, $($index),+> index_hint_private::List<'a, Table> for ($($index,)+)
237        where
238            $($index: SQLIndex<'a, crate::common::MySQLSchemaType, MySQLValue<'a>, Table = Table>,)+
239        {
240        }
241
242        impl<'a, Table, $($index),+> IndexHintList<'a, Table> for ($($index,)+)
243        where
244            $($index: SQLIndex<'a, crate::common::MySQLSchemaType, MySQLValue<'a>, Table = Table>,)+
245        {
246            fn names(&self) -> SQL<'a, MySQLValue<'a>> {
247                SQL::join(
248                    [$(SQL::ident(SQLIndexInfo::name(&self.$field)),)+],
249                    Token::COMMA,
250                )
251            }
252        }
253    };
254}
255
256index_hint_tuple!(A: 0, B: 1);
257index_hint_tuple!(A: 0, B: 1, C: 2);
258index_hint_tuple!(A: 0, B: 1, C: 2, D: 3);
259index_hint_tuple!(A: 0, B: 1, C: 2, D: 3, E: 4);
260index_hint_tuple!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5);
261index_hint_tuple!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6);
262index_hint_tuple!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7);
263
264/// A table with one index hint, ready to be joined.
265///
266/// Created by the methods of [`MySQLIndexHintExt`].
267#[derive(Debug, Clone, Copy)]
268pub struct IndexHintedTable<Table, Indexes, Kind> {
269    table: Table,
270    indexes: Indexes,
271    kind: core::marker::PhantomData<Kind>,
272}
273
274impl<Table, Indexes, Kind> join_source_private::Sealed for IndexHintedTable<Table, Indexes, Kind> {}
275
276impl<Table, Indexes, Kind> IndexHintedTable<Table, Indexes, Kind> {
277    fn into_sql<'a>(self) -> SQL<'a, MySQLValue<'a>>
278    where
279        Table: MySQLTable<'a>,
280        Indexes: IndexHintList<'a, Table>,
281        Kind: IndexHintKind,
282    {
283        self.table
284            .into_sql()
285            .append(SQL::raw(Kind::SQL))
286            .append(self.indexes.names().parens())
287    }
288}
289
290impl<'a, Table, Indexes, Kind> JoinSource<'a> for IndexHintedTable<Table, Indexes, Kind>
291where
292    Table: MySQLTable<'a>,
293    Indexes: IndexHintList<'a, Table>,
294    Kind: IndexHintKind,
295{
296    type JoinedTable = Table;
297
298    fn into_join_source_sql(self) -> SQL<'a, MySQLValue<'a>> {
299        self.into_sql()
300    }
301}
302
303/// Adds an index hint to a table you are about to join.
304///
305/// Implemented for every `MySQL` table. For the FROM table, use the
306/// `use_index` / `force_index` / `ignore_index` methods on the select
307/// builder instead. Each index must belong to the hinted table; an index of
308/// another table does not compile. Pass one index or a tuple of up to eight.
309///
310/// # Examples
311///
312/// ```rust
313/// # let _ = r####"
314/// # use drizzle::core::expr::{alias, count, eq, gt};
315/// # use drizzle::mysql::{builder::QueryBuilder, prelude::*};
316/// # #[MySQLTable(NAME = "users")]
317/// # struct Users {
318/// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
319/// #     #[column(VARCHAR(255))] name: String,
320/// #     #[column(DEFAULT = true)] active: bool,
321/// # }
322/// # #[MySQLTable(NAME = "posts")]
323/// # struct Posts {
324/// #     #[column(PRIMARY, AUTO_INCREMENT)] id: u64,
325/// #     #[column(REFERENCES = Users::id)] user_id: u64,
326/// #     title: String,
327/// # }
328/// # #[MySQLIndex] struct UsersNameIdx(Users::name);
329/// # #[MySQLIndex] struct PostsUserIdIdx(Posts::user_id);
330/// # #[derive(MySQLSchema)] struct Schema { users: Users, posts: Posts }
331/// # let builder = QueryBuilder::new::<Schema>();
332/// # let Schema { users, posts } = Schema::new();
333/// let query = builder
334///     .select((users.id, posts.id))
335///     .from(users)
336///     .inner_join((posts.use_index(PostsUserIdIdx::new()), eq(posts.user_id, users.id)));
337/// assert_eq!(
338///     query.to_sql().sql(),
339///     "SELECT `users`.`id`, `posts`.`id` FROM `users` INNER JOIN `posts` USE INDEX (`posts_user_id_idx`) ON `posts`.`user_id` = `users`.`id`"
340/// );
341/// # "####;
342/// ```
343pub trait MySQLIndexHintExt: Sized {
344    /// Adds `USE INDEX (..)`: `MySQL` only considers these indexes.
345    fn use_index<Indexes>(self, indexes: Indexes) -> IndexHintedTable<Self, Indexes, UseIndex>
346    where
347        Indexes: for<'a> IndexHintList<'a, Self>,
348    {
349        IndexHintedTable {
350            table: self,
351            indexes,
352            kind: core::marker::PhantomData,
353        }
354    }
355
356    /// Adds `FORCE INDEX (..)`: `MySQL` uses a table scan only if none of
357    /// these indexes can be used.
358    fn force_index<Indexes>(self, indexes: Indexes) -> IndexHintedTable<Self, Indexes, ForceIndex>
359    where
360        Indexes: for<'a> IndexHintList<'a, Self>,
361    {
362        IndexHintedTable {
363            table: self,
364            indexes,
365            kind: core::marker::PhantomData,
366        }
367    }
368
369    /// Adds `IGNORE INDEX (..)`: `MySQL` does not use these indexes.
370    fn ignore_index<Indexes>(self, indexes: Indexes) -> IndexHintedTable<Self, Indexes, IgnoreIndex>
371    where
372        Indexes: for<'a> IndexHintList<'a, Self>,
373    {
374        IndexHintedTable {
375            table: self,
376            indexes,
377            kind: core::marker::PhantomData,
378        }
379    }
380}
381
382impl<Table> MySQLIndexHintExt for Table where Table: for<'a> MySQLTable<'a> {}
383
384pub(crate) fn index_hint<'a, Table, Indexes, Kind>(indexes: &Indexes) -> SQL<'a, MySQLValue<'a>>
385where
386    Table: MySQLTable<'a>,
387    Indexes: IndexHintList<'a, Table>,
388    Kind: IndexHintKind,
389{
390    SQL::raw(Kind::SQL).append(indexes.names().parens())
391}
392
393fn auto_join_condition<'a, Joined, From, Via>() -> SQL<'a, MySQLValue<'a>>
394where
395    Joined: MySQLTable<'a> + drizzle_core::JoinKey<From, Via> + Default,
396    From: SQLTableInfo + Default,
397{
398    let joined = Joined::default();
399    let from = From::default();
400    let columns = <Joined as drizzle_core::JoinKey<From, Via>>::pairs();
401    let mut condition = SQL::with_capacity_chunks(columns.len().saturating_mul(7));
402    for (index, pair) in columns.iter().enumerate() {
403        let (joined_column, from_column) = <Joined as drizzle_core::JoinKey<From, Via>>::pair(pair);
404        if index > 0 {
405            condition.push_mut(Token::AND);
406        }
407        condition.append_mut(
408            SQL::ident(joined.name())
409                .push(Token::DOT)
410                .append(SQL::ident(joined_column)),
411        );
412        condition.push_mut(Token::EQ);
413        condition.append_mut(
414            SQL::ident(from.name())
415                .push(Token::DOT)
416                .append(SQL::ident(from_column)),
417        );
418    }
419    condition
420}
421
422impl<'a, Joined, Indexes, Kind, From, Via> JoinArg<'a, From, Via>
423    for IndexHintedTable<Joined, Indexes, Kind>
424where
425    Joined: MySQLTable<'a> + drizzle_core::JoinKey<From, Via> + Default,
426    From: SQLTableInfo + Default,
427    Indexes: IndexHintList<'a, Joined>,
428    Kind: IndexHintKind,
429{
430    type JoinedTable = Joined;
431    type OnSources = ();
432
433    fn into_join_sql(self, join: Join) -> SQL<'a, MySQLValue<'a>> {
434        join.into_sql()
435            .append(self.into_sql())
436            .push(Token::ON)
437            .append(auto_join_condition::<Joined, From, Via>())
438    }
439}
440
441pub(crate) fn insert_ignore<'a>(mut insert: SQL<'a, MySQLValue<'a>>) -> SQL<'a, MySQLValue<'a>> {
442    debug_assert!(matches!(
443        insert.chunks.first(),
444        Some(SQLChunk::Token(Token::INSERT))
445    ));
446    insert.chunks.insert(1, SQLChunk::Token(Token::IGNORE));
447    insert
448}
449
450pub(crate) fn on_duplicate_key_update<'a, Table>(
451    assignments: &Table::Update,
452) -> SQL<'a, MySQLValue<'a>>
453where
454    Table: MySQLTable<'a>,
455{
456    let assignment_sql = assignments.to_sql();
457    assert!(
458        !assignment_sql.chunks.is_empty(),
459        "on_duplicate_key_update requires at least one assignment"
460    );
461    SQL::from_iter([Token::ON, Token::DUPLICATE, Token::KEY, Token::UPDATE]).append(assignment_sql)
462}
463
464#[track_caller]
465pub(crate) fn values<'a, Table, T>(
466    rows: impl IntoIterator<Item = Table::Insert<T>>,
467) -> SQL<'a, MySQLValue<'a>>
468where
469    Table: MySQLTable<'a>,
470{
471    let rows: Vec<_> = rows.into_iter().collect();
472    if rows.is_empty() {
473        return match <Table as drizzle_core::SQLSchema<
474            'a,
475            crate::common::MySQLSchemaType,
476            MySQLValue<'a>,
477        >>::TYPE
478        {
479            crate::common::MySQLSchemaType::Table(table) => {
480                drizzle_core::helpers::insert_no_rows(table, "SELECT NULL FROM DUAL WHERE 1 = 0")
481            }
482            _ => SQL::from(Token::VALUES),
483        };
484    }
485
486    let columns = rows[0].columns();
487    // A `None` passed to a `with_*` setter leaves that column to its default
488    // without changing the row's type, so rows can set different columns.
489    // Then every row lists the union of the columns, with `DEFAULT` where it
490    // sets none.
491    if rows[1..]
492        .iter()
493        .any(|row| row.columns().as_ref() != columns.as_ref())
494        && let Some(rows_sql) = drizzle_core::helpers::insert_values_with_defaults(
495            rows.iter()
496                .map(|row| (row.columns(), row.values()))
497                .collect(),
498        )
499    {
500        return rows_sql;
501    }
502
503    if columns.is_empty() {
504        let mut row_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(3));
505        for index in 0..rows.len() {
506            if index > 0 {
507                row_sql.push_mut(Token::COMMA);
508            }
509            row_sql.append_mut(SQL::empty().parens());
510        }
511        return SQL::empty().parens().push(Token::VALUES).append(row_sql);
512    }
513
514    let mut row_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
515    for (index, row) in rows.iter().enumerate() {
516        if index > 0 {
517            row_sql.push_mut(Token::COMMA);
518        }
519        row_sql.push_mut(Token::LPAREN);
520        row_sql.append_mut(row.values());
521        row_sql.push_mut(Token::RPAREN);
522    }
523
524    SQL::columns(columns.as_ref())
525        .parens()
526        .push(Token::VALUES)
527        .append(row_sql)
528}
529
530pub(crate) fn standalone_offset<'a, P>(offset: P) -> SQL<'a, MySQLValue<'a>>
531where
532    P: drizzle_core::PaginationArg<'a, MySQLValue<'a>>,
533{
534    SQL::from(Token::LIMIT)
535        .append(SQL::raw(drizzle_core::helpers::MYSQL_UNBOUNDED_LIMIT))
536        .append(helpers::offset(offset))
537}
538
539pub(crate) fn unqualified_columns<'a>(
540    mut columns: SQL<'a, MySQLValue<'a>>,
541) -> SQL<'a, MySQLValue<'a>> {
542    for chunk in &mut columns.chunks {
543        if let SQLChunk::Column(column) = chunk {
544            *chunk = SQLChunk::ident_static(column.name);
545        }
546    }
547    columns
548}
549
550/// An ORDER BY term with a direction, made by [`asc`] or [`desc`].
551///
552/// It keeps its operand unrendered so that, after a set operation, the
553/// builder can write a column without its table name.
554#[derive(Debug, Clone, Copy)]
555pub struct OrderExpr<T> {
556    value: T,
557    direction: drizzle_core::OrderBy,
558}
559
560impl<T: drizzle_core::expr::ExprSources> drizzle_core::expr::ExprSources for OrderExpr<T> {
561    type Sources = T::Sources;
562}
563
564impl<T: drizzle_core::expr::ExprSources> drizzle_core::OrderTerm for OrderExpr<T> {}
565
566/// An output alias names a SELECT output, not a FROM source.
567impl drizzle_core::expr::ExprSources for OutputAlias {
568    type Sources = ();
569}
570
571impl<'a, T> ToSQL<'a, MySQLValue<'a>> for OrderExpr<T>
572where
573    T: ToSQL<'a, MySQLValue<'a>>,
574{
575    fn to_sql(&self) -> SQL<'a, MySQLValue<'a>> {
576        self.value.to_sql().append(&self.direction)
577    }
578}
579
580/// Sorts by `value` in ascending order (`value ASC`).
581pub const fn asc<T>(value: T) -> OrderExpr<T> {
582    OrderExpr {
583        value,
584        direction: drizzle_core::OrderBy::Asc,
585    }
586}
587
588/// Sorts by `value` in descending order (`value DESC`).
589pub const fn desc<T>(value: T) -> OrderExpr<T> {
590    OrderExpr {
591        value,
592        direction: drizzle_core::OrderBy::Desc,
593    }
594}
595
596/// The name of a SELECT output, for the ORDER BY of a set operation.
597///
598/// Made by [`output_alias`].
599#[derive(Debug, Clone, Copy)]
600pub struct OutputAlias(&'static str);
601
602impl<'a> ToSQL<'a, MySQLValue<'a>> for OutputAlias {
603    fn to_sql(&self) -> SQL<'a, MySQLValue<'a>> {
604        SQL::ident(self.0)
605    }
606}
607
608/// Refers to a named SELECT output in the ORDER BY of a set operation.
609///
610/// Give the output its name with `alias(expr, "name")` in the first
611/// query. The name is not checked against the query, so a typo is only
612/// caught by `MySQL`. See the set-operation `order_by` on
613/// [`SelectBuilder`](crate::builder::SelectBuilder) for a full query.
614///
615/// # Examples
616///
617/// ```
618/// use drizzle_core::ToSQL;
619/// use drizzle_mysql::helpers::{asc, desc, output_alias};
620/// use drizzle_mysql::values::MySQLValue;
621///
622/// let term: drizzle_core::SQL<'_, MySQLValue<'_>> = desc(output_alias("label")).to_sql();
623/// assert_eq!(term.sql(), "`label` DESC");
624///
625/// let term: drizzle_core::SQL<'_, MySQLValue<'_>> = asc(output_alias("total")).to_sql();
626/// assert_eq!(term.sql(), "`total` ASC");
627/// ```
628#[must_use]
629pub const fn output_alias(name: &'static str) -> OutputAlias {
630    OutputAlias(name)
631}
632
633#[doc(hidden)]
634pub trait SetOrderBy<'a, Projection, Table, Proof>:
635    set_order_private::Sealed<'a, Projection, Table, Proof>
636{
637    fn into_set_order_sql(self) -> SQL<'a, MySQLValue<'a>>;
638}
639
640mod set_order_private {
641    use super::{MySQLValue, OrderExpr, OutputAlias, ToSQL};
642
643    pub trait ProjectionAllows<'a, Item, Table, Proof> {}
644
645    impl<'a, Cols, Scope, Used, Item, Table, Proof> ProjectionAllows<'a, Item, Table, Proof>
646        for drizzle_core::Scoped<drizzle_core::SelectCols<Cols>, Scope, Used>
647    where
648        Cols: drizzle_core::row::SelectedExpressionList,
649        <Cols as drizzle_core::row::SelectedExpressionList>::Expressions:
650            drizzle_core::scope::ListContains<Item, Proof>,
651    {
652    }
653
654    impl<'a, Scope, Used, Item, Table> ProjectionAllows<'a, Item, Table, ()>
655        for drizzle_core::Scoped<drizzle_core::SelectStar, Scope, Used>
656    where
657        Item: drizzle_core::traits::SQLColumn<'a, MySQLValue<'a>>
658            + drizzle_core::traits::ColumnOf<Table>,
659    {
660    }
661
662    pub trait ProjectionListAllowed<'a, Projection, Table, Proof> {}
663
664    impl<'a, Projection, Table> ProjectionListAllowed<'a, Projection, Table, ()> for drizzle_core::Nil {}
665
666    impl<'a, Projection, Table, Head, Tail, HeadProof, TailProof>
667        ProjectionListAllowed<'a, Projection, Table, (HeadProof, TailProof)>
668        for drizzle_core::Cons<Head, Tail>
669    where
670        Head: drizzle_core::traits::SQLColumn<'a, MySQLValue<'a>>,
671        Projection: ProjectionAllows<'a, Head, Table, HeadProof>,
672        Tail: ProjectionListAllowed<'a, Projection, Table, TailProof>,
673    {
674    }
675
676    pub trait Sealed<'a, Projection, Table, Proof> {}
677
678    impl<'a, Projection, Table, Columns, Cols, Proof> Sealed<'a, Projection, Table, Proof> for Columns
679    where
680        Columns: ToSQL<'a, MySQLValue<'a>>
681            + drizzle_core::IntoSelectTarget<Marker = drizzle_core::SelectCols<Cols>>,
682        Cols: drizzle_core::row::SelectedExpressionList,
683        <Cols as drizzle_core::row::SelectedExpressionList>::Expressions:
684            ProjectionListAllowed<'a, Projection, Table, Proof>,
685    {
686    }
687
688    impl<'a, Projection, Table, Column, Proof> Sealed<'a, Projection, Table, Proof>
689        for OrderExpr<Column>
690    where
691        Column: drizzle_core::traits::SQLColumn<'a, MySQLValue<'a>>,
692        Projection: ProjectionAllows<'a, Column, Table, Proof>,
693    {
694    }
695
696    impl<'a, Projection, Table> Sealed<'a, Projection, Table, ()> for OutputAlias {}
697    impl<'a, Projection, Table> Sealed<'a, Projection, Table, ()> for OrderExpr<OutputAlias> {}
698}
699
700impl<'a, Projection, Table, Columns, Cols, Proof> SetOrderBy<'a, Projection, Table, Proof>
701    for Columns
702where
703    Columns: ToSQL<'a, MySQLValue<'a>>
704        + drizzle_core::IntoSelectTarget<Marker = drizzle_core::SelectCols<Cols>>,
705    Cols: drizzle_core::row::SelectedExpressionList,
706    <Cols as drizzle_core::row::SelectedExpressionList>::Expressions:
707        set_order_private::ProjectionListAllowed<'a, Projection, Table, Proof>,
708{
709    fn into_set_order_sql(self) -> SQL<'a, MySQLValue<'a>> {
710        unqualified_columns(self.into_sql())
711    }
712}
713
714impl<'a, Projection, Table, Column, Proof> SetOrderBy<'a, Projection, Table, Proof>
715    for OrderExpr<Column>
716where
717    Column: drizzle_core::traits::SQLColumn<'a, MySQLValue<'a>>,
718    Projection: set_order_private::ProjectionAllows<'a, Column, Table, Proof>,
719{
720    fn into_set_order_sql(self) -> SQL<'a, MySQLValue<'a>> {
721        unqualified_columns(self.value.into_sql()).append(&self.direction)
722    }
723}
724
725impl<'a, Projection, Table> SetOrderBy<'a, Projection, Table, ()> for OutputAlias {
726    fn into_set_order_sql(self) -> SQL<'a, MySQLValue<'a>> {
727        self.to_sql()
728    }
729}
730
731impl<'a, Projection, Table> SetOrderBy<'a, Projection, Table, ()> for OrderExpr<OutputAlias> {
732    fn into_set_order_sql(self) -> SQL<'a, MySQLValue<'a>> {
733        self.value.to_sql().append(&self.direction)
734    }
735}
736
737pub(crate) fn set_op<'a>(
738    left: SQL<'a, MySQLValue<'a>>,
739    operator: Token,
740    all: bool,
741    right: SQL<'a, MySQLValue<'a>>,
742) -> SQL<'a, MySQLValue<'a>> {
743    let sql = left.parens().push(operator);
744    let sql = if all { sql.push(Token::ALL) } else { sql };
745    sql.append(right.parens())
746}