use alloc::vec::Vec;
use crate::encoding::Value;
use crate::schema::SchemaWithPK;
pub type ChangesetUpdatePair<S, B> = (Option<Value<S, B>>, Option<Value<S, B>>);
pub type PatchsetUpdateEntry<S, B> = ((), Option<Value<S, B>>);
#[derive(Debug)]
pub enum ChangesetOp<'a, T, S, B> {
Insert {
table: &'a T,
values: &'a [Value<S, B>],
indirect: bool,
},
Update {
table: &'a T,
values: &'a [ChangesetUpdatePair<S, B>],
indirect: bool,
},
Delete {
table: &'a T,
old_values: &'a [Value<S, B>],
indirect: bool,
},
}
impl<'a, T, S, B> ChangesetOp<'a, T, S, B> {
#[must_use]
pub fn table(&self) -> &'a T {
match self {
Self::Insert { table, .. }
| Self::Update { table, .. }
| Self::Delete { table, .. } => table,
}
}
#[must_use]
pub fn indirect(&self) -> bool {
match self {
Self::Insert { indirect, .. }
| Self::Update { indirect, .. }
| Self::Delete { indirect, .. } => *indirect,
}
}
}
impl<T: SchemaWithPK, S: Clone, B: Clone> ChangesetOp<'_, T, S, B> {
#[must_use]
pub fn primary_key(&self) -> Vec<Value<S, B>> {
match *self {
Self::Insert { table, values, .. } => table.extract_pk(&values),
Self::Delete {
table, old_values, ..
} => table.extract_pk(&old_values),
Self::Update { table, values, .. } => table
.primary_key_columns()
.into_iter()
.map(|col_idx| {
let (old, new) = &values[col_idx];
old.clone().or_else(|| new.clone()).unwrap_or(Value::Null)
})
.collect(),
}
}
}
#[derive(Debug)]
pub enum PatchsetOp<'a, T, S, B> {
Insert {
table: &'a T,
values: &'a [Value<S, B>],
indirect: bool,
},
Update {
table: &'a T,
pk: &'a [Value<S, B>],
entries: &'a [PatchsetUpdateEntry<S, B>],
indirect: bool,
},
Delete {
table: &'a T,
pk: &'a [Value<S, B>],
indirect: bool,
},
}
impl<'a, T, S, B> PatchsetOp<'a, T, S, B> {
#[must_use]
pub fn table(&self) -> &'a T {
match self {
Self::Insert { table, .. }
| Self::Update { table, .. }
| Self::Delete { table, .. } => table,
}
}
#[must_use]
pub fn indirect(&self) -> bool {
match self {
Self::Insert { indirect, .. }
| Self::Update { indirect, .. }
| Self::Delete { indirect, .. } => *indirect,
}
}
#[must_use]
pub fn update_new_values(&self) -> Option<Vec<&'a Option<Value<S, B>>>> {
match self {
Self::Update { entries, .. } => Some(entries.iter().map(|((), v)| v).collect()),
_ => None,
}
}
}
impl<T: SchemaWithPK, S: Clone, B: Clone> PatchsetOp<'_, T, S, B> {
#[must_use]
pub fn primary_key(&self) -> Vec<Value<S, B>> {
match *self {
Self::Insert { table, values, .. } => table.extract_pk(&values),
Self::Update { pk, .. } | Self::Delete { pk, .. } => pk.to_vec(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ChangeSet, DiffOps, Insert, PatchSet, SimpleTable};
use alloc::string::String;
use alloc::vec;
type Pair = ChangesetUpdatePair<String, Vec<u8>>;
type Entry = PatchsetUpdateEntry<String, Vec<u8>>;
type Val = Value<String, Vec<u8>>;
#[test]
fn changeset_primary_key_single_key() {
let schema = SimpleTable::new("kv", &["id", "val"], &[0]);
let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
let insert = ChangesetOp::Insert {
table: &schema,
values: &insert_values,
indirect: false,
};
assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
let update_values: Vec<Pair> = vec![
(Some(Value::Integer(2)), None),
(
Some(Value::Text("before".into())),
Some(Value::Text("after".into())),
),
];
let update = ChangesetOp::Update {
table: &schema,
values: &update_values,
indirect: false,
};
assert_eq!(update.primary_key(), vec![Value::Integer(2)]);
let delete_values: Vec<Val> = vec![Value::Integer(3), Value::Text("gone".into())];
let delete = ChangesetOp::Delete {
table: &schema,
old_values: &delete_values,
indirect: false,
};
assert_eq!(delete.primary_key(), vec![Value::Integer(3)]);
}
#[test]
fn changeset_primary_key_composite_reordered_key() {
let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
let expected: Vec<Val> = vec![Value::Integer(20), Value::Integer(10)];
let insert_values: Vec<Val> = vec![
Value::Integer(10),
Value::Integer(20),
Value::Text("z".into()),
];
let insert = ChangesetOp::Insert {
table: &schema,
values: &insert_values,
indirect: false,
};
assert_eq!(insert.primary_key(), expected);
let update_values: Vec<Pair> = vec![
(Some(Value::Integer(10)), None),
(Some(Value::Integer(20)), None),
(
Some(Value::Text("z".into())),
Some(Value::Text("z2".into())),
),
];
let update = ChangesetOp::Update {
table: &schema,
values: &update_values,
indirect: false,
};
assert_eq!(update.primary_key(), expected);
let delete_values: Vec<Val> = vec![
Value::Integer(10),
Value::Integer(20),
Value::Text("z".into()),
];
let delete = ChangesetOp::Delete {
table: &schema,
old_values: &delete_values,
indirect: false,
};
assert_eq!(delete.primary_key(), expected);
}
#[test]
fn patchset_primary_key_variants() {
let kv = SimpleTable::new("kv", &["id", "val"], &[0]);
let insert_values: Vec<Val> = vec![Value::Integer(1), Value::Text("a".into())];
let insert = PatchsetOp::Insert {
table: &kv,
values: &insert_values,
indirect: false,
};
assert_eq!(insert.primary_key(), vec![Value::Integer(1)]);
let update_pk: Vec<Val> = vec![Value::Integer(9)];
let entries: Vec<Entry> = vec![((), None), ((), Some(Value::Text("z".into())))];
let update = PatchsetOp::Update {
table: &kv,
pk: &update_pk,
entries: &entries,
indirect: false,
};
assert_eq!(update.primary_key(), vec![Value::Integer(9)]);
let delete_pk: Vec<Val> = vec![Value::Integer(7)];
let delete = PatchsetOp::Delete {
table: &kv,
pk: &delete_pk,
indirect: false,
};
assert_eq!(delete.primary_key(), vec![Value::Integer(7)]);
let abc = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
let abc_values: Vec<Val> = vec![
Value::Integer(10),
Value::Integer(20),
Value::Text("z".into()),
];
let abc_insert = PatchsetOp::Insert {
table: &abc,
values: &abc_values,
indirect: false,
};
assert_eq!(
abc_insert.primary_key(),
vec![Value::Integer(20), Value::Integer(10)]
);
}
#[test]
fn changeset_primary_key_through_iter_composite() {
let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
let cs: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(
Insert::from(schema)
.set(0, 10i64)
.unwrap()
.set(1, 20i64)
.unwrap()
.set(2, "z")
.unwrap(),
);
let ops: Vec<_> = cs.iter().collect();
assert_eq!(ops.len(), 1);
assert_eq!(
ops[0].primary_key(),
vec![Value::Integer(20), Value::Integer(10)]
);
}
#[test]
fn patchset_primary_key_through_iter_composite_delete() {
let schema = SimpleTable::new("abc", &["a", "b", "c"], &[1, 0]);
let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
ps.add_table(&schema);
ps.digest_sql("DELETE FROM abc WHERE a = 10 AND b = 20")
.unwrap();
let ops: Vec<_> = ps.iter().collect();
assert_eq!(ops.len(), 1);
assert_eq!(
ops[0].primary_key(),
vec![Value::Integer(20), Value::Integer(10)]
);
}
}