1use core::marker::PhantomData;
10
11use crate::expr::{AllScalar, Expr, HasAggStatus, Scalar};
12use crate::row::{
13 ExprValueType, GroupByIdentity, HasSelectModel, IntoGroupBy, IntoSelectTarget, Scoped,
14 SelectCols, SelectStar,
15};
16use crate::{Cons, Nil, SQL, SQLColumnInfo, SQLParam, SQLSchemaType, SQLTable, Tag, ToSQL, Token};
17
18mod private {
19 pub trait Projection {}
20 pub trait Selection<'a, V: crate::SQLParam, Schema, Table> {}
21 pub trait Output {}
22}
23
24pub struct Derived<'a, V: SQLParam, Name, Projection, Query> {
32 query: Query,
33 marker: PhantomData<(&'a (), V, Name, Projection)>,
34}
35
36impl<'a, V, Name, Projection, Query> Derived<'a, V, Name, Projection, Query>
37where
38 V: SQLParam,
39 Name: Tag,
40 Projection: DerivedProjection<Name>,
41{
42 #[doc(hidden)]
51 #[track_caller]
52 pub unsafe fn new_unchecked(query: Query) -> Self {
53 Projection::validate();
54 Self {
55 query,
56 marker: PhantomData,
57 }
58 }
59
60 pub const fn query(&self) -> &Query {
62 &self.query
63 }
64
65 pub fn into_query(self) -> Query {
67 self.query
68 }
69
70 pub fn fields(&self) -> Projection::Fields
73 where
74 Name: Tag,
75 Projection: DerivedProjection<Name>,
76 {
77 Projection::fields()
78 }
79}
80
81impl<'a, V: SQLParam, Name, Projection, Query: Clone> Clone
82 for Derived<'a, V, Name, Projection, Query>
83{
84 fn clone(&self) -> Self {
85 Self {
86 query: self.query.clone(),
87 marker: PhantomData,
88 }
89 }
90}
91
92impl<'a, V, Name, Projection, Query> ToSQL<'a, V> for Derived<'a, V, Name, Projection, Query>
93where
94 V: SQLParam,
95 Name: Tag,
96 Query: ToSQL<'a, V>,
97{
98 fn to_sql(&self) -> SQL<'a, V> {
99 self.query
100 .to_sql()
101 .parens()
102 .push(Token::AS)
103 .append(SQL::table(crate::TableRef::sql(Name::NAME, &[])))
104 }
105
106 fn into_sql(self) -> SQL<'a, V> {
107 self.query
108 .into_sql()
109 .parens()
110 .push(Token::AS)
111 .append(SQL::table(crate::TableRef::sql(Name::NAME, &[])))
112 }
113}
114
115impl<V: SQLParam, Name, Projection, Query: crate::expr::ExprSources> crate::scope::ScopeEntry
117 for Derived<'_, V, Name, Projection, Query>
118{
119 type Key = crate::scope::AliasKey<Name>;
120 type Nullable = crate::expr::NonNull;
121 type Sources = Query::Sources;
122}
123
124impl<'a, V, Name, Projection, Query> HasSelectModel for Derived<'a, V, Name, Projection, Query>
125where
126 V: SQLParam,
127 Name: Tag,
128 Projection: DerivedProjection<Name>,
129{
130 type SelectModel = Projection::Row;
131
132 const COLUMN_COUNT: usize = Projection::COLUMN_COUNT;
133}
134
135#[doc(hidden)]
137pub trait DerivedProjection<Name: Tag>: private::Projection {
138 type Fields;
140 type Row;
142
143 const COLUMN_COUNT: usize;
145
146 fn validate() {}
152
153 fn fields() -> Self::Fields;
155}
156
157#[doc(hidden)]
163pub trait DerivedSelection<'a, V: SQLParam, Schema, Table>:
164 private::Selection<'a, V, Schema, Table>
165{
166 type Projection;
167}
168
169impl<'a, V, Schema, Table, Used> DerivedSelection<'a, V, Schema, Table>
170 for Scoped<SelectStar, Cons<Table, Nil>, Used>
171where
172 V: SQLParam + 'a,
173 Schema: SQLSchemaType,
174 Table: SQLTable<'a, Schema, V>,
175{
176 type Projection = TableProjection<'a, V, Schema, Table>;
177}
178
179impl<'a, V, Schema, Table, Used> private::Selection<'a, V, Schema, Table>
180 for Scoped<SelectStar, Cons<Table, Nil>, Used>
181where
182 V: SQLParam + 'a,
183 Schema: SQLSchemaType,
184 Table: SQLTable<'a, Schema, V>,
185{
186}
187
188impl<'a, V, Schema, Name, Projection, Query, Used>
189 DerivedSelection<'a, V, Schema, Derived<'a, V, Name, Projection, Query>>
190 for Scoped<SelectStar, Cons<Derived<'a, V, Name, Projection, Query>, Nil>, Used>
191where
192 V: SQLParam,
193 Name: Tag,
194 Projection: DerivedProjection<Name>,
195{
196 type Projection = Projection;
197}
198
199impl<'a, V, Schema, Name, Projection, Query, Used>
200 private::Selection<'a, V, Schema, Derived<'a, V, Name, Projection, Query>>
201 for Scoped<SelectStar, Cons<Derived<'a, V, Name, Projection, Query>, Nil>, Used>
202where
203 V: SQLParam,
204 Name: Tag,
205 Projection: DerivedProjection<Name>,
206{
207}
208
209impl<'a, V, Schema, Table, Columns, Scope, Used> DerivedSelection<'a, V, Schema, Table>
210 for Scoped<SelectCols<Columns>, Scope, Used>
211where
212 V: SQLParam,
213{
214 type Projection = Self;
215}
216
217impl<'a, V, Schema, Table, Columns, Scope, Used> private::Selection<'a, V, Schema, Table>
218 for Scoped<SelectCols<Columns>, Scope, Used>
219where
220 V: SQLParam,
221{
222}
223
224impl<Name, Marker, Scope, Used> DerivedProjection<Name> for Scoped<Marker, Scope, Used>
225where
226 Name: Tag,
227 Marker: DerivedProjection<Name>,
228{
229 type Fields = Marker::Fields;
230 type Row = Marker::Row;
231
232 const COLUMN_COUNT: usize = Marker::COLUMN_COUNT;
233
234 fn validate() {
235 Marker::validate();
236 }
237
238 fn fields() -> Self::Fields {
239 Marker::fields()
240 }
241}
242
243impl<Marker, Scope, Used> private::Projection for Scoped<Marker, Scope, Used> where
244 Marker: private::Projection
245{
246}
247
248#[doc(hidden)]
251pub struct TableProjection<'a, V: SQLParam, Schema, Table>(PhantomData<(&'a (), V, Schema, Table)>);
252
253impl<V: SQLParam, Schema, Table> private::Projection for TableProjection<'_, V, Schema, Table> {}
254
255impl<'a, V, Schema, Name, Table> DerivedProjection<Name> for TableProjection<'a, V, Schema, Table>
256where
257 V: SQLParam + 'a,
258 Schema: SQLSchemaType,
259 Name: Tag + 'static,
260 Table: SQLTable<'a, Schema, V> + HasSelectModel,
261 Table::Aliased<Name>: HasSelectModel<SelectModel = Table::SelectModel>,
262{
263 type Fields = Table::Aliased<Name>;
264 type Row = Table::SelectModel;
265
266 const COLUMN_COUNT: usize = Table::COLUMN_COUNT;
267
268 fn fields() -> Self::Fields {
269 Table::alias::<Name>()
270 }
271}
272
273pub struct DerivedField<Name, Output>(PhantomData<(Name, Output)>);
279
280impl<Name, Output> Copy for DerivedField<Name, Output> {}
281
282impl<Name, Output> Clone for DerivedField<Name, Output> {
283 fn clone(&self) -> Self {
284 *self
285 }
286}
287
288impl<Name, Output> Default for DerivedField<Name, Output> {
289 fn default() -> Self {
290 Self(PhantomData)
291 }
292}
293
294impl<'a, V, Name, Output> ToSQL<'a, V> for DerivedField<Name, Output>
295where
296 V: SQLParam,
297 Name: Tag,
298 Output: ProjectionOutput,
299{
300 fn to_sql(&self) -> SQL<'a, V> {
301 SQL::ident(Name::NAME)
302 .push(Token::DOT)
303 .append(SQL::ident(Output::output_name()))
304 }
305}
306
307impl<'a, V, Name, Output> Expr<'a, V> for DerivedField<Name, Output>
308where
309 V: SQLParam + 'a,
310 Name: Tag,
311 Output: ProjectionOutput + Expr<'a, V>,
312{
313 type SQLType = Output::SQLType;
314 type Nullable = Output::Nullable;
315 type Aggregate = Scalar;
316}
317
318impl<Name, Output> ExprValueType for DerivedField<Name, Output>
319where
320 Name: Tag,
321 Output: ProjectionOutput + ExprValueType,
322{
323 type ValueType = Output::ValueType;
324}
325
326impl<Name, Output> IntoSelectTarget for DerivedField<Name, Output>
327where
328 Name: Tag,
329 Output: ProjectionOutput + ExprValueType,
330{
331 type Marker = SelectCols<(Self,)>;
332}
333
334impl<Name, Output> HasAggStatus for DerivedField<Name, Output>
335where
336 Name: Tag,
337 Output: ProjectionOutput,
338{
339 type Status = AllScalar;
340}
341
342impl<Name, Output> GroupByIdentity for DerivedField<Name, Output>
343where
344 Name: Tag,
345 Output: ProjectionOutput,
346{
347 type Identity = Self;
348}
349
350impl<'a, V, Name, Output, Projection, Query>
351 crate::traits::ColumnOf<Derived<'a, V, Name, Projection, Query>> for DerivedField<Name, Output>
352where
353 V: SQLParam,
354{
355}
356
357impl<Name, Output> crate::expr::ExprSources for DerivedField<Name, Output> {
359 type Sources = crate::scope::Src<crate::scope::AliasKey<Name>>;
360}
361
362impl<'a, V, Name, Output> IntoGroupBy<'a, V> for DerivedField<Name, Output>
363where
364 V: SQLParam + 'a,
365 Name: Tag,
366 Output: ProjectionOutput,
367{
368 type Columns = Cons<Self, Nil>;
369}
370
371#[doc(hidden)]
373pub trait ProjectionOutput: private::Output {
374 fn output_name() -> &'static str;
376}
377
378impl<Column> private::Output for Column where Column: SQLColumnInfo + Default {}
379
380impl<Column> ProjectionOutput for Column
381where
382 Column: SQLColumnInfo + Default,
383{
384 fn output_name() -> &'static str {
385 Column::default().name()
386 }
387}
388
389impl<E, Name> ProjectionOutput for crate::expr::NamedExpr<E, Name>
390where
391 Name: Tag,
392{
393 fn output_name() -> &'static str {
394 Name::NAME
395 }
396}
397
398impl<E, Name> private::Output for crate::expr::NamedExpr<E, Name> where Name: Tag {}
399
400impl<Name, Output> ProjectionOutput for DerivedField<Name, Output>
401where
402 Name: Tag,
403 Output: ProjectionOutput,
404{
405 fn output_name() -> &'static str {
406 Output::output_name()
407 }
408}
409
410impl<Name, Output> private::Output for DerivedField<Name, Output>
411where
412 Name: Tag,
413 Output: ProjectionOutput,
414{
415}
416
417macro_rules! impl_derived_projection_tuple {
418 ($($output:ident),+; $($_index:tt),+) => {
419 impl<Name, $($output),+> DerivedProjection<Name> for SelectCols<($($output,)+)>
420 where
421 Name: Tag,
422 $($output: ProjectionOutput + ExprValueType,)+
423 {
424 type Fields = ($(DerivedField<Name, $output>,)+);
425 type Row = ($(<$output as ExprValueType>::ValueType,)+);
426
427 const COLUMN_COUNT: usize = impl_derived_projection_tuple!(@count $($output),+);
428
429 fn validate() {
430 let names = [$(<$output as ProjectionOutput>::output_name(),)+];
431 let mut left = 0;
432 while left < names.len() {
433 let mut right = left + 1;
434 while right < names.len() {
435 assert!(
436 names[left] != names[right],
437 "derived projection contains duplicate output name `{}`; name one expression with `.named::<Tag>()`",
438 names[left],
439 );
440 right += 1;
441 }
442 left += 1;
443 }
444 }
445
446 fn fields() -> Self::Fields {
447 ($(DerivedField::<Name, $output>::default(),)+)
448 }
449 }
450
451 impl<$($output),+> private::Projection for SelectCols<($($output,)+)>
452 where
453 $($output: ProjectionOutput + ExprValueType,)+
454 {
455 }
456 };
457 (@count $head:ident $(,$tail:ident)*) => {
458 1usize $(+ { let _ = stringify!($tail); 1usize })*
459 };
460}
461
462with_col_sizes_8!(impl_derived_projection_tuple);
463
464#[cfg(any(
465 feature = "col16",
466 feature = "col32",
467 feature = "col64",
468 feature = "col128",
469 feature = "col200"
470))]
471with_col_sizes_16!(impl_derived_projection_tuple);
472
473#[cfg(any(
474 feature = "col32",
475 feature = "col64",
476 feature = "col128",
477 feature = "col200"
478))]
479with_col_sizes_32!(impl_derived_projection_tuple);
480
481#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
482with_col_sizes_64!(impl_derived_projection_tuple);
483
484#[cfg(any(feature = "col128", feature = "col200"))]
485with_col_sizes_128!(impl_derived_projection_tuple);
486
487#[cfg(feature = "col200")]
488with_col_sizes_200!(impl_derived_projection_tuple);
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493 use crate::{Dialect, SQLiteDialect};
494
495 #[derive(Clone, Debug)]
496 struct TestParam;
497
498 impl SQLParam for TestParam {
499 const DIALECT: Dialect = Dialect::SQLite;
500 type DialectMarker = SQLiteDialect;
501 }
502
503 struct Alias;
504
505 impl Tag for Alias {
506 const NAME: &'static str = "alias";
507 }
508
509 struct First;
510 struct Second;
511
512 impl private::Output for First {}
513 impl private::Output for Second {}
514
515 impl ProjectionOutput for First {
516 fn output_name() -> &'static str {
517 "duplicate"
518 }
519 }
520
521 impl ProjectionOutput for Second {
522 fn output_name() -> &'static str {
523 "duplicate"
524 }
525 }
526
527 impl ExprValueType for First {
528 type ValueType = i32;
529 }
530
531 impl ExprValueType for Second {
532 type ValueType = i32;
533 }
534
535 #[test]
536 #[should_panic(expected = "duplicate output name")]
537 fn duplicate_projection_names_are_rejected() {
538 let _: Derived<'_, TestParam, Alias, SelectCols<(First, Second)>, ()> =
539 unsafe { Derived::new_unchecked(()) };
542 }
543}