1use crate::entity::{IEntitySnapshot, IEntityType, IGetKeyValues};
8use crate::error::LrefResult;
9use crate::metadata::EntityTypeMeta;
10use crate::provider::{DbValue, IAsyncConnection, IDatabaseProvider};
11use std::collections::HashMap;
12
13pub struct ChangeExecutor;
15
16impl ChangeExecutor {
17 pub async fn execute_inserts<E, F>(
22 conn: &mut dyn IAsyncConnection,
23 provider: &dyn IDatabaseProvider,
24 entities: &[(&E, &EntityTypeMeta)],
25 mut on_key_backfill: F,
26 ) -> LrefResult<usize>
27 where
28 E: IEntityType + IEntitySnapshot + IGetKeyValues,
29 F: FnMut(usize, i64),
30 {
31 let gen = provider.sql_generator();
32 let mut inserted = 0;
33
34 for (idx, (entity, meta)) in entities.iter().enumerate() {
35 let snap = entity.snapshot();
36 let scalar_props: Vec<_> = meta.mapped_scalar_properties().collect();
37 if scalar_props.is_empty() {
38 continue;
39 }
40
41 let insert_cols: Vec<&str> = scalar_props
43 .iter()
44 .filter(|p| !p.is_auto_increment || !p.is_primary_key)
45 .map(|p| p.column_name.as_ref())
46 .collect();
47
48 let params: Vec<DbValue> = scalar_props
49 .iter()
50 .filter(|p| !p.is_auto_increment || !p.is_primary_key)
51 .map(|p| {
52 snap.get(p.field_name.as_ref())
53 .cloned()
54 .unwrap_or(DbValue::Null)
55 })
56 .collect();
57
58 if insert_cols.is_empty() {
59 continue;
60 }
61
62 let sql = gen.insert(meta.table_name.as_ref(), &insert_cols, true);
63 let rows = conn.execute(&sql, ¶ms).await?;
64
65 if rows > 0 {
67 on_key_backfill(idx, rows as i64);
68 inserted += 1;
69 }
70 }
71
72 Ok(inserted)
73 }
74
75 pub async fn execute_updates<E>(
77 conn: &mut dyn IAsyncConnection,
78 provider: &dyn IDatabaseProvider,
79 entities: &[(&E, &EntityTypeMeta)],
80 ) -> LrefResult<usize>
81 where
82 E: IEntityType + IEntitySnapshot + IGetKeyValues,
83 {
84 let gen = provider.sql_generator();
85 let mut updated = 0;
86
87 for (_entity, meta) in entities {
88 let snap = _entity.snapshot();
89 let keys = _entity.key_values();
90 let scalar_props: Vec<_> = meta.mapped_scalar_properties().collect();
91
92 let set_cols: Vec<&str> = scalar_props
94 .iter()
95 .filter(|p| !p.is_primary_key)
96 .map(|p| p.column_name.as_ref())
97 .collect();
98
99 if set_cols.is_empty() || keys.is_empty() {
100 continue;
101 }
102
103 let where_parts: Vec<String> = keys
105 .keys()
106 .enumerate()
107 .map(|(i, k)| {
108 format!(
109 "{} = {}",
110 gen.quote_identifier(k),
111 gen.parameter_placeholder(i + 1)
112 )
113 })
114 .collect();
115 let where_clause = where_parts.join(" AND ");
116
117 let sql = gen.update(meta.table_name.as_ref(), &set_cols, &where_clause);
118
119 let mut params: Vec<DbValue> = set_cols
121 .iter()
122 .map(|col| {
123 let prop = scalar_props.iter().find(|p| p.column_name.as_ref() == *col);
125 match prop {
126 Some(p) => snap
127 .get(p.field_name.as_ref())
128 .cloned()
129 .unwrap_or(DbValue::Null),
130 None => DbValue::Null,
131 }
132 })
133 .collect();
134
135 for (_k, v) in &keys {
136 params.push(v.clone());
137 }
138
139 let rows = conn.execute(&sql, ¶ms).await?;
140 if rows > 0 {
141 updated += 1;
142 }
143 }
144
145 Ok(updated)
146 }
147
148 pub async fn execute_deletes<E>(
150 conn: &mut dyn IAsyncConnection,
151 provider: &dyn IDatabaseProvider,
152 entities: &[(&E, &EntityTypeMeta)],
153 ) -> LrefResult<usize>
154 where
155 E: IEntityType + IGetKeyValues,
156 {
157 let gen = provider.sql_generator();
158 let mut deleted = 0;
159
160 for (_entity, meta) in entities {
161 let keys = _entity.key_values();
162 if keys.is_empty() {
163 continue;
164 }
165
166 let where_parts: Vec<String> = keys
167 .keys()
168 .enumerate()
169 .map(|(i, k)| {
170 format!(
171 "{} = {}",
172 gen.quote_identifier(k),
173 gen.parameter_placeholder(i + 1)
174 )
175 })
176 .collect();
177 let where_clause = where_parts.join(" AND ");
178
179 let sql = gen.delete(meta.table_name.as_ref(), &where_clause);
180
181 let params: Vec<DbValue> = keys.values().cloned().collect();
182 let rows = conn.execute(&sql, ¶ms).await?;
183 if rows > 0 {
184 deleted += 1;
185 }
186 }
187
188 Ok(deleted)
189 }
190}
191
192pub fn generate_insert_sql(
197 provider: &dyn IDatabaseProvider,
198 meta: &EntityTypeMeta,
199 _property_values: &HashMap<String, DbValue>,
200) -> String {
201 let gen = provider.sql_generator();
202 let scalar_props: Vec<_> = meta.mapped_scalar_properties().collect();
203 let columns: Vec<&str> = scalar_props
204 .iter()
205 .map(|p| p.column_name.as_ref())
206 .collect();
207 if columns.is_empty() {
208 return String::new();
209 }
210 gen.insert(meta.table_name.as_ref(), &columns, true)
211}
212
213pub fn generate_update_sql(
214 provider: &dyn IDatabaseProvider,
215 meta: &EntityTypeMeta,
216 property_values: &HashMap<String, DbValue>,
217 primary_key_values: &HashMap<String, DbValue>,
218) -> String {
219 let gen = provider.sql_generator();
220 let set_columns: Vec<&str> = property_values
221 .keys()
222 .filter(|k| !primary_key_values.contains_key(*k))
223 .map(|k| k.as_str())
224 .collect();
225 if set_columns.is_empty() || primary_key_values.is_empty() {
226 return String::new();
227 }
228 let where_parts: Vec<String> = primary_key_values
229 .keys()
230 .enumerate()
231 .map(|(i, k)| {
232 format!(
233 "{} = {}",
234 gen.quote_identifier(k),
235 gen.parameter_placeholder(i + 1)
236 )
237 })
238 .collect();
239 gen.update(
240 meta.table_name.as_ref(),
241 &set_columns,
242 &where_parts.join(" AND "),
243 )
244}
245
246pub fn generate_delete_sql(
247 provider: &dyn IDatabaseProvider,
248 meta: &EntityTypeMeta,
249 primary_key_values: &HashMap<String, DbValue>,
250) -> String {
251 let gen = provider.sql_generator();
252 if primary_key_values.is_empty() {
253 return String::new();
254 }
255 let where_parts: Vec<String> = primary_key_values
256 .keys()
257 .enumerate()
258 .map(|(i, k)| {
259 format!(
260 "{} = {}",
261 gen.quote_identifier(k),
262 gen.parameter_placeholder(i + 1)
263 )
264 })
265 .collect();
266 gen.delete(meta.table_name.as_ref(), &where_parts.join(" AND "))
267}
268
269pub fn collect_insert_params(
270 meta: &EntityTypeMeta,
271 property_values: &HashMap<String, DbValue>,
272) -> Vec<DbValue> {
273 meta.mapped_scalar_properties()
274 .map(|p| {
275 property_values
276 .get(p.field_name.as_ref())
277 .cloned()
278 .unwrap_or(DbValue::Null)
279 })
280 .collect()
281}
282
283pub fn collect_update_params(
284 property_values: &HashMap<String, DbValue>,
285 primary_key_values: &HashMap<String, DbValue>,
286 set_keys: &[String],
287) -> Vec<DbValue> {
288 let mut params: Vec<DbValue> = set_keys
289 .iter()
290 .filter(|k| !primary_key_values.contains_key(*k))
291 .map(|k| property_values.get(k).cloned().unwrap_or(DbValue::Null))
292 .collect();
293 for v in primary_key_values.values() {
294 params.push(v.clone());
295 }
296 params
297}
298
299pub fn collect_delete_params(primary_key_values: &HashMap<String, DbValue>) -> Vec<DbValue> {
300 primary_key_values.values().cloned().collect()
301}