Skip to main content

drizzle_core/
derived.rs

1//! Typed derived-table projections.
2//!
3//! A [`Derived`] value owns a complete query and exposes only the fields named
4//! by its projection. Dialect builders construct these values after checking
5//! that the query is in an executable state.
6
7use core::marker::PhantomData;
8
9use crate::expr::{AllScalar, Expr, HasAggStatus, Scalar};
10use crate::row::{
11    ExprValueType, GroupByIdentity, HasSelectModel, IntoGroupBy, IntoSelectTarget, Scoped,
12    SelectCols, SelectStar,
13};
14use crate::{Cons, Nil, SQL, SQLColumnInfo, SQLParam, SQLSchemaType, SQLTable, Tag, ToSQL, Token};
15
16mod private {
17    pub trait Projection {}
18    pub trait Selection<'a, V: crate::SQLParam, Schema, Table> {}
19    pub trait Output {}
20}
21
22/// A complete query used as a named source in another query.
23pub struct Derived<'a, V: SQLParam, Name, Projection, Query> {
24    query: Query,
25    marker: PhantomData<(&'a (), V, Name, Projection)>,
26}
27
28impl<'a, V, Name, Projection, Query> Derived<'a, V, Name, Projection, Query>
29where
30    V: SQLParam,
31    Name: Tag,
32    Projection: DerivedProjection<Name>,
33{
34    /// Constructs a derived source without validating its query.
35    ///
36    /// # Safety
37    ///
38    /// `query` must select exactly the columns described by `Projection`, in
39    /// the same order, and must already satisfy the dialect builder's scope
40    /// and aggregate rules.
41    #[doc(hidden)]
42    #[track_caller]
43    pub unsafe fn new_unchecked(query: Query) -> Self {
44        Projection::validate();
45        Self {
46            query,
47            marker: PhantomData,
48        }
49    }
50
51    /// Returns the underlying query.
52    pub const fn query(&self) -> &Query {
53        &self.query
54    }
55
56    /// Returns the underlying query by value.
57    pub fn into_query(self) -> Query {
58        self.query
59    }
60
61    /// Returns typed fields qualified by this source's name.
62    pub fn fields(&self) -> Projection::Fields
63    where
64        Name: Tag,
65        Projection: DerivedProjection<Name>,
66    {
67        Projection::fields()
68    }
69}
70
71impl<'a, V: SQLParam, Name, Projection, Query: Clone> Clone
72    for Derived<'a, V, Name, Projection, Query>
73{
74    fn clone(&self) -> Self {
75        Self {
76            query: self.query.clone(),
77            marker: PhantomData,
78        }
79    }
80}
81
82impl<'a, V, Name, Projection, Query> ToSQL<'a, V> for Derived<'a, V, Name, Projection, Query>
83where
84    V: SQLParam,
85    Name: Tag,
86    Query: ToSQL<'a, V>,
87{
88    fn to_sql(&self) -> SQL<'a, V> {
89        self.query
90            .to_sql()
91            .parens()
92            .push(Token::AS)
93            .append(SQL::table(crate::TableRef::sql(Name::NAME, &[])))
94    }
95
96    fn into_sql(self) -> SQL<'a, V> {
97        self.query
98            .into_sql()
99            .parens()
100            .push(Token::AS)
101            .append(SQL::table(crate::TableRef::sql(Name::NAME, &[])))
102    }
103}
104
105impl<'a, V, Name, Projection, Query> HasSelectModel for Derived<'a, V, Name, Projection, Query>
106where
107    V: SQLParam,
108    Name: Tag,
109    Projection: DerivedProjection<Name>,
110{
111    type SelectModel = Projection::Row;
112
113    const COLUMN_COUNT: usize = Projection::COLUMN_COUNT;
114}
115
116/// Maps a SELECT projection to the fields and row exposed by a derived source.
117#[doc(hidden)]
118pub trait DerivedProjection<Name: Tag>: private::Projection {
119    type Fields;
120    type Row;
121
122    const COLUMN_COUNT: usize;
123
124    fn validate() {}
125
126    fn fields() -> Self::Fields;
127}
128
129/// Select-marker capability for becoming a derived source.
130///
131/// The single-table `SELECT *` implementation deliberately matches only an
132/// exact one-table scope. After a join, `SELECT *` contains more columns and
133/// cannot soundly expose the last joined table as its complete projection.
134#[doc(hidden)]
135pub trait DerivedSelection<'a, V: SQLParam, Schema, Table>:
136    private::Selection<'a, V, Schema, Table>
137{
138    type Projection;
139}
140
141impl<'a, V, Schema, Table> DerivedSelection<'a, V, Schema, Table>
142    for Scoped<SelectStar, Cons<Table, Nil>>
143where
144    V: SQLParam + 'a,
145    Schema: SQLSchemaType,
146    Table: SQLTable<'a, Schema, V>,
147{
148    type Projection = TableProjection<'a, V, Schema, Table>;
149}
150
151impl<'a, V, Schema, Table> private::Selection<'a, V, Schema, Table>
152    for Scoped<SelectStar, Cons<Table, Nil>>
153where
154    V: SQLParam + 'a,
155    Schema: SQLSchemaType,
156    Table: SQLTable<'a, Schema, V>,
157{
158}
159
160impl<'a, V, Schema, Name, Projection, Query>
161    DerivedSelection<'a, V, Schema, Derived<'a, V, Name, Projection, Query>>
162    for Scoped<SelectStar, Cons<Derived<'a, V, Name, Projection, Query>, Nil>>
163where
164    V: SQLParam,
165    Name: Tag,
166    Projection: DerivedProjection<Name>,
167{
168    type Projection = Projection;
169}
170
171impl<'a, V, Schema, Name, Projection, Query>
172    private::Selection<'a, V, Schema, Derived<'a, V, Name, Projection, Query>>
173    for Scoped<SelectStar, Cons<Derived<'a, V, Name, Projection, Query>, Nil>>
174where
175    V: SQLParam,
176    Name: Tag,
177    Projection: DerivedProjection<Name>,
178{
179}
180
181impl<'a, V, Schema, Table, Columns, Scope> DerivedSelection<'a, V, Schema, Table>
182    for Scoped<SelectCols<Columns>, Scope>
183where
184    V: SQLParam,
185{
186    type Projection = Self;
187}
188
189impl<'a, V, Schema, Table, Columns, Scope> private::Selection<'a, V, Schema, Table>
190    for Scoped<SelectCols<Columns>, Scope>
191where
192    V: SQLParam,
193{
194}
195
196impl<Name, Marker, Scope> DerivedProjection<Name> for Scoped<Marker, Scope>
197where
198    Name: Tag,
199    Marker: DerivedProjection<Name>,
200{
201    type Fields = Marker::Fields;
202    type Row = Marker::Row;
203
204    const COLUMN_COUNT: usize = Marker::COLUMN_COUNT;
205
206    fn validate() {
207        Marker::validate();
208    }
209
210    fn fields() -> Self::Fields {
211        Marker::fields()
212    }
213}
214
215impl<Marker, Scope> private::Projection for Scoped<Marker, Scope> where Marker: private::Projection {}
216
217/// Projection marker used when a dialect has proven that `SELECT *` comes from
218/// one base table.
219#[doc(hidden)]
220pub struct TableProjection<'a, V: SQLParam, Schema, Table>(PhantomData<(&'a (), V, Schema, Table)>);
221
222impl<V: SQLParam, Schema, Table> private::Projection for TableProjection<'_, V, Schema, Table> {}
223
224impl<'a, V, Schema, Name, Table> DerivedProjection<Name> for TableProjection<'a, V, Schema, Table>
225where
226    V: SQLParam + 'a,
227    Schema: SQLSchemaType,
228    Name: Tag + 'static,
229    Table: SQLTable<'a, Schema, V> + HasSelectModel,
230    Table::Aliased<Name>: HasSelectModel<SelectModel = Table::SelectModel>,
231{
232    type Fields = Table::Aliased<Name>;
233    type Row = Table::SelectModel;
234
235    const COLUMN_COUNT: usize = Table::COLUMN_COUNT;
236
237    fn fields() -> Self::Fields {
238        Table::alias::<Name>()
239    }
240}
241
242/// A field exposed by a named derived source.
243pub struct DerivedField<Name, Output>(PhantomData<(Name, Output)>);
244
245impl<Name, Output> Copy for DerivedField<Name, Output> {}
246
247impl<Name, Output> Clone for DerivedField<Name, Output> {
248    fn clone(&self) -> Self {
249        *self
250    }
251}
252
253impl<Name, Output> Default for DerivedField<Name, Output> {
254    fn default() -> Self {
255        Self(PhantomData)
256    }
257}
258
259impl<'a, V, Name, Output> ToSQL<'a, V> for DerivedField<Name, Output>
260where
261    V: SQLParam,
262    Name: Tag,
263    Output: ProjectionOutput,
264{
265    fn to_sql(&self) -> SQL<'a, V> {
266        SQL::ident(Name::NAME)
267            .push(Token::DOT)
268            .append(SQL::ident(Output::output_name()))
269    }
270}
271
272impl<'a, V, Name, Output> Expr<'a, V> for DerivedField<Name, Output>
273where
274    V: SQLParam + 'a,
275    Name: Tag,
276    Output: ProjectionOutput + Expr<'a, V>,
277{
278    type SQLType = Output::SQLType;
279    type Nullable = Output::Nullable;
280    type Aggregate = Scalar;
281}
282
283impl<Name, Output> ExprValueType for DerivedField<Name, Output>
284where
285    Name: Tag,
286    Output: ProjectionOutput + ExprValueType,
287{
288    type ValueType = Output::ValueType;
289}
290
291impl<Name, Output> IntoSelectTarget for DerivedField<Name, Output>
292where
293    Name: Tag,
294    Output: ProjectionOutput + ExprValueType,
295{
296    type Marker = SelectCols<(Self,)>;
297}
298
299impl<Name, Output> HasAggStatus for DerivedField<Name, Output>
300where
301    Name: Tag,
302    Output: ProjectionOutput,
303{
304    type Status = AllScalar;
305}
306
307impl<Name, Output> GroupByIdentity for DerivedField<Name, Output>
308where
309    Name: Tag,
310    Output: ProjectionOutput,
311{
312    type Identity = Self;
313}
314
315impl<'a, V, Name, Output, Projection, Query>
316    crate::traits::ColumnOf<Derived<'a, V, Name, Projection, Query>> for DerivedField<Name, Output>
317where
318    V: SQLParam,
319{
320}
321
322impl<'a, V, Name, Output, Projection, Query, Scope, Witness>
323    crate::row::ProjectionInScope<
324        Scope,
325        crate::row::ColumnScope<Derived<'a, V, Name, Projection, Query>, Witness>,
326    > for DerivedField<Name, Output>
327where
328    V: SQLParam,
329    Scope: crate::row::ScopeContains<Derived<'a, V, Name, Projection, Query>, Witness>,
330{
331}
332
333impl<'a, V, Name, Output> IntoGroupBy<'a, V> for DerivedField<Name, Output>
334where
335    V: SQLParam + 'a,
336    Name: Tag,
337    Output: ProjectionOutput,
338{
339    type Columns = Cons<Self, Nil>;
340}
341
342/// Supplies the static output name for one SELECT expression.
343#[doc(hidden)]
344pub trait ProjectionOutput: private::Output {
345    fn output_name() -> &'static str;
346}
347
348impl<Column> private::Output for Column where Column: SQLColumnInfo + Default {}
349
350impl<Column> ProjectionOutput for Column
351where
352    Column: SQLColumnInfo + Default,
353{
354    fn output_name() -> &'static str {
355        Column::default().name()
356    }
357}
358
359impl<E, Name> ProjectionOutput for crate::expr::NamedExpr<E, Name>
360where
361    Name: Tag,
362{
363    fn output_name() -> &'static str {
364        Name::NAME
365    }
366}
367
368impl<E, Name> private::Output for crate::expr::NamedExpr<E, Name> where Name: Tag {}
369
370impl<Name, Output> ProjectionOutput for DerivedField<Name, Output>
371where
372    Name: Tag,
373    Output: ProjectionOutput,
374{
375    fn output_name() -> &'static str {
376        Output::output_name()
377    }
378}
379
380impl<Name, Output> private::Output for DerivedField<Name, Output>
381where
382    Name: Tag,
383    Output: ProjectionOutput,
384{
385}
386
387macro_rules! impl_derived_projection_tuple {
388    ($($output:ident),+; $($_index:tt),+) => {
389        impl<Name, $($output),+> DerivedProjection<Name> for SelectCols<($($output,)+)>
390        where
391            Name: Tag,
392            $($output: ProjectionOutput + ExprValueType,)+
393        {
394            type Fields = ($(DerivedField<Name, $output>,)+);
395            type Row = ($(<$output as ExprValueType>::ValueType,)+);
396
397            const COLUMN_COUNT: usize = impl_derived_projection_tuple!(@count $($output),+);
398
399            fn validate() {
400                let names = [$(<$output as ProjectionOutput>::output_name(),)+];
401                let mut left = 0;
402                while left < names.len() {
403                    let mut right = left + 1;
404                    while right < names.len() {
405                        assert!(
406                            names[left] != names[right],
407                            "derived projection contains duplicate output name `{}`; name one expression with `.named::<Tag>()`",
408                            names[left],
409                        );
410                        right += 1;
411                    }
412                    left += 1;
413                }
414            }
415
416            fn fields() -> Self::Fields {
417                ($(DerivedField::<Name, $output>::default(),)+)
418            }
419        }
420
421        impl<$($output),+> private::Projection for SelectCols<($($output,)+)>
422        where
423            $($output: ProjectionOutput + ExprValueType,)+
424        {
425        }
426    };
427    (@count $head:ident $(,$tail:ident)*) => {
428        1usize $(+ { let _ = stringify!($tail); 1usize })*
429    };
430}
431
432with_col_sizes_8!(impl_derived_projection_tuple);
433
434#[cfg(any(
435    feature = "col16",
436    feature = "col32",
437    feature = "col64",
438    feature = "col128",
439    feature = "col200"
440))]
441with_col_sizes_16!(impl_derived_projection_tuple);
442
443#[cfg(any(
444    feature = "col32",
445    feature = "col64",
446    feature = "col128",
447    feature = "col200"
448))]
449with_col_sizes_32!(impl_derived_projection_tuple);
450
451#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
452with_col_sizes_64!(impl_derived_projection_tuple);
453
454#[cfg(any(feature = "col128", feature = "col200"))]
455with_col_sizes_128!(impl_derived_projection_tuple);
456
457#[cfg(feature = "col200")]
458with_col_sizes_200!(impl_derived_projection_tuple);
459
460#[cfg(test)]
461mod tests {
462    use super::*;
463    use crate::{Dialect, SQLiteDialect};
464
465    #[derive(Clone, Debug)]
466    struct TestParam;
467
468    impl SQLParam for TestParam {
469        const DIALECT: Dialect = Dialect::SQLite;
470        type DialectMarker = SQLiteDialect;
471    }
472
473    struct Alias;
474
475    impl Tag for Alias {
476        const NAME: &'static str = "alias";
477    }
478
479    struct First;
480    struct Second;
481
482    impl private::Output for First {}
483    impl private::Output for Second {}
484
485    impl ProjectionOutput for First {
486        fn output_name() -> &'static str {
487            "duplicate"
488        }
489    }
490
491    impl ProjectionOutput for Second {
492        fn output_name() -> &'static str {
493            "duplicate"
494        }
495    }
496
497    impl ExprValueType for First {
498        type ValueType = i32;
499    }
500
501    impl ExprValueType for Second {
502        type ValueType = i32;
503    }
504
505    #[test]
506    #[should_panic(expected = "duplicate output name")]
507    fn duplicate_projection_names_are_rejected() {
508        let _: Derived<'_, TestParam, Alias, SelectCols<(First, Second)>, ()> =
509            // SAFETY: This test exercises projection-name validation before
510            // the query is ever rendered or decoded.
511            unsafe { Derived::new_unchecked(()) };
512    }
513}