1use alloc::vec::Vec;
18
19use crate::encoding::Value;
20use crate::schema::SchemaWithPK;
21
22pub type ChangesetUpdatePair<S, B> = (Option<Value<S, B>>, Option<Value<S, B>>);
26
27pub type PatchsetUpdateEntry<S, B> = ((), Option<Value<S, B>>);
31
32#[derive(Debug)]
34pub enum ChangesetOp<'a, T, S, B> {
35 Insert {
37 table: &'a T,
39 values: &'a [Value<S, B>],
41 indirect: bool,
43 },
44 Update {
47 table: &'a T,
49 values: &'a [ChangesetUpdatePair<S, B>],
51 indirect: bool,
53 },
54 Delete {
56 table: &'a T,
58 old_values: &'a [Value<S, B>],
60 indirect: bool,
62 },
63}
64
65impl<'a, T, S, B> ChangesetOp<'a, T, S, B> {
66 #[must_use]
68 pub fn table(&self) -> &'a T {
69 match self {
70 Self::Insert { table, .. }
71 | Self::Update { table, .. }
72 | Self::Delete { table, .. } => table,
73 }
74 }
75
76 #[must_use]
78 pub fn indirect(&self) -> bool {
79 match self {
80 Self::Insert { indirect, .. }
81 | Self::Update { indirect, .. }
82 | Self::Delete { indirect, .. } => *indirect,
83 }
84 }
85}
86
87impl<T: SchemaWithPK, S: Clone, B: Clone> ChangesetOp<'_, T, S, B> {
88 #[must_use]
97 pub fn primary_key(&self) -> Vec<Value<S, B>> {
98 match *self {
99 Self::Insert { table, values, .. } => table.extract_pk(&values),
100 Self::Delete {
101 table, old_values, ..
102 } => table.extract_pk(&old_values),
103 Self::Update { table, values, .. } => table
104 .primary_key_columns()
105 .into_iter()
106 .map(|col_idx| {
107 let (old, new) = &values[col_idx];
108 old.clone().or_else(|| new.clone()).unwrap_or(Value::Null)
109 })
110 .collect(),
111 }
112 }
113}
114
115#[derive(Debug)]
117pub enum PatchsetOp<'a, T, S, B> {
118 Insert {
120 table: &'a T,
122 values: &'a [Value<S, B>],
124 indirect: bool,
126 },
127 Update {
132 table: &'a T,
134 pk: &'a [Value<S, B>],
136 entries: &'a [PatchsetUpdateEntry<S, B>],
138 indirect: bool,
140 },
141 Delete {
144 table: &'a T,
146 pk: &'a [Value<S, B>],
148 indirect: bool,
150 },
151}
152
153impl<'a, T, S, B> PatchsetOp<'a, T, S, B> {
154 #[must_use]
156 pub fn table(&self) -> &'a T {
157 match self {
158 Self::Insert { table, .. }
159 | Self::Update { table, .. }
160 | Self::Delete { table, .. } => table,
161 }
162 }
163
164 #[must_use]
166 pub fn indirect(&self) -> bool {
167 match self {
168 Self::Insert { indirect, .. }
169 | Self::Update { indirect, .. }
170 | Self::Delete { indirect, .. } => *indirect,
171 }
172 }
173
174 #[must_use]
177 pub fn update_new_values(&self) -> Option<Vec<&'a Option<Value<S, B>>>> {
178 match self {
179 Self::Update { entries, .. } => Some(entries.iter().map(|((), v)| v).collect()),
180 _ => None,
181 }
182 }
183}
184
185impl<T: SchemaWithPK, S: Clone, B: Clone> PatchsetOp<'_, T, S, B> {
186 #[must_use]
192 pub fn primary_key(&self) -> Vec<Value<S, B>> {
193 match *self {
194 Self::Insert { table, values, .. } => table.extract_pk(&values),
195 Self::Update { pk, .. } | Self::Delete { pk, .. } => pk.to_vec(),
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use crate::{ChangeSet, DiffOps, Insert, PatchSet, SimpleTable};
204 use alloc::string::String;
205 use alloc::vec;
206
207 type Pair = ChangesetUpdatePair<String, Vec<u8>>;
208 type Entry = PatchsetUpdateEntry<String, Vec<u8>>;
209 type Val = Value<String, Vec<u8>>;
210
211 #[test]
212 fn changeset_primary_key_single_key() {
213 let schema = SimpleTable::new("kv", &["id", "val"], &[0]);
214
215 let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
216 let insert = ChangesetOp::Insert {
217 table: &schema,
218 values: &insert_values,
219 indirect: false,
220 };
221 assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
222
223 let update_values: Vec<Pair> = vec![
226 (Some(Value::Integer(2)), None),
227 (
228 Some(Value::Text("before".into())),
229 Some(Value::Text("after".into())),
230 ),
231 ];
232 let update = ChangesetOp::Update {
233 table: &schema,
234 values: &update_values,
235 indirect: false,
236 };
237 assert_eq!(update.primary_key(), vec![Value::Integer(2)]);
238
239 let delete_values: Vec<Val> = vec![Value::Integer(3), Value::Text("gone".into())];
240 let delete = ChangesetOp::Delete {
241 table: &schema,
242 old_values: &delete_values,
243 indirect: false,
244 };
245 assert_eq!(delete.primary_key(), vec![Value::Integer(3)]);
246 }
247
248 #[test]
249 fn changeset_primary_key_composite_reordered_key() {
250 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
252 let expected: Vec<Val> = vec![Value::Integer(20), Value::Integer(10)];
253
254 let insert_values: Vec<Val> = vec![
255 Value::Integer(10),
256 Value::Integer(20),
257 Value::Text("z".into()),
258 ];
259 let insert = ChangesetOp::Insert {
260 table: &schema,
261 values: &insert_values,
262 indirect: false,
263 };
264 assert_eq!(insert.primary_key(), expected);
265
266 let update_values: Vec<Pair> = vec![
269 (Some(Value::Integer(10)), None),
270 (Some(Value::Integer(20)), None),
271 (
272 Some(Value::Text("z".into())),
273 Some(Value::Text("z2".into())),
274 ),
275 ];
276 let update = ChangesetOp::Update {
277 table: &schema,
278 values: &update_values,
279 indirect: false,
280 };
281 assert_eq!(update.primary_key(), expected);
282
283 let delete_values: Vec<Val> = vec![
284 Value::Integer(10),
285 Value::Integer(20),
286 Value::Text("z".into()),
287 ];
288 let delete = ChangesetOp::Delete {
289 table: &schema,
290 old_values: &delete_values,
291 indirect: false,
292 };
293 assert_eq!(delete.primary_key(), expected);
294 }
295
296 #[test]
297 fn patchset_primary_key_variants() {
298 let kv = SimpleTable::new("kv", &["id", "val"], &[0]);
299
300 let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
301 let insert = PatchsetOp::Insert {
302 table: &kv,
303 values: &insert_values,
304 indirect: false,
305 };
306 assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
307
308 let update_pk: Vec<Val> = vec![Value::Integer(9)];
309 let entries: Vec<Entry> = vec![((), None), ((), Some(Value::Text("z".into())))];
310 let update = PatchsetOp::Update {
311 table: &kv,
312 pk: &update_pk,
313 entries: &entries,
314 indirect: false,
315 };
316 assert_eq!(update.primary_key(), vec![Value::Integer(9)]);
317
318 let delete_pk: Vec<Val> = vec![Value::Integer(7)];
319 let delete = PatchsetOp::Delete {
320 table: &kv,
321 pk: &delete_pk,
322 indirect: false,
323 };
324 assert_eq!(delete.primary_key(), vec![Value::Integer(7)]);
325
326 let abc = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
328 let abc_values: Vec<Val> = vec![
329 Value::Integer(10),
330 Value::Integer(20),
331 Value::Text("z".into()),
332 ];
333 let abc_insert = PatchsetOp::Insert {
334 table: &abc,
335 values: &abc_values,
336 indirect: false,
337 };
338 assert_eq!(
339 abc_insert.primary_key(),
340 vec![Value::Integer(20), Value::Integer(10)]
341 );
342 }
343
344 #[test]
345 fn changeset_primary_key_through_iter_composite() {
346 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
349 let cs: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(
350 Insert::from(schema)
351 .set(0, 10i64)
352 .unwrap()
353 .set(1, 20i64)
354 .unwrap()
355 .set(2, "z")
356 .unwrap(),
357 );
358 let ops: Vec<_> = cs.iter().collect();
359 assert_eq!(ops.len(), 1);
360 assert_eq!(
361 ops[0].primary_key(),
362 vec![Value::Integer(20), Value::Integer(10)]
363 );
364 }
365
366 #[test]
367 fn patchset_primary_key_through_iter_composite_delete() {
368 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
371 let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
372 ps.add_table(&schema);
373 ps.digest_sql("DELETE FROM abc WHERE a = 10 AND b = 20")
374 .unwrap();
375 let ops: Vec<_> = ps.iter().collect();
376 assert_eq!(ops.len(), 1);
377 assert_eq!(
378 ops[0].primary_key(),
379 vec![Value::Integer(20), Value::Integer(10)]
380 );
381 }
382}