1use core::marker::PhantomData;
2
3use crate::{
4 Cons, HasSelectModel, IntoSelectTarget, Nil, SQL, SQLChunk, SQLColumnInfo, SQLParam, Scoped,
5 SelectAs, SelectCols, SelectRequiredTables, SelectStar, SelectedExpressionList, Token, TypeEq,
6 TypeSet,
7 expr::{Expr, NullAnd},
8 row::{ProjectionsInScope, ScopeSatisfies},
9 types::Assignable,
10};
11
12#[derive(Debug, Clone, Copy, Default)]
14pub struct InsertColumnsSet<Columns>(PhantomData<Columns>);
15
16#[doc(hidden)]
18pub trait InsertColumn<Table> {
19 type Column;
20}
21
22impl<T, Table> InsertColumn<Table> for &T
23where
24 T: InsertColumn<Table>,
25{
26 type Column = T::Column;
27}
28
29#[doc(hidden)]
31pub trait InsertSelectTable {
32 type Columns: TypeSet;
33 type RequiredColumns: TypeSet;
34
35 const INSERT_COLUMNS: &'static [&'static str];
36
37 fn insert_columns_sql<'a, V: SQLParam>() -> SQL<'a, V> {
38 let mut sql = SQL::empty();
39 for (index, column) in Self::INSERT_COLUMNS.iter().enumerate() {
40 if index > 0 {
41 sql.push_mut(Token::COMMA);
42 }
43 sql.append_mut(SQL::ident(*column));
44 }
45 sql.parens()
46 }
47}
48
49impl<T> InsertSelectTable for &T
50where
51 T: InsertSelectTable,
52{
53 type Columns = T::Columns;
54 type RequiredColumns = T::RequiredColumns;
55
56 const INSERT_COLUMNS: &'static [&'static str] = T::INSERT_COLUMNS;
57}
58
59#[doc(hidden)]
61#[diagnostic::on_unimplemented(
62 message = "select-all cannot feed this INSERT target",
63 label = "select the insertable source columns explicitly"
64)]
65pub trait InsertSelectAllColumns: InsertSelectTable {}
66
67impl<T> InsertSelectAllColumns for &T where T: InsertSelectAllColumns {}
68
69#[doc(hidden)]
71pub trait InsertSelectColumns<'a, V: SQLParam, Source> {}
72
73impl<'a, V: SQLParam> InsertSelectColumns<'a, V, Nil> for Nil {}
74
75impl<'a, V, TargetExpr, TargetTail, SourceExpr, SourceTail>
76 InsertSelectColumns<'a, V, Cons<SourceExpr, SourceTail>> for Cons<TargetExpr, TargetTail>
77where
78 V: SQLParam,
79 TargetExpr: Expr<'a, V>,
80 SourceExpr: Expr<'a, V>,
81 TargetExpr::SQLType: Assignable<SourceExpr::SQLType>,
82 TargetExpr::Nullable: NullAnd<SourceExpr::Nullable, Output = SourceExpr::Nullable>,
83 TargetTail: InsertSelectColumns<'a, V, SourceTail>,
84{
85}
86
87#[doc(hidden)]
89pub trait InsertTargetColumns<'a, V: SQLParam, Table> {
90 type Columns: TypeSet;
91
92 fn into_target_columns_sql(self) -> SQL<'a, V>;
93}
94
95#[doc(hidden)]
96pub trait InsertTargetMarker<Table> {
97 type Columns: TypeSet;
98}
99
100impl<Table, Selected> InsertTargetMarker<Table> for SelectCols<Selected>
101where
102 Selected: SelectedExpressionList,
103 Selected::Expressions: InsertTargetColumnList<Table>,
104{
105 type Columns = <Selected::Expressions as InsertTargetColumnList<Table>>::Columns;
106}
107
108#[doc(hidden)]
109pub trait InsertTargetColumnList<Table> {
110 type Columns: TypeSet;
111
112 fn append_columns<'a, V: SQLParam>(sql: &mut SQL<'a, V>);
113}
114
115impl<Table> InsertTargetColumnList<Table> for Nil {
116 type Columns = Nil;
117
118 fn append_columns<'a, V: SQLParam>(_sql: &mut SQL<'a, V>) {}
119}
120
121impl<Table, Head, Tail> InsertTargetColumnList<Table> for Cons<Head, Tail>
122where
123 Head: InsertColumn<Table>,
124 Head::Column: SQLColumnInfo + Default,
125 Tail: InsertTargetColumnList<Table>,
126{
127 type Columns = Cons<Head::Column, Tail::Columns>;
128
129 fn append_columns<'a, V: SQLParam>(sql: &mut SQL<'a, V>) {
130 let name = Head::Column::default().name();
131 assert!(
132 !sql.chunks.iter().any(
133 |chunk| matches!(chunk, SQLChunk::Ident(identifier) if identifier.as_ref() == name),
134 ),
135 "an INSERT target column cannot appear more than once",
136 );
137 if !sql.chunks.is_empty() {
138 sql.push_mut(Token::COMMA);
139 }
140 sql.append_mut(SQL::ident(name));
141 Tail::append_columns(sql);
142 }
143}
144
145impl<'a, V, Table, Columns> InsertTargetColumns<'a, V, Table> for Columns
146where
147 V: SQLParam,
148 Columns: IntoSelectTarget,
149 Columns::Marker: InsertTargetMarker<Table>,
150 <Columns::Marker as InsertTargetMarker<Table>>::Columns: InsertTargetColumnList<Table>,
151{
152 type Columns = <Columns::Marker as InsertTargetMarker<Table>>::Columns;
153
154 fn into_target_columns_sql(self) -> SQL<'a, V> {
155 let mut columns = SQL::empty();
156 <<Columns::Marker as InsertTargetMarker<Table>>::Columns as InsertTargetColumnList<
157 Table,
158 >>::append_columns(&mut columns);
159 assert!(
160 !columns.chunks.is_empty(),
161 "an INSERT target list must contain at least one column",
162 );
163 columns.parens()
164 }
165}
166
167#[doc(hidden)]
169#[diagnostic::on_unimplemented(
170 message = "the INSERT target list omits one or more required columns",
171 label = "add every non-null column without a database default to `.columns(...)`"
172)]
173pub trait IncludesRequired<Required, Proof> {}
174
175impl<Targets, Required, Proof> IncludesRequired<Required, Proof> for Targets where
176 Targets: ScopeSatisfies<Required, Proof>
177{
178}
179
180#[doc(hidden)]
182pub trait PartialInsertSelectCompatible<'a, V: SQLParam, Targets> {}
183
184impl<'a, V, M, Scope, Targets> PartialInsertSelectCompatible<'a, V, Targets> for Scoped<M, Scope>
185where
186 V: SQLParam,
187 M: PartialInsertSelectCompatible<'a, V, Targets>,
188{
189}
190
191impl<'a, V, Targets, Selected> PartialInsertSelectCompatible<'a, V, Targets>
192 for SelectCols<Selected>
193where
194 V: SQLParam,
195 Selected: SelectedExpressionList,
196 Targets: InsertSelectColumns<'a, V, Selected::Expressions>,
197{
198}
199
200#[doc(hidden)]
202pub trait InsertSourceInScope<Proof> {}
203
204impl<Scope> InsertSourceInScope<()> for Scoped<SelectStar, Scope> {}
205
206impl<Row, Scope, Proof> InsertSourceInScope<Proof> for Scoped<SelectAs<Row>, Scope>
207where
208 Row: SelectRequiredTables,
209 Scope: ScopeSatisfies<Row::RequiredTables, Proof>,
210{
211}
212
213impl<Selected, Scope, Proof> InsertSourceInScope<Proof> for Scoped<SelectCols<Selected>, Scope>
214where
215 Selected: SelectedExpressionList,
216 Selected::Expressions: ProjectionsInScope<Scope, Proof>,
217{
218}
219
220#[doc(hidden)]
222pub trait InsertSelectCompatible<'a, V: SQLParam, Target, Row> {}
223
224impl<'a, V, M, Scope, Target, Row> InsertSelectCompatible<'a, V, Target, Row> for Scoped<M, Scope>
225where
226 V: SQLParam,
227 M: InsertSelectCompatible<'a, V, Target, Row>,
228{
229}
230
231impl<'a, V, Target, Row> InsertSelectCompatible<'a, V, Target, Row> for SelectStar
232where
233 V: SQLParam,
234 Target: HasSelectModel + InsertSelectAllColumns,
235 Row: TypeEq<Target::SelectModel>,
236{
237}
238
239impl<'a, V, Target, Row, Selected> InsertSelectCompatible<'a, V, Target, Row>
240 for SelectCols<Selected>
241where
242 V: SQLParam,
243 Target: InsertSelectTable,
244 Selected: SelectedExpressionList,
245 Target::Columns: InsertSelectColumns<'a, V, Selected::Expressions>,
246{
247}
248
249impl<'a, V, Target, Row, Selected> InsertSelectCompatible<'a, V, Target, Row> for SelectAs<Selected>
250where
251 V: SQLParam,
252 Target: HasSelectModel + InsertSelectAllColumns,
253 Row: TypeEq<Target::SelectModel>,
254{
255}