1use 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
22pub 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 #[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 pub const fn query(&self) -> &Query {
53 &self.query
54 }
55
56 pub fn into_query(self) -> Query {
58 self.query
59 }
60
61 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#[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#[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#[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
242pub 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#[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 unsafe { Derived::new_unchecked(()) };
512 }
513}