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>>);
29
30pub trait ChangesetUpdatePairExt {
32 #[must_use]
41 fn is_changed(&self) -> bool;
42}
43
44impl<S, B> ChangesetUpdatePairExt for ChangesetUpdatePair<S, B>
45where
46 Value<S, B>: PartialEq,
47{
48 #[inline]
49 fn is_changed(&self) -> bool {
50 match self {
51 (_, None) => false,
52 (Some(old), Some(new)) => old != new,
53 (None, Some(_)) => true,
54 }
55 }
56}
57
58pub type PatchsetUpdateEntry<S, B> = ((), Option<Value<S, B>>);
62
63#[derive(Debug)]
65pub enum ChangesetOp<'a, T, S, B> {
66 Insert {
68 table: &'a T,
70 values: &'a [Value<S, B>],
72 indirect: bool,
74 },
75 Update {
78 table: &'a T,
80 values: &'a [ChangesetUpdatePair<S, B>],
82 indirect: bool,
84 },
85 Delete {
87 table: &'a T,
89 old_values: &'a [Value<S, B>],
91 indirect: bool,
93 },
94}
95
96impl<'a, T, S, B> ChangesetOp<'a, T, S, B> {
97 #[must_use]
99 pub fn table(&self) -> &'a T {
100 match self {
101 Self::Insert { table, .. }
102 | Self::Update { table, .. }
103 | Self::Delete { table, .. } => table,
104 }
105 }
106
107 #[must_use]
109 pub fn indirect(&self) -> bool {
110 match self {
111 Self::Insert { indirect, .. }
112 | Self::Update { indirect, .. }
113 | Self::Delete { indirect, .. } => *indirect,
114 }
115 }
116}
117
118impl<T, S, B> ChangesetOp<'_, T, S, B>
119where
120 Value<S, B>: PartialEq,
121{
122 pub fn changed_column_indices(&self) -> impl Iterator<Item = usize> {
125 let values: &[ChangesetUpdatePair<S, B>] = match self {
126 Self::Update { values, .. } => values,
127 Self::Insert { .. } | Self::Delete { .. } => &[],
128 };
129 values
130 .iter()
131 .enumerate()
132 .filter_map(|(index, pair)| pair.is_changed().then_some(index))
133 }
134}
135
136impl<T: SchemaWithPK, S: Clone, B: Clone> ChangesetOp<'_, T, S, B> {
137 #[must_use]
146 pub fn primary_key(&self) -> Vec<Value<S, B>> {
147 match *self {
148 Self::Insert { table, values, .. } => table.extract_pk(&values),
149 Self::Delete {
150 table, old_values, ..
151 } => table.extract_pk(&old_values),
152 Self::Update { table, values, .. } => table
153 .primary_key_columns()
154 .map(|col_idx| {
155 let (old, new) = &values[col_idx];
156 old.clone().or_else(|| new.clone()).unwrap_or(Value::Null)
157 })
158 .collect(),
159 }
160 }
161}
162
163#[derive(Debug)]
165pub enum PatchsetOp<'a, T, S, B> {
166 Insert {
168 table: &'a T,
170 values: &'a [Value<S, B>],
172 indirect: bool,
174 },
175 Update {
180 table: &'a T,
182 pk: &'a [Value<S, B>],
184 entries: &'a [PatchsetUpdateEntry<S, B>],
186 indirect: bool,
188 },
189 Delete {
192 table: &'a T,
194 pk: &'a [Value<S, B>],
196 indirect: bool,
198 },
199}
200
201impl<'a, T, S, B> PatchsetOp<'a, T, S, B> {
202 #[must_use]
204 pub fn table(&self) -> &'a T {
205 match self {
206 Self::Insert { table, .. }
207 | Self::Update { table, .. }
208 | Self::Delete { table, .. } => table,
209 }
210 }
211
212 #[must_use]
214 pub fn indirect(&self) -> bool {
215 match self {
216 Self::Insert { indirect, .. }
217 | Self::Update { indirect, .. }
218 | Self::Delete { indirect, .. } => *indirect,
219 }
220 }
221
222 #[must_use]
225 pub fn update_new_values(&self) -> Option<Vec<&'a Option<Value<S, B>>>> {
226 match self {
227 Self::Update { entries, .. } => Some(entries.iter().map(|((), v)| v).collect()),
228 _ => None,
229 }
230 }
231}
232
233impl<T: SchemaWithPK, S: Clone, B: Clone> PatchsetOp<'_, T, S, B> {
234 #[must_use]
240 pub fn primary_key(&self) -> Vec<Value<S, B>> {
241 match *self {
242 Self::Insert { table, values, .. } => table.extract_pk(&values),
243 Self::Update { pk, .. } | Self::Delete { pk, .. } => pk.to_vec(),
244 }
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use crate::{ChangeDelete, ChangeSet, DiffOps, Insert, ParsedDiffSet, PatchSet, SimpleTable};
252 use alloc::string::String;
253 use alloc::vec;
254
255 type Pair = ChangesetUpdatePair<String, Vec<u8>>;
256 type Entry = PatchsetUpdateEntry<String, Vec<u8>>;
257 type Val = Value<String, Vec<u8>>;
258
259 #[test]
260 fn changeset_primary_key_single_key() {
261 let schema = SimpleTable::new("kv", &["id", "val"], &[0]);
262
263 let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
264 let insert = ChangesetOp::Insert {
265 table: &schema,
266 values: &insert_values,
267 indirect: false,
268 };
269 assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
270
271 let update_values: Vec<Pair> = vec![
274 (Some(Value::Integer(2)), None),
275 (
276 Some(Value::Text("before".into())),
277 Some(Value::Text("after".into())),
278 ),
279 ];
280 let update = ChangesetOp::Update {
281 table: &schema,
282 values: &update_values,
283 indirect: false,
284 };
285 assert_eq!(update.primary_key(), vec![Value::Integer(2)]);
286
287 let delete_values: Vec<Val> = vec![Value::Integer(3), Value::Text("gone".into())];
288 let delete = ChangesetOp::Delete {
289 table: &schema,
290 old_values: &delete_values,
291 indirect: false,
292 };
293 assert_eq!(delete.primary_key(), vec![Value::Integer(3)]);
294 }
295
296 #[test]
297 fn changeset_primary_key_composite_reordered_key() {
298 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
300 let expected: Vec<Val> = vec![Value::Integer(20), Value::Integer(10)];
301
302 let insert_values: Vec<Val> = vec![
303 Value::Integer(10),
304 Value::Integer(20),
305 Value::Text("z".into()),
306 ];
307 let insert = ChangesetOp::Insert {
308 table: &schema,
309 values: &insert_values,
310 indirect: false,
311 };
312 assert_eq!(insert.primary_key(), expected);
313
314 let update_values: Vec<Pair> = vec![
317 (Some(Value::Integer(10)), None),
318 (Some(Value::Integer(20)), None),
319 (
320 Some(Value::Text("z".into())),
321 Some(Value::Text("z2".into())),
322 ),
323 ];
324 let update = ChangesetOp::Update {
325 table: &schema,
326 values: &update_values,
327 indirect: false,
328 };
329 assert_eq!(update.primary_key(), expected);
330
331 let delete_values: Vec<Val> = vec![
332 Value::Integer(10),
333 Value::Integer(20),
334 Value::Text("z".into()),
335 ];
336 let delete = ChangesetOp::Delete {
337 table: &schema,
338 old_values: &delete_values,
339 indirect: false,
340 };
341 assert_eq!(delete.primary_key(), expected);
342 }
343
344 #[test]
345 fn patchset_primary_key_variants() {
346 let kv = SimpleTable::new("kv", &["id", "val"], &[0]);
347
348 let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
349 let insert = PatchsetOp::Insert {
350 table: &kv,
351 values: &insert_values,
352 indirect: false,
353 };
354 assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
355
356 let update_pk: Vec<Val> = vec![Value::Integer(9)];
357 let entries: Vec<Entry> = vec![((), None), ((), Some(Value::Text("z".into())))];
358 let update = PatchsetOp::Update {
359 table: &kv,
360 pk: &update_pk,
361 entries: &entries,
362 indirect: false,
363 };
364 assert_eq!(update.primary_key(), vec![Value::Integer(9)]);
365
366 let delete_pk: Vec<Val> = vec![Value::Integer(7)];
367 let delete = PatchsetOp::Delete {
368 table: &kv,
369 pk: &delete_pk,
370 indirect: false,
371 };
372 assert_eq!(delete.primary_key(), vec![Value::Integer(7)]);
373
374 let abc = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
376 let abc_values: Vec<Val> = vec![
377 Value::Integer(10),
378 Value::Integer(20),
379 Value::Text("z".into()),
380 ];
381 let abc_insert = PatchsetOp::Insert {
382 table: &abc,
383 values: &abc_values,
384 indirect: false,
385 };
386 assert_eq!(
387 abc_insert.primary_key(),
388 vec![Value::Integer(20), Value::Integer(10)]
389 );
390 }
391
392 #[test]
393 fn changeset_primary_key_through_iter_composite() {
394 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
397 let cs: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(
398 Insert::from(schema)
399 .set(0, 10i64)
400 .unwrap()
401 .set(1, 20i64)
402 .unwrap()
403 .set(2, "z")
404 .unwrap(),
405 );
406 let ops: Vec<_> = cs.iter().collect();
407 assert_eq!(ops.len(), 1);
408 assert_eq!(
409 ops[0].primary_key(),
410 vec![Value::Integer(20), Value::Integer(10)]
411 );
412 }
413
414 #[test]
415 fn patchset_primary_key_through_iter_composite_delete() {
416 let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
419 let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
420 ps.add_table(&schema);
421 ps.digest_sql("DELETE FROM abc WHERE a = 10 AND b = 20")
422 .unwrap();
423 let ops: Vec<_> = ps.iter().collect();
424 assert_eq!(ops.len(), 1);
425 assert_eq!(
426 ops[0].primary_key(),
427 vec![Value::Integer(20), Value::Integer(10)]
428 );
429 }
430
431 #[test]
432 fn primary_key_on_parsed_changeset_ops() {
433 let schema = SimpleTable::new("kv", &["id", "val"], &[0]);
436 let bytes = ChangeSet::<SimpleTable, String, Vec<u8>>::new()
437 .insert(
438 Insert::from(schema.clone())
439 .set(0, 1i64)
440 .unwrap()
441 .set(1, "a")
442 .unwrap(),
443 )
444 .delete(
445 ChangeDelete::from(schema)
446 .set(0, 2i64)
447 .unwrap()
448 .set(1, "b")
449 .unwrap(),
450 )
451 .build();
452 let ParsedDiffSet::Changeset(set) = ParsedDiffSet::parse(&bytes).unwrap() else {
453 panic!("expected changeset");
454 };
455 let keys: Vec<Vec<Val>> = set.iter().map(|op| op.primary_key()).collect();
456 assert!(keys.contains(&vec![Value::Integer(1)]));
457 assert!(keys.contains(&vec![Value::Integer(2)]));
458 }
459
460 #[test]
461 fn primary_key_on_parsed_patchset_ops() {
462 let schema = SimpleTable::new("kv", &["id", "val"], &[0]);
463 let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
464 ps.add_table(&schema);
465 ps.digest_sql("INSERT INTO kv (id, val) VALUES (1, 'a')")
466 .unwrap();
467 ps.digest_sql("DELETE FROM kv WHERE id = 2").unwrap();
468 let bytes = ps.build();
469 let ParsedDiffSet::Patchset(set) = ParsedDiffSet::parse(&bytes).unwrap() else {
470 panic!("expected patchset");
471 };
472 let keys: Vec<Vec<Val>> = set.iter().map(|op| op.primary_key()).collect();
473 assert!(keys.contains(&vec![Value::Integer(1)]));
474 assert!(keys.contains(&vec![Value::Integer(2)]));
475 }
476}