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