1use crate::values::SQLiteValue;
2use drizzle_core::{SQL, SQLModel, SQLTable, ToSQL};
3use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use super::ExecutableState;
8
9#[derive(Debug, Clone, Copy, Default)]
15pub struct InsertInitial;
16
17#[derive(Debug, Clone, Copy, Default)]
19pub struct InsertValuesSet;
20
21#[derive(Debug, Clone, Copy, Default)]
23pub struct InsertReturningSet;
24
25#[derive(Debug, Clone, Copy, Default)]
27pub struct InsertOnConflictSet;
28
29impl InsertInitial {
31 #[inline]
32 pub const fn new() -> Self {
33 Self
34 }
35}
36impl InsertValuesSet {
37 #[inline]
38 pub const fn new() -> Self {
39 Self
40 }
41}
42impl InsertReturningSet {
43 #[inline]
44 pub const fn new() -> Self {
45 Self
46 }
47}
48impl InsertOnConflictSet {
49 #[inline]
50 pub const fn new() -> Self {
51 Self
52 }
53}
54
55#[derive(Debug, Clone)]
57pub enum Conflict<
58 'a,
59 T: IntoIterator<Item: ToSQL<'a, SQLiteValue<'a>>> = Vec<SQL<'a, SQLiteValue<'a>>>,
60> {
61 Ignore {
63 target: Option<T>,
65 },
66 Update {
68 target: T,
70 set: Box<SQL<'a, SQLiteValue<'a>>>,
72 target_where: Box<Option<SQL<'a, SQLiteValue<'a>>>>,
75 set_where: Box<Option<SQL<'a, SQLiteValue<'a>>>>,
78 },
79}
80
81impl<'a> Default for Conflict<'a> {
82 fn default() -> Self {
83 Self::Ignore { target: None }
84 }
85}
86
87impl<'a, T> Conflict<'a, T>
88where
89 T: IntoIterator<Item: ToSQL<'a, SQLiteValue<'a>>>,
90{
91 pub fn update(
92 target: T,
93 set: SQL<'a, SQLiteValue<'a>>,
94 target_where: Option<SQL<'a, SQLiteValue<'a>>>,
95 set_where: Option<SQL<'a, SQLiteValue<'a>>>,
96 ) -> Self {
97 Conflict::Update {
98 target,
99 set: Box::new(set),
100 target_where: Box::new(target_where),
101 set_where: Box::new(set_where),
102 }
103 }
104}
105
106impl ExecutableState for InsertValuesSet {}
108impl ExecutableState for InsertReturningSet {}
109impl ExecutableState for InsertOnConflictSet {}
110
111pub type InsertBuilder<'a, Schema, State, Table> = super::QueryBuilder<'a, Schema, State, Table>;
117
118impl<'a, Schema, Table> InsertBuilder<'a, Schema, InsertInitial, Table>
123where
124 Table: SQLTable<'a, SQLiteValue<'a>>,
125{
126 #[inline]
128 pub fn values<I, T>(self, values: I) -> InsertBuilder<'a, Schema, InsertValuesSet, Table>
129 where
130 I: IntoIterator<Item = Table::Insert<T>>,
131 Table::Insert<T>: SQLModel<'a, SQLiteValue<'a>>,
132 {
133 let sql = crate::helpers::values::<'a, Table, T>(values);
134 InsertBuilder {
135 sql: self.sql.append(sql),
136 schema: PhantomData,
137 state: PhantomData,
138 table: PhantomData,
139 }
140 }
141}
142
143impl<'a, S, T> InsertBuilder<'a, S, InsertValuesSet, T> {
148 pub fn on_conflict<TI>(
150 self,
151 conflict: Conflict<'a, TI>,
152 ) -> InsertBuilder<'a, S, InsertOnConflictSet, T>
153 where
154 TI: IntoIterator,
155 TI::Item: ToSQL<'a, SQLiteValue<'a>>,
156 {
157 let conflict_sql = match conflict {
158 Conflict::Ignore { target } => {
159 if let Some(target_iter) = target {
160 let cols = SQL::join(target_iter.into_iter().map(|item| item.to_sql()), ", ");
161 SQL::raw("ON CONFLICT (")
162 .append(cols)
163 .append_raw(") DO NOTHING")
164 } else {
165 SQL::raw("ON CONFLICT DO NOTHING")
166 }
167 }
168 Conflict::Update {
169 target,
170 set,
171 target_where,
172 set_where,
173 } => {
174 let target_cols = SQL::join(target.into_iter().map(|item| item.to_sql()), ", ");
175 let mut sql = SQL::raw("ON CONFLICT (")
176 .append(target_cols)
177 .append_raw(")");
178
179 if let Some(target_where) = *target_where {
181 sql = sql.append_raw(" WHERE ").append(target_where);
182 }
183
184 sql = sql.append_raw(" DO UPDATE SET ").append(*set);
185
186 if let Some(set_where) = *set_where {
188 sql = sql.append_raw(" WHERE ").append(set_where);
189 }
190
191 sql
192 }
193 };
194
195 InsertBuilder {
196 sql: self.sql.append(conflict_sql),
197 schema: PhantomData,
198 state: PhantomData,
199 table: PhantomData,
200 }
201 }
202
203 #[inline]
205 pub fn returning(
206 self,
207 columns: impl ToSQL<'a, SQLiteValue<'a>>,
208 ) -> InsertBuilder<'a, S, InsertReturningSet, T> {
209 let returning_sql = crate::helpers::returning(columns);
210 InsertBuilder {
211 sql: self.sql.append(returning_sql),
212 schema: PhantomData,
213 state: PhantomData,
214 table: PhantomData,
215 }
216 }
217}
218
219impl<'a, S, T> InsertBuilder<'a, S, InsertOnConflictSet, T> {
224 #[inline]
226 pub fn returning(
227 self,
228 columns: impl ToSQL<'a, SQLiteValue<'a>>,
229 ) -> InsertBuilder<'a, S, InsertReturningSet, T> {
230 let returning_sql = crate::helpers::returning(columns);
231 InsertBuilder {
232 sql: self.sql.append(returning_sql),
233 schema: PhantomData,
234 state: PhantomData,
235 table: PhantomData,
236 }
237 }
238}