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